So I finally took the plunge and upgraded to awesome3. As is the tradition with awesome releases, the config-file syntax has changed. Awesome3 sees the introduction of a lua config file, which although unpopular with some, I see as being quite exciting – I can fiddle and tweak to my heart’s content.
Here are some of the things I have achieved thus far:
- Tags being coloured yellow on an ‘urgent’ hint being set – e.g. when I get a new IM
- A keybinding to jump between current and last visited tab
- Marking those tags with clients in bold
- Launchboxen
- The obligatory ArchLinux logo, which upon clicking brings up a menu to hibernate or shutdown
- Some magic
Code for the above is below.
Handling Urgent hints
This aims to replicate the functionality of awesome-2.x, whereby programs such as pidgin setting the ‘urgent’ hint would cause the tag(s) to which they were assigned to be displayed in a different colour, so you could see, for example, when someone had messaged you.
Where I initially setup my tabs, I added this definition
urgentTags = {}
then in the label function of my taglist widget, I added the following:
if urgentTags[t.name] then text = "<bg color='#ffff00'/> <span color='"..fg_focus.."'>"..t.name.."</span> " end
and to make it all work, I created a hook_urgent function, and called hooks.urgent() accordingly:
function hook_urgent(c) urgentTag = nil for index,tag in pairs(tags[1]) do if c:istagged(tag) then urgentTag = tag end end if urgentTag ~= nil then if awful.tag.selected(1).name ~= urgentTag.name then urgentTags[urgentTag.name] = true end end end --------- --- ... further down --------- awful.hooks.urgent(hook_urgent)
and finally – to make the urgent hint dissappear when the tag is focused, in hook_arrange:
tagname = awful.tag.selected(1).name if urgentTags[tagname] then urgentTags[tagname] = false end
Jumping between current and last tags
This is useful when, for example, I have an editor on tag one and am reading through an api reference in my webbrowser, and want to switch between the two. Mod4 + Up alternates between them.
Before my tags are defined, I add the following definitions:
customtagnames = {"one", "two", "three", "four", "five", "six", "im", "web", "info"}
clientCount = {}
urgentTags = {}
tags = {}
currentTag = customtagnames[1]
lastTag = customtagnames[1]
my hook_arrange has the following added:
function hook_arrange(screen) local layout = awful.layout.get(screen) mylayoutbox[screen].text = "<bg image=\"/usr/share/awesome/icons/layouts/" .. layout .. "w.png\" resize=\"true\"/>" tagname = awful.tag.selected(1).name if currentTag ~= tagname then lastTag = currentTag currentTag = tagname end end
then the actual keybinding:
function tagBack() --this function switches between current and last tag moveTo = getTagIndexFromName(lastTag) awful.tag.viewonly(tags[1][moveTo]) end keybinding({ "Mod4" }, "Up", tagBack):add()
the function tagBack uses getTagIndexFromName() which is:
function getTagIndexFromName(tagName) for index,tag in pairs(tags[1]) do if tag.name == tagName then return index end end return -1 end
Tags with clients in bold
Before tag definitions, setup the following variable:
-- {{{ Tags
-- Define tags table
customtagnames = {"one", "two", "three", "four", "five", "six", "im", "web", "info"}
clientCount = {}
urgentTags = {}
in taglist.label(t) add
function mytaglist.label(t) ... if clientCount[t.name] and clientCount[t.name] > 0 then text = "<b>" .. text .. "</b>" end ... end
then define the following function:
function hook_client_count () --call every 2 secs newClientCount = {} for index,t in pairs(tags[1]) do for key,c in pairs(client.get()) do if c:istagged(t) then newClientCount[t.name] = 1 end end end clientCount = newClientCount end ---Setup hook awful.hooks.timer(2, hook_client_count)
The clients are recounted every two seconds. Ideally this would happen more frequently, but this was the only way I could find to implement it, and is quite inefficient.
Launchboxen
Icons in my statusbar to launch programs – pretty self explanatory.
[sourcode language=’python’]
launchboxen = {}
function launchboxen:add(image, command)
local index = #self + 1
self[index] = widget({ type=”textbox”, name = “launchbox” .. index, align = “left”})
self[index].text = “”
self[index]:mouse_add(mouse({ }, 1, function () awful.spawn(command) end))
end
launchboxen:add(“/usr/share/pixmaps/firefox.png”, “exec firefox”)
launchboxen:add(“/usr/share/icons/hicolor/32×32/apps/pidgin.png”, “exec pidgin”)
launchboxen:add(“/usr/share/icons/bw2vista/scalable/apps/gnome-terminal.png”, “exec urxvt”)
[/sourcecode]
then in the place where widgets are added to my statusbar:
for s = 1, screen.count() do mystatusbar[s] = statusbar({ position = "top", name = "mystatusbar" .. s, fg = fg_normal, bg = bg_normal, height=30 }) -- Add widgets to the statusbar - order matters ... for launchboxes = 1, #launchboxen do mystatusbar[s]:widget_add(launchboxen[launchboxes]) end ... end
Some magic
This was just something I concocted in school, in order to show my friends how versatile a scriptable window-manager can be (the all use windows). I suppose it could act as a very weak security feature, if I had to leave my laptop turned on for some reason. Pressing mod4 + m makes the mouse move to the screen co-ords (100,100) 100 times per second, mod4 + shift + m turns it off. At some point I plan to turn this ‘magic’ into a function which swirls consoles about in high speed patterns, for no earthly purpose of course.
function doSomeMagic() mouse.coords = { x = 100, y = 100} end keybinding({ "Mod4"}, "m", function() hooks.timer(0.01,doSomeMagic) end):add() keybinding({ "Mod4", "Shift"}, "m", function() hooks.timer(0,doSomeMagic) end):add(
rc.lua
Here is the whole unwieldy beast. Please ignore the messyness, or the littering of debugbox.text = ‘blah’ – this is my first experience with any lua whatsoever, so needed a way to see what was happening. The whole config needs a tidy-up, but that can wait for the time being.
-- awesome 3 configuration file -- Include awesome library, with lots of useful function! require("awful") require("tabulous") -- {{{ Variable definitions terminal = "rxvt" modkey = "Mod4" -- Table of layouts to cover with awful.layout.inc, order matters. layouts = { "tile", "tileleft", "tilebottom", "tiletop", "magnifier", "max", "spiral", "dwindle", "floating" } -- Table of clients that should be set floating floatings = { ["mplayer"] = true, ["pinentry"] = true, ["gimp"] = true, ["phun"] = false } tagrules = { ["midori"] = 8, ["navigator"] = 8, ---this is firefox ["pidgin"] = 7, } -- Color & Appearance definitions, we use these later to display things font = "nimbus 10" fancyfont = "fertigo 10" border_width = 1 bg_normal = "#444444" fg_normal = "#dddddd" border_normal = "#111111" bg_focus = "#a10121" fg_focus = "#dae0d2" border_focus = bg_focus border_marked = "#91231C" awesome.font_set(font) awesome.colors_set({ fg = fg_normal, bg = bg_normal }) -- }}} -- {{{ Tags -- Define tags table customtagnames = {"one", "two", "three", "four", "five", "six", "im", "web", "info"} clientCount = {} urgentTags = {} tags = {} currentTag = customtagnames[1] lastTag = customtagnames[1] for s = 1, screen.count() do tags[s] = {} for tagnumber = 1, 9 do tags[s][tagnumber] = tag({ name = customtagnames[tagnumber], layout = layouts[1] }) tags[s][tagnumber].mwfact = 0.618033988769 tags[s][tagnumber]:add(s) end tags[s][1].selected = true end function getTagIndexFromName(tagName) for index,tag in pairs(tags[1]) do if tag.name == tagName then return index end end return -1 end -- }}} -- {{{ Statusbar debugbox = widget({ type = "textbox", name = "debugbox", align = "left" }) debugbox.text = 'debug' reportingtagnames = false -- Create a taglist widget mytaglist = widget({ type = "taglist", name = "mytaglist" }) mytaglist:mouse_add(mouse({}, 1, function (object, tag) awful.tag.viewonly(tag) end)) mytaglist:mouse_add(mouse({ modkey }, 1, function (object, tag) awful.client.movetotag(tag) end)) mytaglist:mouse_add(mouse({}, 3, function (object, tag) tag.selected = not tag.selected end)) mytaglist:mouse_add(mouse({ modkey }, 3, function (object, tag) awful.client.toggletag(tag) end)) mytaglist:mouse_add(mouse({ }, 4, awful.tag.viewnext)) mytaglist:mouse_add(mouse({ }, 5, awful.tag.viewprev)) function mytaglist.label(t) local text = "" if t.selected then text = "<bg color='"..bg_focus.."'/> <span color='"..fg_focus.."'>"..t.name.."</span> " else text = " "..t.name.." " end if clientCount[t.name] and clientCount[t.name] > 0 then text = "<b>" .. text .. "</b>" end if urgentTags[t.name] then text = "<bg color='#ffff00'/> <span color='"..fg_focus.."'>"..t.name.."</span> " end return text end -- Create a tasklist widget mytasklist = widget({ type = "tasklist", name = "mytasklist", align="right" }) mytasklist:mouse_add(mouse({ }, 1, function (object, c) c:focus_set(); c:raise() end)) mytasklist:mouse_add(mouse({ }, 4, function () awful.client.focus(1) end)) mytasklist:mouse_add(mouse({ }, 5, function () awful.client.focus(-1) end)) function mytasklist.label(c) local text = "" if c.floating then text = "<bg image=\"/usr/share/awesome/icons/floatingw.png\" align=\"right\"/>" end if client.focus_get() == c then text = text .. " <bg color='"..bg_focus.."'/><span color='"..awful.escape(fg_focus).."'>"..c.name.."</span> " else text = text .. " "..awful.escape(c.name).." " end return ' ' end mypromptbox = widget({ type = "textbox", name = "mypromptbox", align = "left" }) clockicon = widget({ type = "textbox", name = "clockicon", align = "right" }) clockicon.text = "<bg image = \"/usr/share/icons/bw2vista/scalable/emblems/emblem-urgent.png\" resize=\"false\"/>" clockbox = widget({ type = "textbox", name = "clockbox", align = "right" }) clockbox.text = "<b>The</b> Time" archbox = widget({ type = "textbox", name = "archbox", align = "left", height=20 }) archbox.text = "<bg image=\"/home/ben/Pictures/awesome-arch-red.png\" resize=\"true\"/>" archbox:mouse_add(mouse({ }, 1, function () awful.spawn("exec /home/ben/builds/PyShutdown/PowerDown.py") end )) launchboxen = {} function launchboxen:add(image, command) local index = #self + 1 self[index] = widget({ type="textbox", name = "launchbox" .. index, align = "left"}) self[index].text = "<bg image=\"" .. image .. "\" resize=\"true\"/>" self[index]:mouse_add(mouse({ }, 1, function () awful.spawn(command) end)) end launchboxen:add("/usr/share/pixmaps/firefox.png", "exec firefox") launchboxen:add("/usr/share/icons/hicolor/32x32/apps/pidgin.png", "exec pidgin") launchboxen:add("/usr/share/icons/bw2vista/scalable/apps/gnome-terminal.png", "exec urxvt") -- Create a systray mysystray = widget({ type = "systray", name = "mysystray", align = "right" }) mylayoutbox = {} for s = 1, screen.count() do mylayoutbox[s] = widget({ type = "textbox", name = "mylayoutbox", align = "left" }) mylayoutbox[s]:mouse_add(mouse({ }, 1, function () awful.layout.inc(layouts, 1) end)) mylayoutbox[s]:mouse_add(mouse({ }, 3, function () awful.layout.inc(layouts, -1) end)) mylayoutbox[s]:mouse_add(mouse({ }, 4, function () awful.layout.inc(layouts, 1) end)) mylayoutbox[s]:mouse_add(mouse({ }, 5, function () awful.layout.inc(layouts, -1) end)) mylayoutbox[s].text = "<bg image=\"/usr/share/awesome/icons/layouts/tilew.png\" resize=\"true\"/>" end -- Create a statusbar for each screen and add it mystatusbar = {} for s = 1, screen.count() do mystatusbar[s] = statusbar({ position = "top", name = "mystatusbar" .. s, fg = fg_normal, bg = bg_normal, height=30 }) -- Add widgets to the statusbar - order matters mystatusbar[s]:widget_add(archbox) mystatusbar[s]:widget_add(mytaglist) mystatusbar[s]:widget_add(mylayoutbox[s]) for launchboxes = 1, #launchboxen do mystatusbar[s]:widget_add(launchboxen[launchboxes]) end mystatusbar[s]:widget_add(mypromptbox) mystatusbar[s]:widget_add(debugbox) mystatusbar[s]:widget_add(clockicon) mystatusbar[s]:widget_add(clockbox) mystatusbar[s]:add(s) end bottombar = statusbar({ position = "bottom", name = "bottombar", fg = fg_normal, bg = bg_normal, height=5 }) bottombar:widget_add(mysystray) bottombar:add(1) -- }}} -- {{{ Mouse bindings awesome.mouse_add(mouse({ }, 3, function () awful.spawn(terminal) end)) awesome.mouse_add(mouse({ }, 4, awful.tag.viewnext)) awesome.mouse_add(mouse({ }, 5, awful.tag.viewprev)) -- }}} -- {{{ Key bindings -- Bind keyboard digits -- Compute the maximum number of digit we need, limited to 9 keynumber = 0 for s = 1, screen.count() do keynumber = math.min(9, math.max(#tags[s], keynumber)); end for i = 1, keynumber do keybinding({ modkey }, i, function () local screen = mouse.screen if tags[screen][i] then awful.tag.viewonly(tags[screen][i]) end end):add() keybinding({ modkey, "Control" }, i, function () local screen = mouse.screen if tags[screen][i] then tags[screen][i].selected = not tags[screen][i].selected end end):add() keybinding({ modkey, "Shift" }, i, function () local screen = mouse.screen if tags[screen][i] then awful.client.movetotag(tags[screen][i]) end end):add() keybinding({ modkey, "Control", "Shift" }, i, function () local screen = mouse.screen if tags[screen][i] then awful.client.toggletag(tags[screen][i]) end end):add() end keybinding({ modkey }, "Left", awful.tag.viewprev):add() keybinding({ modkey }, "Right", awful.tag.viewnext):add() ---go back and forth between tags -- Standard program keybinding({ modkey }, "Return", function () awful.spawn(terminal) end):add() keybinding({ modkey, "Control" }, "r", awesome.restart):add() keybinding({ modkey, "Shift" }, "q", awesome.quit):add() -- Client manipulation keybinding({ modkey, "Shift" }, "c", function () client.focus_get():kill() end):add() keybinding({ modkey }, "j", function () awful.client.focus(1); client.focus_get():raise() end):add() keybinding({ modkey }, "k", function () awful.client.focus(-1); client.focus_get():raise() end):add() keybinding({ modkey, "Shift" }, "j", function () awful.client.swap(1) end):add() keybinding({ modkey, "Shift" }, "k", function () awful.client.swap(-1) end):add() keybinding({ modkey, "Control" }, "j", function () awful.screen.focus(1) end):add() keybinding({ modkey, "Control" }, "k", function () awful.screen.focus(-1) end):add() keybinding({ modkey, "Control" }, "space", awful.client.togglefloating):add() keybinding({ modkey, "Control" }, "Return", function () client.focus_get():swap(awful.client.master()) end):add() keybinding({ modkey }, "o", awful.client.movetoscreen):add() -- Layout manipulation keybinding({ modkey }, "l", function () awful.tag.incmwfact(0.05) end):add() keybinding({ modkey }, "h", function () awful.tag.incmwfact(-0.05) end):add() keybinding({ modkey, "Shift" }, "h", function () awful.tag.incnmaster(1) end):add() keybinding({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1) end):add() keybinding({ modkey, "Control" }, "h", function () awful.tag.incncol(1) end):add() keybinding({ modkey, "Control" }, "l", function () awful.tag.incncol(-1) end):add() keybinding({ modkey }, "space", function () awful.layout.inc(layouts, 1) end):add() keybinding({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -1) end):add() -- Prompt keybinding({ modkey }, "F1", function () awful.prompt({ prompt = "Run: ", cursor_fg = fg_focus, cursor_bg = bg_focus }, mypromptbox, awful.spawn, awful.completion.bash) end):add() keybinding({ modkey }, "F4", function () awful.prompt({ prompt = "Run Lua code: ", cursor_fg = fg_focus, cursor_bg = bg_focus }, mypromptbox, awful.eval, awful.prompt.bash) end):add() ---MY KEYBINDINGS keybinding({ "Mod1" }, "F4", function () client.focus_get():kill() end):add() keybinding({ "Mod1" }, "grave", function () awful.spawn("exec ~/Scripts/dmenu.sh") end):add() keybinding({ "Control" }, "grave", function () awful.spawn(terminal) end):add() keybinding({ "Mod4" }, "p", function () awful.spawn("exec mpc toggle") end):add() keybinding({ "Mod4" }, "n", function () awful.spawn("exec mpc next") end):add() keybinding({ "Mod4" }, "s", function () awful.spawn("exec mpc stop") end):add() function tagBack() --this function switches between current and last tag moveTo = getTagIndexFromName(lastTag) ---debugbox.text = "TAGGING BACK from " .. currentTag .. " to " .. lastTag .. " which is " .. moveTo awful.tag.viewonly(tags[1][moveTo]) end keybinding({ "Mod4" }, "Up", tagBack):add() function doSomeMagic() mouse.coords = { x = 100, y = 100} end keybinding({ "Mod4"}, "m", function() hooks.timer(0.01,doSomeMagic) end):add() keybinding({ "Mod4", "Shift"}, "m", function() hooks.timer(0,doSomeMagic) end):add() -- }}} -- {{{ Hooks -- Hook function to execute when focusing a client. function hook_focus(c) if not awful.client.ismarked(c) then c.border_color = border_focus end c.opacity = 1.0 end -- Hook function to execute when unfocusing a client. function hook_unfocus(c) if not awful.client.ismarked(c) then c.border_color = border_normal end c.opacity = 0.6 end -- Hook function to execute when marking a client function hook_marked(c) c.border_color = border_marked end -- Hook function to execute when unmarking a client function hook_unmarked(c) c.border_color = border_focus end -- Hook function to execute when the mouse is over a client. function hook_mouseover(c) -- Sloppy focus, but disabled for magnifier layout if awful.layout.get(c.screen) ~= "magnifier" then c:focus_set() end end -- Hook function to execute when a new client appears. function hook_manage(c) -- Add mouse bindings c:mouse_add(mouse({ }, 1, function (c) c:focus_set(); c:raise() end)) c:mouse_add(mouse({ modkey }, 1, function (c) c:mouse_move() end)) c:mouse_add(mouse({ modkey }, 3, function (c) c:mouse_resize() end)) -- New client may not receive focus -- if they're not focusable, so set border anyway. c.border_width = border_width c.border_color = border_normal c:focus_set() if floatings[c.name:lower()] then c.floating = true end ---TODO: improve this bit for floating for k,v in pairs(tagrules) do if c.class:lower():find(k) or c.name:lower():find(k) then local screen = mouse.screen if tags[screen][v] then awful.client.movetotag(tags[screen][v]) end end end -- Honor size hints c.honorsizehints = true end -- Hook function to execute when arranging the screen -- (tag switch, new client, etc) lastfocus = nil function hook_arrange(screen) local layout = awful.layout.get(screen) mylayoutbox[screen].text = "<bg image=\"/usr/share/awesome/icons/layouts/" .. layout .. "w.png\" resize=\"true\"/>" tagname = awful.tag.selected(1).name if currentTag ~= tagname then lastTag = currentTag currentTag = tagname if urgentTags[tagname] then urgentTags[tagname] = false end ---debugbox.text = 'current - "' .. currentTag .. '" last - "' .. lastTag .. '"' ---.. #tabulous.clients_get(3) debugbox.text = "opacity omg" c = client.focus_get() if c then c.opacity = 1.0 c:focus_set() c:raise() c:redraw() end end -- Uncomment if you want mouse warping --[[ local sel = client.focus_get() if sel then local c_c = sel.coords local m_c = mouse.coords if m_c.x < c_c.x or m_c.x >= c_c.x + c_c.width or m_c.y < c_c.y or m_c.y >= c_c.y + c_c.height then if table.maxn(m_c.buttons) == 0 then mouse.coords = { x = c_c.x + 5, y = c_c.y + 5} end end end ]] end --hook function function hook_urgent(c) ---debugbox.text = "URGENT HOOK RECEIVED" ---urgentTags[tags[1][5].name] = true urgentTag = nil ---debugbox.text = "URGENT TAG SETUP" for index,tag in pairs(tags[1]) do if c:istagged(tag) then urgentTag = tag end end if urgentTag ~= nil then ---debugbox.text = "URGENT TAG true" if awful.tag.selected(1).name ~= urgentTag.name then urgentTags[urgentTag.name] = true end ---debugbox.text = "URGENT TAG DONE and is treu" else ---debugbox.text = "URGENT TAG DONE and is false" end end -- Hook called every 10 secs function hook_timer () clockbox.text = "<b> " .. os.date("%a, %b %d %I:%M%p") .. "</b> " reportingtagnames = true end function hook_client_count () --call every 2 secs ---debugbox.text = "COUNTING CLIENTS" newClientCount = {} for index,t in pairs(tags[1]) do for key,c in pairs(client.get()) do if c:istagged(t) then newClientCount[t.name] = 1 end end end clientCount = newClientCount ---debugbox.text = "COUNTING DONE" end -- Set up some hooks awful.hooks.focus(hook_focus) awful.hooks.unfocus(hook_unfocus) awful.hooks.marked(hook_marked) awful.hooks.unmarked(hook_unmarked) awful.hooks.manage(hook_manage) awful.hooks.mouseover(hook_mouseover) awful.hooks.arrange(hook_arrange) awful.hooks.urgent(hook_urgent) awful.hooks.timer(10, hook_timer) awful.hooks.timer(2, hook_client_count) -- }}}
Postscripts:
- Sorry for the poor syntax highlighting, wordpress.com doesn’t support lua, so I chose python, which is probably the most-similar supported language.
- If anyone can see an easier way to do what I’ve done, please let me know. I found myself at many points wondering whether my way was a bit convoluted.
The doSomeMagic function should read
mouse.coords({ x = 100, y = 100}) rather than
mouse.coords = { x = 100, y = 100} since mouse.coords is a function and not a variable.
Thanks for pointing that out,
This config was written back when mouse.coords was a variable. Oh how quickly awesome’s config changes.