Tag Archives: GNOME

Saving & Restoring Mate Terminal’s Color-Profile Information

Terminal palettes come in infinite variations. Almost everyone has a “favorite” palette, and if you are like me, you probably hate losing all your finely tuned terminal colors, because you wanted to experiment with this new color scheme and forgot to save your original color profile. The script I’m about to describe can save all color-related information for mate-terminal, and can be easily adapted to work at least for gnome-terminal in GNOME 2.X desktops.

The GUI-Based Way of Saving Profile State

One way to save your current color preferences, along with everything else related to the current terminal profile, is to create a full copy of your mate-terminal profile. This can be done through the “Profiles” dialog of mate-terminal. First you navigate to the “Edit ▸ Profiles” menu entry:

Profile menu of mate-terminal

Profile menu of mate-terminal

Then you create a new profile, based on your current one:

Profile dialog of mate-terminal

Profile dialog of mate-terminal

This way you can mess with any of the two profiles, either the original or the new copy, without worrying that you will lose any important settings.

The Scripted Way of Saving Color Settings

The good aspect of having a GUI to save your profile state is that it’s accessible to everyone, and very easy to use. One of the disadvantages is that your settings are all stored in your config database, which also contains a gazillion other options and is not necessarily easy to backup and restore in one step. A simple search for ‘delete your “.config” directory gnome‘ yields many web pages where people recommend deleting the entire ~/.config directory and starting over. This makes me feel rather cautious about depending on always having the full contents of .config around, so I started looking for an alternative way of saving mate-terminal color information: one that I can reliably script myself; one that stores results in a plain text file that I can read easily, fast and with any tool.

So I wrote the shell script shown below. The main idea behind the script is that it should be possible to run a single command and get as output a set of mateconftool-2 commands that will instantly restore my color settings. This way I can save my color profile information by e.g. typing:

mate-terminal-save-colors.sh > terminal-colors.sh

Then if I mess with my color settings, I can just run the resulting script to restore them:

sh terminal-colors.sh

Finding the right mate configuration database keys was not very hard. I originally saw a shell script that tweaks gconf2 database keys when I was experimenting with the solarized color theme for gnome-terminal. There is a nice set of shell scripts and palettes at Github, created by Sigurd Gartmann, that contains a full set of gconf2 keys for gnome-terminal’s color information, as part of its installed scripts. The keys are listed in the set_dark.sh and set_light.sh shell scripts of the gnome-terminal-colors-solarized project.

Adapting the keys and wrapping them in a bit of shell script code, I came up with the following mate-terminal-save-colors.sh script.

Note: The shell script is also available online, as part of my “miscellaneous scripts” collection, at: http://bitbucket.org/keramida/scripts/src/tip/mate-terminal-save-colors.sh.

#!/bin/sh

# Copyright (C) 2013, 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.

# Dump an executable form of all the mate-terminal keys associated with
# colors.  Running the resulting script should restore all color-related
# state of the terminal to whatever it was at the time the script was
# created.
#
# Inspired by:
# The setup scripts of solarized theme for gnome-terminal from:
# https://github.com/sigurdga/gnome-terminal-colors-solarized

# ----- startup code ---------------------------------------------------

# Save the original program invocation name, and the real path of the
# startup directory, for later use.
progdir=$( cd $(dirname "$0") ; /bin/pwd -P )
progname=$( basename "$0" )

# ----- misc functions -------------------------------------------------

#
# err exitval message
#   Display message to stderr and to the logfile, if any, and then
#   exit with exitval as the return code of the script.
#
err()
{
    exitval=$1
    shift

    log "$0: ERROR: $*"
    exit $exitval
}

#
# warn message
#   Display message to stderr and the log file.
#
warn()
{
    log "$0: WARNING: $*"
}

#
# info message
#   Display informational message to stderr and to the logfile.
#
info()
{
    log "$0: INFO: $*"
}

