Awesome WM in Ubuntu 12.04

I’ve been using awesome and other tiling window managers for a while now. I find it more comfortable when programs are using as much screen space as possible, and I like organizing my open windows in “workspaces” instead of having a huge pile of overlapping windows. Being able to extend the window manager’s default behavior with Lua is also rather appealing. So when a colleague recommended awesome a while ago, I started experimenting with it, and it stuck with me.

At first I was using the pre-packaged version of awesome, from the repository of Ubuntu Lucid. Then, as I started learning more about awesome, I installed a snapshot from the ‘master’ git branch of awesome’s development tree. For a while this worked fine, and when awesome started supporting fontconfig, pango and cairo, things were looking quite peachy!

My laptop was running Ubuntu Lucid though. This was not something I could change, for various reasons, security being one of the most important ones. So, when awesome’s git version switched from oocairo and oopango to lgi, various things broke. I could compile new git snapshots on Lucid just fine, but running awesome threw a traceback when it tried to load the very old “lgi” snapshot of Lucid. Compiling lgi and all its dependencies manually turned out to be a major undertaking, involving manual compiling of quite basic and central Gnome bits, like gobject-introspection. Replacing half of my Gnome desktop with manually compiled versions was a really bad idea, because it would require a major time and effort investment to keep up with all the fast-moving bits of Gnome. So I switched to another tiling window manager.

Why Awesome

The ‘container’ model of i3-wm is wonderful, but I was missing an easy way to extend the window manager, e.g. to tweak or extend its core features beyond what is supported by the relatively spartan roadmap of i3. Awesome’s scripting support fills that gap, using Lua for a large part of even the window-manager itself. A small core of awesome is written in C, and there’s a growing library of modules, extensions and plugins. In my not-so-humble opinion, having an extension language instantly makes a program a million times more useful. This is precisely why my favorite editor is GNU Emacs, for example.

So now that I had the opportunity to give awesome another try, I jumped right in. All I had to do was to make sure that lgi would work on my new Ubuntu installation. This post describes everything I tried, and how I eventually made my desktop look like this:

Tiled terminals and nautilus windows. A sample Gnome session with awesome as the window manager.

Sample awesome session, in Ubuntu Precise 12.04

I’m perfectly happy with this sort of setup. The rest of this article describes how I reached this installed and configured awesome, in the hope that other people who upgrade their machines to post-Gnome2 versions may find this useful too.

Installing Dependencies

The first thing I had to do was to make sure that all the dependencies of awesome are installed on my laptop.

Dependencies of Awesome Itself

Since awesome is already available in the package repositories of Ubuntu, the easiest way to do that is to use apt-get itself:

sudo apt-get install build-deps awesome

This will install all the dependencies of the pre-packaged awesome version. The latest development version of awesome uses the same libraries and one more: lgi, the Lua bindings to gnome libraries using gobject-introspection.

LGI: Lua bindings for gobject-introspection

Awesome depends on a fairly recent version of lgi. The one included in Precise repositories is, at the time of this writing, version 0.5-1, which is older than what awesome needs. So I had to compile lgi myself, from one of the newer releases. Release 0.6.2 is the latest version available at lgi’s Github repository, so I downloaded and unpacked this one:

wget -nd -np -c -r -O lgi-0.6.2.tar.gz \
    https://github.com/pavouk/lgi/tarball/0.6.2
tar xzvf lgi-0.6.2.tar.gz
mv pavouk-lgi-a4ad06c lgi-0.6.2

Before building lgi, it’s useful to have all its dependencies installed too. This is, again, rather easy to do with apt-get’s build-dep command:

sudo apt-get build-dep lua-lgi-dev

With the dependencies of lua-lgi out of the way, the next step is to compile and install lgi itself. The source of lgi uses a simple Makefile to compile everything, but you have to add the right directory to the C compiler’s include path, so that including “lua.h” works correctly. I did this by setting CPPFLAGS in the environment of make:

env CPPFLAGS='-I/usr/include/lua5.1' make

Then I installed the newly compiled lgi:

sudo env CPPFLAGS='-I/usr/include/lua5.1' make install

Gobject-introspection Repository

Lgi is not very useful without the necessary gobject-introspection data files. These are available as a package already, so the next things I installed are: libgirepository1.0-1 and libgirepository1.0-dev:

