Awesome3 Configuration – rc.lua

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.

Awesome3 Configuration – rc.lua

2 thoughts on “Awesome3 Configuration – rc.lua

  1. heurist says:

    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.

Leave a comment