Run or raise

Some clients you want quick one-key access to and you don't want more than one of them running at a time (Firefox springs to mind). The function run_or_raise starts the client if it is not running, otherwise it jumps you to the running client window. If there are multiple matching client windows, like a few Firefox windows, it will cycle through them. Client windows can be grouped and cycled through by their class, instance or name attributes.

Caveat: I haven't tested this on multiple screens yet, it may fail miserably...

There are two ways to set this up. One way is to put everything in the user's config file ~/.config/awesome/rc.lua. The other is modular and much cleaner.

 01.01.2013: Both approaches are compatible with awesome 3.5 

First, the modular approach
You'll need the two files below for this. The aweror.lua file contains the function module and ror.lua is where the desired key bindings are listed.

aweror.lua can go in either the system wide awesome library directory (/usr/local/share/awesome/lib/ or /usr/share/awesome/lib/) or in the user's config directory (~/.config/awesome/).

ror.lua contains the user's desired keybindings and should go in the user's config directory (~/.config/awesome) unless system wide key bindings are wanted. Edit the key bindings to match your needs (the format is described in the file).

Once the aweror.lua and ror.lua files are in place the user's config file ~/.config/awesome/rc.lua needs to be edited a little. In that file there will be a root.keys(globalkeys) command that sets the user's key bindings. The aweror function needs to be loaded and the 'run or raise' key bindings added to the globalkeys table before that command. On the lines before the root.keys(globalkeys) we need to add these two commands: -- load the 'run or raise' function local ror = require("aweror")

-- generate and add the 'run or raise' key bindings to the globalkeys table globalkeys = awful.util.table.join(globalkeys, ror.genkeys(modkey))

root.keys(globalkeys)

With the rc.lua config file edited and the aweror.lua and ror.lua files in place the new 'run or raise' key bindings should be good to go. Now you can cycle through your Firefox windows, or Emacs windows, or whatnot with a single "Modkey + key" press.

The aweror.lua function file: -- aweror.lua -- Save this file as "aweror.lua" in awesome's system library directory "/usr/local/share/awesome/lib", -- "/usr/share/awesome/lib", or your own config directory "~/.config/awesome/". -- You will also need a "ror.lua" file (contains your key bindings) in your config directory. -- Now you need to generate and add the keybindings in your ~/.config/awesome/rc.lua -- In the rc.lua file there will be a "root.keys(globalkeys)" command that sets -- your keys, you need to add the following lines just before that command: -- -- local ror = require("aweror") -- globalkeys = awful.util.table.join(globalkeys, ror.genkeys(modkey))

-- get our key bindings from separate ror.lua file require("ror")

local awful= require("awful") local client=client local pairs=pairs local table=table local allt1=table5 local print=print local USE_T = true --local USE_T = false

local aweror = {}

function aweror.run_or_raise(cmd, properties) local clients = client.get local focused = awful.client.next(0) local findex = 0 local matched_clients = {} local n = 0 for i, c in pairs(clients) do     --make an array of matched clients if match(properties, c) then n = n + 1 matched_clients[n] = c        if c == focused then findex = n        end end end if n > 0 then local c = matched_clients[1] -- if the focused window matched switch focus to next in list if 0 < findex and findex < n then c = matched_clients[findex+1] end local ctags = c:tags if #ctags == 0 then -- ctags is empty, show client on current tag local curtag = awful.tag.selected awful.client.movetotag(curtag, c)     else -- Otherwise, pop to first tag client is visible on        awful.tag.viewonly(ctags[1]) end -- And then focus the client client.focus = c     c:raise return end awful.util.spawn(cmd) end

-- Returns true if all pairs in table1 are present in table2 function match (table1, table2) for k, v in pairs(table1) do     if table2[k] == nil or (table2[k] ~= v and not table2[k]:find(v)) then return false end end return true end

function genfun(t3) local cmd=t3[1] local rule=t3[2] local flag=t3[3] local table1={} s1="class" if flag then s1=flag end table1[s1]=rule return function aweror.run_or_raise(cmd,table1) end end

function aweror.genkeys(mod1) rorkeys = awful.util.table.join for i,v in pairs(allt1) do   modifier="" if i:len > 1 then modifier=i:sub(1, i:find("-")-1) i=i:sub(-1,-1) end rorkeys = awful.util.table.join(rorkeys,     awful.key({ mod1, modifier}, i, genfun(v))) end return rorkeys end

return aweror