#
# debug message
#   Output message to stderr if debug_output_enabled is set to
#   'yes', 'true' or '1'.  Please AVOID calling any shell subroutine
#   that may recursively call debug().
#
debug()
{
    case ${debug_enabled} in
    [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
        log "$0: DEBUG: $*"
        ;;
    esac
}

#
# log message
#   Print a log message to standard error.  If ${LOGFILE} is set
#   Output message to "${LOGFILE}" if it is set and is writable.
#
log()
{
    __timestamp="`date -u '+%Y-%m-%d %H:%M:%S'`"
    __msg="${__timestamp} [${progname}] -- $*"
    echo >&2 "${__msg}" 2>&1
    if [ -n "${LOGFILE}" ]; then
        echo "${__msg}" >> "${LOGFILE}"
    fi
}

# ----- main script body ------------------------------------------------

# The gconf-compatible tool to use for reading and writing gconf keys
# for the MATE desktop, and the application name under /apps/ to
# configure.  These are provisionaly set to work for the MATE desktop,
# but they can also be tweaked to work for GNOME 2.X by setting:
#
#   conftool='gconftool-2'
#   appname='gnome-terminal'

conftool='mateconftool-2'
appname='mate-terminal'

# Basic command-line sanity checking.
if test $# -ne 0 && test $# -ne 1 ; then
    echo >&2 "usage: ${progname} [ PROFILE ]"
    exit 1
fi

# The name of the profile we are dumping can be passed as a command line
# argument, or auto-detected by peeking at:
# '/apps/${appname}/global/default_profile'
if test $# -eq 1 ; then
    profile="$1"
else
    key="/apps/${appname}/global/default_profile"
    profile=$( ${conftool} --get "${key}" 2>/dev/null )
    if test $? -ne 0 ; then
        debug "Cannot read configuration key: ${key}"
        err 1 "Cannot detect default profile name."
    fi
    unset key
fi

# Verify that the profile we are looking for really exists, by trying to
# read at least one key from it:
# '/apps/${appname}/profiles/${profile}/foreground_color'
key="/apps/${appname}/profiles/${profile}/foreground_color"
${conftool} --get "${key}" > /dev/null 2>&1
if test $? -ne 0 ; then
    debug "Cannot read configuration key: ${key}"
    err 1 "Profile ${profile} cannot be found."
fi
unset key

# dumpkey TYPE KEY
#   Dump a configuration key to standard output, as a shell command that
#   will _set_ it to its current value, using the associated type.
dumpkey()
{
    if test $# -ne 2 || test -z "$1" || test -z "$2" ; then
        debug "dumpkey() requires exactly 2 non-empty arguments,"
        debug "but it was invoked with:"
        debug "    \"$1\""
        debug "    \"$2\""
        return 1
    fi
    __type="$1"
    __key="$2"

    __value=$( ${conftool} --get "${__key}" )
    if test $? -ne 0 ; then
        err 1 "Cannot read key \"${__key}\""
    fi
    echo "${conftool} --set --type \"${__type}\""                       \
        "\"${__key}\" \"${__value}\""
}

dumpkey "string" "/apps/${appname}/profiles/${profile}/background_color"
dumpkey "string" "/apps/${appname}/profiles/${profile}/bold_color"
dumpkey "bool"   "/apps/${appname}/profiles/${profile}/bold_color_same_as_fg"
dumpkey "string" "/apps/${appname}/profiles/${profile}/foreground_color"
dumpkey "string" "/apps/${appname}/profiles/${profile}/palette"

Using The Script

Using the script should be pretty easy to discern by now, but here’s a sample run from my laptop:

$ sh mate-terminal-save-colors.sh 
mateconftool-2 --set --type "string" "/apps/mate-terminal/profiles/Default/background_color" "#000000000000"
mateconftool-2 --set --type "string" "/apps/mate-terminal/profiles/Default/bold_color" "#000000000000"
mateconftool-2 --set --type "bool" "/apps/mate-terminal/profiles/Default/bold_color_same_as_fg" "true"
mateconftool-2 --set --type "string" "/apps/mate-terminal/profiles/Default/foreground_color" "#E8E8E8E8ECEC"
mateconftool-2 --set --type "string" "/apps/mate-terminal/profiles/Default/palette" "#000000000000:#D7D700000000:#5F5F87870000:#CFCFA7A70000:#26268B8BD2D2:#ADAD7F7FA8A8:#2A2AB1B1A8A8:#D3D3D7D7CFCF:#555557575353:#DCDC32322F2F:#9595C9C90000:#F5F5D9D90000:#00008787FFFF:#CDCD9F9FC8C8:#4A4AE1E1D8D8:#EEEEEEEEECEC"

The color values are slurped directly from the MATE desktop’s configuration database entries for mate-terminal. Redirecting this output to a plain text file yields a nice, compact sh(1)-compatible script, which restores the colors of the “Default” mate-terminal profile to the values of the script, i.e. the color setup I was using when this color profile was saved:

# Save the current color settings.
sh mate-terminal-save-colors.sh > ~/term-colors.sh

# Some time later... Restore them once more.
sh ~/term-colors.sh

Mate desktop support for xdg-open (tiny patch)

The Mate desktop really lives up to its promise of a “traditional desktop”, compatible in look and feel with Gnome 2.X versions.

After running it for a few hours now, I’m sold. I never really liked Unity very much, so Mate feels like ‘home’.

There are a few minor things that need tweaking though, mostly because the desktop is no longer called ‘Gnome’. One of them is the default xdg-open script that ships with the xdg-utils package. The current version of the script assumes that the desktop is called one of ‘KDE’, ‘LXDE’, ‘gnome’ or something else. The detectDE() function fails to recognize Mate, because it tries to look for GNOME_DESKTOP_SESSION_ID in its environment, and this is obviously no longer there for Mate.

The following patch fixes that, by checking for the string 'mate' in DESKTOP_SESSION instead, and adding a tiny bit of dispatch code based on DE='mate' further down, to call a new open_mate() function.

--- xdg-open.orig	2013-03-18 12:39:07.955516232 +0100
+++ xdg-open	2013-03-18 12:38:45.955515638 +0100
@@ -308,6 +308,7 @@
     elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
     elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
     elif [ x"$DESKTOP_SESSION" = x"LXDE" ]; then DE=lxde;
+    elif [ x"$DESKTOP_SESSION" = x'mate' ]; then DE=mate;
     else DE=""
     fi
 }
@@ -371,6 +372,21 @@
     fi
 }
 
+open_mate()
+{
+    if gvfs-open --help 2>/dev/null 1>&2; then
+        gvfs-open "$1"
+    else
+        mate-open "$1"
+    fi
+
+    if [ $? -eq 0 ]; then
+        exit_success
+    else
+        exit_failure_operation_failed
+    fi
+}
+
 open_xfce()
 {
     exo-open "$1"
@@ -545,6 +561,10 @@
     open_gnome "$url"
     ;;
 
+    mate)
+    open_mate "$url"
+    ;;
+
     xfce)
     open_xfce "$url"
     ;;

Now this is a tiny problem, but it’s one that improves the usability of Mate for every-day stuff. One of the things that this fixes it the handling of magnet: links in Chrome. When Chrome tries to open magnet: links without this patch, it seems to “do nothing” but open a new tab and stop. With this patch applied, xdg-open works fine for magnet links (depending on your current MIME application settings too, of course), and Chrome happily opens torrent links in Mate desktops without a glitch.

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

Using the Condensed DejaVu Font-Family Variant by Default

The DejaVu font family is a very popular font collection for Linux and BSD systems. The font package of DejaVu includes a condensed variant; a variation of the same basic font theme that sports narrower characters. Continue reading