Category Archives: Open source

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.

Advertisements

Automatically Joining Password-Enabled Channels in ERC

If, like me, you are using ERC to chat with your IRC friends all over the world, then the next few paragraphs describe how you can set up automatically joining channels in a secure manner, even if the channels require a key.

Being able to auto-join channels after connecting to an IRC server is a must have for any serious IRC client. This way the user doesn’t have to laboriously type /join commands again and again, every time the IRC client start, or when there’s a network problem and the IRC server temporarily disconnects. Typing long lists of channel names gets old really fast if one has to type stuff like this for example:

/join #public
/join #secret somekeyword
/join #interest
/join #hobby hobbykey

Most serious IRC clients have some form of ‘autojoin’ feature, that lets you configure the channels they should join when connected to a specific IRC network or to a specific IRC server. ERC supports auto-joining with a loadable module called erc-join. You can load this module in your ERC startup code, and then use erc-autojoin-channels-alist to specify which channels to join on a per-server basis.

Loading the erc-join module and enabling auto-join support is the easy bit:

(require 'erc-join)             ; autojoin support is implemented by erc-join.el
(erc-autojoin-enable)           ; enable channel autojoin support, by default

Setting up the list of channels in erc-autojoin-channel-alist is also easy, but it does lend itself to a bit of upfront explanation. When auto-join code looks at erc-autojoin-channel-alist it expects to find there a list of ‘server patterns’ and their associated channels. So its value should look like this:

((SERVER-PATTERN-1 . CHANNEL-1 CHANNEL-2 ...)
 (SERVER-PATTERN-2 . CHANNEL-3 CHANNEL-4 CHANNEL-5 ...))

When a connection to a new IRC server is established, erc-join has set things up so that erc-autojoin-channels is called. The erc-autojoin-channels function iterates through erc-autojoin-channels-alist and checks if the server pattern matches the current server-name. If it does, it calls erc-server-join-channel for each of the channels attached to this server pattern.

The server pattern can be a regular expression and pattern matching for the ‘server’ part of each erc-autojoin-channels-alist item does not stop when it finds a match. This means that you can mix and match server patterns in all sorts of amusing ways, e.g. to set up a channel that is automatically joined for all IRC networks, by having an erc-autojoin-channels-alist entry whose server pattern is ".*":

(setq erc-autojoin-channels-alist
      '((".*" . "#everywhere")))

Now, for the interesting part. What happens when you want to automatically join channels that require a key. That’s where the real magic behind erc-server-join-channel kicks in, and lets you store your passwords in a secure place, like e.g. a PGP-encrypted ~/.authinfo file in your home directory!

Every time erc-server-join-channel is called it tries to look up a matching entry for the current IRC server and channel names in what Emacs calls “authentication sources”. These are, basically, files in ‘netrc’ file format (traditionally used by FTP clients). If a matching entry is found for the current combination of server and channel in one of the configured netrc files, the password key of this netrc file entry is used as the key for joining the IRC channel. The service name used for netrc entry lookup is also conveniently set to “irc”, so that ERC will not pick up unrelated passwords from your netrc files.

A valid netrc file entry, one which ERC can use to locate the key for an IRC channel, looks like this:

machine irc.freenode.net login "#mychannel" password "supersecret" port irc

If this line is present in your ~/.authinfo or ~/.netrc file, and one of the erc-autojoin-channels-alist entries specifies that “#mychannel” should be automatically joined when connected to the IRC server “irc.freenode.net”, then ERC will use “supersecret” as the key for joining the channel. This is a much safer alternative than typing the channel passwords yourself at an ERC prompt (because the channel passwords may then be logged on disk, if you have enabled server-buffer logging), and it’s definitely safer than listing the password in your ~/.emacs startup code or other place that can be world-readable.

Putting it All Together

To summarize, configuring ERC to automatically join channels “#public” and “#secret” when connecting to IRC server “irc.example.net”, and to use the secret key “supersecret” for the “#secret” channel, you can put the following in your ERC startup code:

(require 'erc-join)
(erc-autojoin-enable)

(setq erc-autojoin-channels-alist
      '(("irc.example.net" . "#public" "#secret"))))

and the following in your ~/.authinfo or ~/.netrc file:

machine irc.example.net login "#secret" password "supersecret" port irc

Mutt-like Scrolling for Gnus

Mutt scrolls the index of email folders up or down, one line at a time, with the press of a single key: ‘<‘ or ‘>’. This is a very convenient way to skim through email folder listings, so I wrote a small bit of Emacs Lisp to do the same in Gnus tonight.

;;;
;; Scrolling like mutt for group, summary, and article buffers.
;;
;; Being able to scroll the current buffer view by one line with a
;; single key, rather than having to guess a random number and recenter
;; with `C-u NUM C-l' is _very_ convenient.  Mutt binds scrolling by one
;; line to '<' and '>', and it's something I often miss when working
;; with Gnus buffers.  Thanks to the practically infinite customizability
;; of Gnus, this doesn't have to be an annoyance anymore.

(defun keramida-mutt-like-scrolling ()
  "Set up '<' and '>' keys to scroll down/up one line, like mutt."
  ;; mutt-like scrolling of summary buffers with '<' and '>' keys.
  (local-set-key (kbd ">") 'scroll-up-line)
  (local-set-key (kbd "<") 'scroll-down-line))

(add-hook 'gnus-group-mode-hook 'keramida-mutt-like-scrolling)
(add-hook 'gnus-summary-mode-hook 'keramida-mutt-like-scrolling)
(add-hook 'gnus-article-prepare-hook 'keramida-mutt-like-scrolling)

This is now the latest addition to my ~/.gnus startup code, and we’re one step closer to making Gnus behave like my favorite old-time mailer.

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

Concatenating Video Files with MPlayer

I found out how to concatenate the parts of a video to a single file with MPlayer. It’s relatively easy, so this is just a mini-post to save it for posterity:

$ cat video_part1.avi video_part2.avi ... > temp.avi
$ mencoder -forceidx -oac copy -ovc copy \
    temp.avi -o final.avi && \
    rm temp.avi

Δυναμικοί Σελιδοδείκτες σε Firefox 3

Το Firefox 3 παρέχει τη δυνατότητα να ορίσουμε δυναμικούς σελιδοδείκτες, για να βρίσκουμε εύκολα τις σελίδες που χρησιμοποιούμε συχνά. Στη γραμμή διεύθυνσης μπορεί να φαίνονται αποτελέσματα από το ιστορικό των σελίδων που έχουμε ήδη επισκεφθεί ή από τους υπάρχοντες σελιδοδείκτες. Με τους δυναμικούς σελιδοδείκτες όμως μπορούμε να ρυθμίσουμε το Firefox έτσι που γράφοντας “t keramida” να φορτώνεται η σελίδα του χρήστη “keramida” στο Twitter ή γράφοντας “w FreeBSD” να φορτώνεται η σελίδα της Wikipedia για το FreeBSD.

Η δυνατότητα αυτή υποστηρίζεται από το πεδίο “Λέξη-κλειδί” του διαλόγου ρυθμίσεων ενός σελιδοδείκτη. Παρακάτω θα δούμε βήμα-βήμα πως μπορεί κάποιος να φτιάξει τη λέξη κλειδί για το Twitter, την οποία ανέφερα παραπάνω, και μετά μερικά από τις άλλες λέξεις κλειδιά που έχω ήδη ρυθμίσει εγώ στο δικό μου Firefox.
Continue reading

sudo hints and ~/.hushlogin in debian or ubuntu

My work laptop runs Ubuntu Linux. After using Ubuntu for more than a month now, I can definitely agree that it is a really nice Linux distribution. Most of the things I have to do as part of my every-day job “just work”. There are binary packages for literally hundreds of applications. The desktop itself feels snappy, fast, it looks good and it’s as configurable as any Gnome-based desktop you will find out there.
Continue reading