Sample ror.lua key binding file (edit to taste): -- ror.lua -- This is the file goes in your ~/.config/awesome/ directory -- It contains your table of 'run or raise' key bindings for aweror.lua -- Table entry format: ["key"]={"function", "match string", "optional attribute to match"} -- The "key" can include "Control-", "Shift-", and "Mod1-" modifiers (eg "Control-z") -- The "key" will be bound as "modkey + key". (eg from above would end up as modkey+Control+z) -- The "function" is what gets run if no matching client windows are found. -- Usual attributes are "class","instance", or "name". If no attribute is given it defaults to "class". -- The "match string" will match substrings. So "Firefox" will match "blah Firefox blah" -- Use xprop to get this info from a window. WM_CLASS(STRING) gives you "instance", "class". -- WM_NAME(STRING) gives you the name of the selected window (usually something like the web page title -- for browsers, or the file name for emacs).

table5={ ["Control-z"]={"google-chrome --app=http://www.rdio.com","www.rdio.com", "instance"}, ["e"]={"emacsclient -a emacs -n -c","Emacs"}, ["w"]={"firefox","Firefox"}, ["v"]={"firefox -new-window 'http://www.evernote.com/Home.action?login=true#v=l&so=mn'","Evernote", "name"}, ["g"]={"firefox -new-window 'http://mail.google.com/mail/'","Gmail","name"}, ["x"]={"xterm","xterm", "instance"}, ["f"]={"xterm -name mcTerm -e mc -d","mcTerm", "instance"}, ["Shift-s"]={"xterm -name rootTerm -cr red -title rootTerm -e su","rootTerm", "instance"}, ["t"]={"xterm -name htopTerm -e htop","htopTerm","instance"}, ["b"]={"xterm -name rtorrentTerm -e rtorrent","rtorrentTerm","instance"}, ["z"]={"xterm -name mocpTerm -e mocp","mocpTerm", "instance"} }

Another example, all matching with the default class attribute. -- ror.lua table5={ ["f"]={"firefox","Namoroka"}, ["a"]={"chromium --user-data-dir=/dev/shm/goo", "Chromium" }, ["s"]={"xterm -class Screen -e screen -xRRS xterm ", "Screen" }, ["v"]={"gvim", "GVIM"}, ["e"]={"emacs", "Emacs"} }

The all in your rc.lua approach
In this approach everything goes in your user's config file ~/.config/awesome/rc.lua.

Just add the following function to the bottom of your config file and then add the desired key bindings to the globalkeys table (defined in the same file).

The key binding will look like:

awful.key({ modkey, }, "KEY", function run_or_raise("FUNCTION", { ATTRIBUTE = "MATCH STRING" }) end),

KEY = the desired key to be bound

FUNCTION = the function to run if no client windows match

ATTRIBUTE = the window attribute to match. Either class, instance, or name.

MATCH STRING = the substring to match in the attribute

Some examples are provided below.

--- Spawns cmd if no client can be found matching properties -- If such a client can be found, pop to first tag where it is visible, and give it focus -- @param cmd the command to execute -- @param properties a table of properties to match against clients. Possible entries: any properties of the client object function run_or_raise(cmd, properties) local clients = client.get local focused = awful.client.next(0) local findex = 0 local matched_clients = {} local n = 0 for i, c in pairs(clients) do     --make an array of matched clients if match(properties, c) then n = n + 1 matched_clients[n] = c        if c == focused then findex = n        end end end if n > 0 then local c = matched_clients[1] -- if the focused window matched switch focus to next in list if 0 < findex and findex < n then c = matched_clients[findex+1] end local ctags = c:tags if #ctags == 0 then -- ctags is empty, show client on current tag local curtag = awful.tag.selected awful.client.movetotag(curtag, c)     else -- Otherwise, pop to first tag client is visible on        awful.tag.viewonly(ctags[1]) end -- And then focus the client client.focus = c     c:raise return end awful.util.spawn(cmd) end

-- Returns true if all pairs in table1 are present in table2 function match (table1, table2) for k, v in pairs(table1) do     if table2[k] ~= v and not table2[k]:find(v) then return false end end return true end

Usage example:

The following are the key bindings I have for google-chrome that use the substring matching with the NAME attribute. They allow me to cycle through my web browser windows seperately from my gmail, and evernote windows. Hotkey launching, or raising, of evernote and gmail is very sweet.

Add something like the following to the global key bindings table in your rc.lua: awful.key({ modkey, }, "f", function run_or_raise("xterm -name mcTerm -e mc -d", { instance = "mcTerm" }) end), awful.key({ modkey, }, "w", function run_or_raise("google-chrome", { name = "Google Chrome" }) end), awful.key({ modkey, }, "v", function run_or_raise("google-chrome --app='http://www.evernote.com/Home.action?login=true'", { name = "Evernote" }) end), awful.key({ modkey, }, "g", function run_or_raise("google-chrome --app='http://mail.google.com/mail/'", { name = "Gmail" }) end),

Integrating with dmenu
Run or raise with dmenu section on Using dmenu page has a very simple example showing how to use dmenu with run or raise. Though the example doesn't directly use the run or raise setup described here, it can be easily extended.