Persistent web app windows in awesomewm

21 Aug 2016

Motivation

I use awesome, a tiling window manager to manage the GUI half of my linux environment.

There are some web applications that I use multiple times a day and some that I keep on my screen for monitoring purposes. For a while, it was a tedious task to reboot into my machine, relaunch around 5 separate chromium windows and assign them to a desktop for when I want to look at them. So, I found a way. I will show you how I did it using devdocs.io as an example.

The end result is having a dedicated chrome app window with no toolbar/border that runs if it's not already running:

Getting an instance ID

My first goal was to have some sort of unique identifier for each app. So I dug around the command line options for chromium (chromium-browser in Debian). Turns out you can run any URL with the --app= option and chromium will automatically assign your URL as the window's instance ID (first index) in its WM_CLASS. Or you could save the app as a desktop shortcut and get a more deterministic instance ID. This way, you get an ID similar to what you'd find for chrome apps in chrome://extensions. That is what I did and what follows is based on this.

Save the app as a shorcut

Go to your URL (e.g. devdocs.io) and from the chrome menu pick More Tools > Add to Desktop and check Open as window

This will save a desktop entry on your desktop (if you don't have a desktop folder you have to create it) with content similar to the following:

#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=DevDocs API Documentation
Exec=/usr/bin/chromium --profile-directory=Default --app-id=fckabphocaacjhpehedanblllacppdjb
Icon=chrome-fckabphocaacjhpehedanblllacppdjb-Default
StartupWMClass=crx_fckabphocaacjhpehedanblllacppdjb

Perfect! We have our instance ID in there so let's use it to script awesome.

Note: for convenience, I have a folder called webapps in the desktop folder where I move all these desktop entries. For example, $HOME/Desktop/webapps/devdocs.desktop

Awesome: run web apps if not running

Now that we know what the instance ID is for a web app, we can use wmctrl to find it. wmctrl -l -x will list all your running windows along with their (instance, class) pairs.

Since we will be using outputs from standard commands let's define a function that gives us the output of a command:

function command_output(command)
   local fh = io.popen(command)
   local str = ""
   for i in fh:lines() do
      str = str .. i
   end
   io.close(fh)
   return str
end

Define an idempotent function that we can safely call every time awesome is reloaded:

function run_webapp_once(webapp_file)
  instance = webapp_instance(webapp_file)
  if instance_running(instance) then
    return
  else
    awful.util.spawn(webapp_exec_cmd(webapp_file))
  end
end

Define the helper functions:

function webapp_exec_cmd(webapp_file)
  instance = command_output('sed -n "s/Exec=\\(.*\\)$/\\1/p" ' .. webapp_file)
  return instance
end

function webapp_instance(webapp_file)
  instance = command_output('sed -n "s/StartupWMClass=\\(.*\\)$/\\1/p" ' .. webapp_file)
  return instance
end

function instance_running(instance)
  count = command_output("wmctrl -l -x | grep " .. instance .. " | wc -l")
  return count ~= "0"
end

-- Example function that returns path to a *.desktop entry
function webapp_file(app_name)
  return "~/Desktop/webapps/" .. app_name
end

Note that we use the desktop entry's content to get the instance ID and the command we execute.

After you have defined those functions then having your web applications launched on startup is as easy as creating a desktop shortcut and adding two lines to your rc.lua:

devdocs = webapp_file("devdocs") -- get the path to file
run_webapp_once(devdocs)

Awesome: assign them to a tag

If you are not familiar with awesome rules, then find the table awful.rules.rules in your rc.lua. We will add the following rule to this table:

{ rule = { instance = webapp_instance(devdocs) },
      properties = { tag = tags[3] and tags[3][2] } },

This basically tells awesome to assign the window with the devdocs instance (instance = ...) to desktop 2 on monitor 3 (tag[3][2]).

Reload awesome (default: CtrlMetaR) and your windows should launch successfully.



awesomewmchromiumwebappchromedesktoparchlinuxTweet