sudo apt-get install libgirepository1.0-1 libgirepository1.0-dev

That concludes the list of awesome and lgi dependencies, so it should be possible to build and run awesome just fine.

Getting the Sources

I normally run the git version of awesome. The master branch in the git repository is actively developed, and it’s always nice to be able to see recent developments in action, before they hit an official ‘stable’ release. I also happen to have git around “just in case”, since it’s now so popular that it threatens to dethrone Coca Cola or something similar from its popularity throne. So I branched awesome’s git repository and checked out the master branch:

cd ~/git
git clone git://git.naquadah.org/awesome.git
cd awesome
git checkout master

That’s all. A fairly recent, up to date, clean checkout of awesome’s development sources is now part of my permanent dumping ground of git clones, at ~gkeramidas/git/….

Compiling Awesome

The first thing you’ll notice when you look at a checkout of awesome’s sources is that it doesn’t use the ‘usual’ autoconf-based configure script to build. It uses CMake instead. If you don’t already have cmake around, now is a good time to install it:

sudo apt-get install cmake

Then you’re ready to build awesome from the sources. One of the interesting features of cmake is that it supports building in a “build directory”, separate from the source directory itself. This is very convenient, especially if you want to keep your git checkout ‘clean’. I often build stuff in “~/tmp/_build“, so I can quickly reclaim space by deleting old object files, without having to crawl my entire home directory. This is where cmake’s build-tree support came handy. To build awesome from its sources I typed:

cd ~/tmp/_build
mkdir awesome-git && cd awesome-git
cmake ~/git/awesome
make

If all goes well this should produce a full build of awesome and its associated files: an installable binary, Lua extensions of the core window-manager binary, manpages, etc. Installing this to the local system is then a piece of cake:

sudo make install

Note: By default this installs awesome in /usr/local. If you prefer to install awesome in /usr/bin instead, you should tell cmake to produce makefiles that use a different installation prefix directory:

cmake -DCMAKE_INSTALL_PREFIX:PATH='/usr' ~/git/awesome

Session Setup for Gnome

After awesome is installed to the system, some manual configuration is needed to launch awesome as part of the usual Gnome session. I created the following files, copying the xmonad instructions from another blog post.

The files I had to adapt are the following:

  • /usr/share/applications/awesome.desktop
  • /usr/share/gnome-session/sessions/awesome.session
  • /usr/share/xsessions/awesome-gnome-session.desktop

The first file, /usr/share/applications/awesome.desktop, registers awesome with the applications a Gnome user can run. It’s a simple text file, with the following contents:

[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=Awesome
Exec=awesome
NoDisplay=true
X-GNOME-WMName=Awesome
X-GNOME-Autostart-Phase=WindowManager
X-GNOME-Provides=windowmanager
X-GNOME-Autostart-Notify=true

If you have installed awesome in /usr/local/bin/awesome and this directory is not in your default PATH for executables, you may have to replace “awesome” in the highlighted line 5 with the full path of the binary: /usr/local/bin/awesome.

The second file, /usr/share/gnome-session/sessions/awesome.session, defines what to start for a ‘Gnome session’ that uses awesome as the window manager. I use this file to launch awesome as the window-manager and a few useful applets: gnome-settings-daemon, gnome-sound-applet, bluetooth-applet and nm-applet.

[GNOME Session]
Name=Awesome Unity-2D Desktop
RequiredComponents=gnome-settings-daemon;gnome-sound-applet;nm-applet;bluetooth-applet;
RequiredProviders=windowmanager;
DefaultProvider-windowmanager=awesome

The third file, /usr/share/xsessions/awesome-gnome-session.desktop, configures the options that gnome-session will use when starting an ‘awesome style’ session. The most important bit is the –session option of line 5:

[Desktop Entry]
Name=Awesome GNOME
Comment=Tiling window manager
TryExec=/usr/bin/gnome-session
Exec=gnome-session --session=awesome
Type=XSession

Sample Awesome Desktops

Everything about awesome’s look and behavior is configured with Lua. This is very similar to what e.g. GNU Emacs does with Emacs Lisp, and it may seem a bit difficult to tweak at first. Fortunately there is a nice default startup file included in the sources of awesome itself. You will find it at awesomerc.lua in the source tree of awesome itself. This cna be immediately copied to ~/.config/awesome/rc.lua and you are ready to go. Incremental changes and small tweaks can then be added on top of the default configuration, to change the behavior of awesome or even to extend it with new features.

The two screenshots below show the default configuration of awesome, and how it looks with my own custom configuration file:

Screenshot of Default Awesome Configuration

Awesome running with its default configuration

Screenshot of Awesome with my custom configuration

Screenshot of Awesome with my custom configuration

You can find my own configuration attached below. Even more configuration examples are available at the wiki of awesome itself: http://awesome.naquadah.org/wiki/User_Configuration_Files

My Own Awesome Configuration

The rest of the post includes my current configuration file for awesome. This has diverged in several ways from the original configuration in the source tree of awesome, but I’m including it here for your reading pleasure.

-- -*- mode: lua; coding: utf-8; fill-column: 78; -*-
--
-- awesome/rc.lua -- startup code for the 'awesome' window manager
--
-- Copyright (C) 2010-2012, Giorgos Keramidas <gkeramidas@gmail.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.

-- {{{ Important globals
-- Important global variables that have to be defined even before
-- loading modules or extensions, because they may affect where the
-- modules are loaded from.

-- Where most of the config files and loadable options live in my
-- current awesome setup.
home = os.getenv('HOME') or '/'
confdir = os.getenv("HOME") .. "/.config/awesome"

terminal = "term"
editor = os.getenv("EDITOR") or "utf-emacs"
filemgr = 'nautilus'
editor_cmd = editor

-- Standard awesome library
local gears = require("gears")
awful = require("awful")
awful.rules = require("awful.rules")
require("awful.autofocus")
-- Widget and layout library
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Notification library
local naughty = require("naughty")
local menubar = require("menubar")

-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
    naughty.notify({ preset = naughty.config.presets.critical,
                     title = "Oops, there were errors during startup!",
                     text = awesome.startup_errors })
end

-- Handle runtime errors after startup
do
    local in_error = false
    awesome.connect_signal(
        "debug::error",
        function (err)
            -- Make sure we don't go into an endless error loop
            if in_error then
                return
            end
            in_error = true
            naughty.notify({ preset = naughty.config.presets.critical,
                             title = "Oops, an error happened!",
                             text = err })
            in_error = false
        end)
end
-- }}}

-- {{{ Variable definitions
-- Themes define colours, icons, and wallpapers
beautiful.init(confdir .. "/themes/keramida/theme.lua")

-- Default modkey.  Note that Mod1 is usually "Left Alt", so binding many
-- keys with this modifier may interfere with some of the shortcuts that
-- other programs want to use.  Use with caution.
modkey = "Mod1"

-- Table of layouts to cover with awful.layout.inc, order matters.
-- NOTE: These are not all the layouts supported by awesome; just the ones I
-- regularly find useful and want to have readily available.
local layouts = {
    awful.layout.suit.floating,
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.max,
}
-- }}}

-- {{{ Wallpaper
if beautiful.wallpaper then
    for s = 1, screen.count() do
        gears.wallpaper.maximized(beautiful.wallpaper, s, true)
    end
end
-- }}}

-- {{{ Tags
-- Define a tag table which hold all screen tags.
tags = {}
for s = 1, screen.count() do
    -- Each screen has its own tag table.
    tags[s] = awful.tag({ 1, 2, 3, 4 }, s, layouts[2])
end
-- }}}

-- {{{ Menu
-- Create a laucher widget and a main menu
myawesomemenu = {
    { "Terminal",    terminal },
    { "Preferences", "gnome-control-center" },
    { "Screenshot",  "gnome-screenshot -i" },
    { "Run",         "gmrun" },
    { "Restart",     awesome.restart },
    { "Lock",        "gnome-screensaver-command --lock" },
    { "Quit",        "pkill gnome-session" }
}
mymainmenu = awful.menu({ items = myawesomemenu })
mylauncher = awful.widget.launcher(
    { image = beautiful.awesome_icon, menu = mymainmenu })

-- Menubar plugin configuration
-- Just set the 'terminal' for those applications that require one.
menubar.utils.terminal = terminal
-- }}}

-- {{{ Wibox
-- Create a textclock widget
mytextclock = awful.widget.textclock(" %H:%M ")

-- Create a wibox for each screen and add it
mywibox = {}
mypromptbox = {}
mylayoutbox = {}
mytaglist = {}
mytaglist.buttons = awful.util.table.join(
    awful.button({        }, 1, awful.tag.viewonly),
    awful.button({ modkey }, 1, awful.client.movetotag),
    awful.button({        }, 3, awful.tag.viewtoggle),
    awful.button({ modkey }, 3, awful.client.toggletag),
    awful.button({        }, 4, function(t) awful.tag.viewprev(t.screen) end),
    awful.button({        }, 5, function(t) awful.tag.viewnext(t.screen) end)
)

mytasklist = {}
mytasklist.buttons = awful.util.table.join(
awful.button({ }, 1,
function (c)
    if c == client.focus then
        c.minimized = true
    else
        -- Without this, the following
        -- :isvisible() makes no sense
        c.minimized = false
        if not c:isvisible() then
            awful.tag.viewonly(c:tags()[1])
        end
        -- This will also un-minimize
        -- the client, if needed
        client.focus = c
        c:raise()
    end
end),
awful.button({ }, 3,
function ()
    if instance then
        instance:hide()
        instance = nil
    else
        instance = awful.menu.clients({ width=250 })
    end
end),
awful.button({ }, 4,
function ()
    awful.client.focus.byidx(1)
    if client.focus then client.focus:raise() end
end),
awful.button({ }, 5,
function ()
    awful.client.focus.byidx(-1)
    if client.focus then client.focus:raise() end
end))

for s = 1, screen.count() do
    -- Create a promptbox for each screen
    mypromptbox[s] = awful.widget.prompt()

    -- Create an imagebox widget which will contains an icon indicating which
    -- layout we're using. We need one layoutbox per screen.
    mylayoutbox[s] = awful.widget.layoutbox(s)
    mylayoutbox[s]:buttons(awful.util.table.join(
        awful.button({ }, 1, function () awful.layout.inc(layouts,  1) end),
        awful.button({ }, 3, function () awful.layout.inc(layouts, -1) end),
        awful.button({ }, 4, function () awful.layout.inc(layouts,  1) end),
        awful.button({ }, 5, function () awful.layout.inc(layouts, -1) end)))
    -- Create a taglist widget
    mytaglist[s] = awful.widget.taglist(
        s, awful.widget.taglist.filter.all, mytaglist.buttons)

    -- Create a tasklist widget
    mytasklist[s] = awful.widget.tasklist(
        s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons)

    -- Create the wibox
    mywibox[s] = awful.wibox({ height = "20", position = "top", screen = s })

    -- Widgets that are aligned to the left
    local left_layout = wibox.layout.fixed.horizontal()
    left_layout:add(mylauncher)
    left_layout:add(mytaglist[s])
    left_layout:add(mypromptbox[s])

    -- Widgets that are aligned to the right
    local right_layout = wibox.layout.fixed.horizontal()
    if s == 1 then
        right_layout:add(wibox.widget.systray())
    end
    right_layout:add(mytextclock)
    right_layout:add(mylayoutbox[s])

    -- Now bring it all together (with the tasklist in the middle)
    local layout = wibox.layout.align.horizontal()
    layout:set_left(left_layout)
    layout:set_middle(mytasklist[s])
    layout:set_right(right_layout)

    mywibox[s]:set_widget(layout)
end
-- }}}

-- {{{ Mouse bindings
root.buttons(awful.util.table.join(
    awful.button({ }, 3, function () mymainmenu:toggle() end),
    awful.button({ }, 4, awful.tag.viewnext),
    awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}

-- {{{ Key bindings
-- {{{
-- Experimental TAB focus cycling code for Awesome.
--
-- Copyright (C) 2011 Giorgos Keramidas <gkeramidas@gmail.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.

-- The tabfocus function cycles through all the clients in the visible
-- screen, keeping the order of the clients unchanged.  Every time it is
-- called it returns the 'next' client in the list of clients of the
-- current screen.
function tabfocus()
    local focus = client.focus  -- the currently focused client
    local screen                -- the current screen

    -- If there's a currently focused client, cycle through the clients
    -- of the same screen.  Otherwise just use the one that the mouse is
    -- currently over.
    if focus then
        screen = focus.screen
    else
        screen = mouse.screen
    end

    -- Build a list of the clients listed in all the 'selected' tags of
    -- the current screen.
    local vc = {}
    local count = 1
    local stags = awful.tag.selectedlist(screen)

    for k, v in ipairs(stags) do
        local tag = v
        local tagclients = tag.clients(tag)
        for ck, cv in ipairs(tagclients) do
            if awful.client.focus.filter(cv) then
                vc[count] = cv
                count = count + 1
            end
        end
    end
    count = nil
    
    -- Cache the first client for later.  If the currently focused
    -- client is the last one, we will pick this one for focus.
    local first = vc[1]

    local found = nil           -- sentinel for focused client match
    local sel =  nil            -- the 'next' selected client for focus
    local first = vc[1]         -- cache first client for later

    for k, c in ipairs(vc) do
        if focus and c == focus then
            -- We just found the currently focused client.  Mark it as
            -- 'found' and let the next loop iteration pick up the next
            -- client in the list as the focus candidate.
            found = c
        end
        if sel == nil and found and c ~= focus then
            -- We don't have a focus candidate in 'sel' yet, but we
            -- found the currently focused client in a previous
            -- iteration and the current 'c' client is not the focused
            -- one.  Pick this one for the focus candidate.
            sel = c
        end
    end
    if sel == nil then
        -- We found the currently focused client as the last item in
        -- vc[], so loop back to the start of the client list and pick
        -- the first vc[] item for our focus candidate.
        sel = first
    end
    client.focus = sel
    if client.focus then
        client.focus:raise()
    end
end
-- }}}

-- A function that spawns 'gnome-screensaver-command' to lock the desktop.
-- Useful for binding to Alt-Ctrl-L to immitate Gnome's lock key behavior.
function lockdesktop ()
    command = "gnome-screensaver-command --lock"
    awful.util.spawn(command)
end

globalkeys = awful.util.table.join(
    awful.key({ modkey, "Control" }, "Left",   awful.tag.viewprev ),
    awful.key({ modkey, "Control" }, "Right",  awful.tag.viewnext ),
    awful.key({ modkey, "Control" }, "Escape", awful.tag.history.restore),

    -- Layout manipulation
    awful.key({ modkey, "Control" }, "j",
              function ()
                  awful.client.swap.byidx(1)
              end),
    awful.key({ modkey, "Control" }, "k",
              function ()
                  awful.client.swap.byidx(-1)
              end),
    awful.key({ modkey }, "Tab",
              function ()
                  tabfocus()
              end),
    awful.key({ modkey }, "space",
              function ()
                  awful.layout.inc(layouts,  1)
              end),

    -- Standard program
    awful.key({ modkey }, "Return",
              function ()
                  awful.util.spawn(terminal)
              end),
    awful.key({ modkey, "Control" }, "q",
              awesome.quit),
    awful.key({ modkey, "Control" }, "l",
              function()
                  lockdesktop()
              end),

    -- Menubar, and prompt.
    awful.key({ modkey, "Control" }, "p",
              function ()
                  menubar.show()
              end),
    awful.key({ modkey, "Control" }, "r",
              function ()
                  awful.util.spawn("gmrun")
              end)
)

clientkeys = awful.util.table.join(
    awful.key({ modkey,           }, "F11",
        function (c)
            -- TODO(gkeramidas): Save the state of the client window, to
            -- some place that allows restoring of all the 'important'
            -- attributes (c.maximized_horizontal, c,maximized_vertical
            -- and c.floating at least... maybe more).

            -- If the client-window is not already expanded to full-screen
            -- mode, maximize it at both directions first and float it before
            -- setting full-screen mode.  This avoids a small problem with
            -- compositing managers when a window is currently tiled.
            if not c.fullscreen then
                c.maximized_horizontal = true
                c.maximized_vertical = true
            end
            -- Now it should be safe to turn full-screen mode on or off.
            c.fullscreen = not c.fullscreen
        end),
    awful.key({ modkey,           }, "F4",
              function (c)
                  c:kill()
              end),
    awful.key({ modkey, "Control" }, "space",
              awful.client.floating.toggle),
    awful.key({ modkey, "Control" }, "Return",
              function (c)
                  c:swap(awful.client.getmaster())
              end),
    awful.key({ modkey,           }, "m",
              function (c)
                  c.minimized = not c.minimized
              end),
    awful.key({ modkey,           }, "F8",
        function (c)
          c.maximized_vertical = not c.maximized_vertical
        end),
    awful.key({ modkey, "Control" }, "m",
        function (c)
            c.maximized_horizontal = not c.maximized_horizontal
            c.maximized_vertical   = not c.maximized_vertical
        end)
)

-- 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

-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it works on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, keynumber do
    globalkeys = awful.util.table.join(globalkeys,
        awful.key({ modkey }, "#" .. i + 9,
                  function ()
                        local screen = mouse.screen
                        if tags[screen][i] then
                            awful.tag.viewonly(tags[screen][i])
                        end
                  end),
        awful.key({ modkey, "Control" }, "#" .. i + 9,
                  function ()
                      if client.focus and tags[client.focus.screen][i] then
                          awful.client.movetotag(tags[client.focus.screen][i])
                      end
                  end))
end

clientbuttons = awful.util.table.join(
    awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
    awful.button({ modkey }, 1, awful.mouse.client.move),
    awful.button({ modkey }, 3, awful.mouse.client.resize)
)

-- Enable the keys defined in the globalkeys table.
root.keys(globalkeys)
-- }}}

-- {{{ Rules
awful.rules.rules = {
    -- All clients will match this rule.
    {
        rule = { },
        properties = {
            border_width = beautiful.border_width,
            border_color = beautiful.border_normal,
            focus = true,
            size_hints_honor = false,
            keys = clientkeys,
            buttons = clientbuttons,
            maximized_vertical = false,
            maximized_horizontal = false
        }
    },

    -- Handle pidgin windows sensibly.  The buddy list is focused and
    -- set as the master window; conversations are slaves by default.
    { 
        rule = { class = "Pidgin", role = "buddy_list" },
        properties = {
        }
    },
    {
        rule = { class = "Pidgin", role = "conversation" },
        properties = {
            callback = awful.client.setslave
        }
    },

    -- Make the 'desktop' window of Nautilus sticky, so that it shows in
    -- all tags.  Other Nautilus windows, like 'file manager' views keep
    -- the normal, default window style.
    {
        rule = { class = "Nautilus", instance = "desktop_window" },
        properties = {
            border_width = 0,
            sticky = true
        }
    },

    -- Disable border for Chrome windows.  I usually run Chrome windows
    -- fully maximized, so a border doesn't appear useful.
    {
        rule = { class = "Google-chrome" },
        properties = {
            border_width = 0
        }
    },

    -- Some clients are floated by default.
    {
        rule = { class = "Gedit" },
        properties = {
            floating = true
        }
    },
}
-- }}}

-- {{{ Signals

--
-- Examples of what one can do in client signal callbacks:
--
--     c.opacity = 0.8        -- set transparency level
--     c.sticky = true        -- set the 'sticky window' property
--

-- Signal function to execute when a new client appears.
client.connect_signal("manage",
    function (c, startup)
        -- Enable sloppy focus
        c:connect_signal("mouse::enter", function(c)
            if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
                and awful.client.focus.filter(c) then
                client.focus = c
            end
        end)

        if not startup then
            -- Put windows in a smart way, only if they does not set an
            -- initial position.
            if ( not c.size_hints.user_position and
                 not c.size_hints.program_position ) then
                awful.placement.no_overlap(c)
                awful.placement.no_offscreen(c)
            end
        end
    end)

-- Signal that is triggered when a client window is focused.
client.connect_signal("focus",
    function(c)
        c.border_color = beautiful.border_focus
    end)

-- Signal that is triggered when a client window is unfocused.
client.connect_signal("unfocus",
    function(c)
        c.border_color = beautiful.border_normal
    end)
-- }}}

3 thoughts on “Awesome WM in Ubuntu 12.04

  1. Kalle Hyvönen

    Hey, thanks for the guide! Any idea why I’m getting a black screen with a dialog saying “Failed to load session awesome” after trying to start the awesome/gnome session from login on Ubuntu 12.10? Awesome is installed and works fine when started by itself.

Comments are closed.