From Ubuntu / Gnome Classic to i3

I noticed recently that when I am working on two screens, the only window manipulation I perform is to move a window from one screen to another. Every once in a while I would tile two windows on one screen, but I’d never have one window overlapping another. I’d chosen Gnome Classic sometime back because it was snappy and did a decent job of getting out of the way (and alt-tab wasn’t completely broken like in Unity), but it seemed like a tiling window manager would work better for me. So I went googling, narrowed the field of contenders down to xmonad, qtile and i3, tried xmonad, failed to make it stick, and finally ended up with i3.

And I am pretty happy with it. The other day I noticed sometime in the afternoon that the monitor on my left was attached to my main screen on the right side. Apparently, I’d spent most of the day without having occasion to move my mouse from one screen to the other. i3 makes it easy to move windows around workspaces and it’s possible to more efficiently get to the window you want compared to alt-tabbing. It also feels even faster than Gnome Classic.

On the other hand, i3 is just a window manager, and it is missing some of the functionality that the gnome shell provides. Here’s a quick run of how to make it work.

no display configuration UI

I learned to use xrandr some time ago because I was sick of the gnome display configuration UI, but if I hadn’t already known it, I definitely would have had to learn it when I started using i3, as there is no UI to change resolutions or position screens in relation to each other. The xrandr commands I use most often are:

xrandr

This will list all outputs (i.e. monitors) and available modes for them.

xrandr --output VGA1 --mode 1680x1050

Set an output (VGA1) to a resolution (1680×1050, needs to be listed as an available mode for that output).

xrandr --output VGA1 --auto --left-of eDP1

Position one output (VGA1) to the left of another one (eDP1), and enable it with its preferred resolution (–auto flag). I execute this after I connect my monitor to my laptop.

xrandr --output VGA1 --auto --same-as eDP1

Same as above, but this makes one output display the same content as another. I use this after I connect to a projector.

no network manager, power manager and hardware keys

Sounds tough but turned out to be pretty simple, since the gnome tools play pretty well with i3.

exec --no-startup-id nm-applet
exec --no-startup-id gnome-settings-daemon

Addings this to the top of .i3/config gives a network manager icon in the status bar (including wireless selector), as well as menu for switching keyboard layouts, and all the hardware keys (mute, volume) and automatic suspend on closing the lid work again.

exec --no-startup-id dropbox start

For extra goodness, this will start the dropbox daemon and add a dropbox icon to the status bar.

No keyring

The only thing I used the gnome keyring for was to save myself from entering ssh key passphrases. Turns out that the ssh-add command does pretty much the same thing

exec --no-startup-id ssh-add

Adding this to the top of .i3/config will prompt for the passphrase of the default ssh key when logging in. It’s also possible to open other keys by giving a path.

exec --no-startup-id ssh-add /path/to/private/key

Or you can just execute the same command manually from the command line.

no program launch bar

i3 comes with dmenu for launching programs, but since I only ever launch a few programs anyway, I figured I’d go for a simple launch script.

#!/bin/bash
exec "$@" &> /dev/null &

This will launch a program, orphan it, and redirect its output to /dev/null. In order to get completion to work with this script,

complete -F _command launch

I added this to my .bash_profile (assumes that the script is called ‘launch’).

no UI for shutdown, suspend, lock

I found this script that will build an UI using python and GTK. I had to modify it slightly, removing the call to i3-lock (which should be i3lock these days anyhow) when suspending, as I had already setup gnome to do the same thing back in the olden days. You can find the modified script here.

no system proxy

I am behind a proxy at work. Switching to i3 didn’t cause any trouble for most programs, since they were using the http_proxy et al. environment variables or had their separate proxy configuration (*grumble, grumble*). Chrome, however, will try to use the system proxy and cry piteously if it’s not available. Fortunately, it’s possible to set a proxy using command line options (–proxy-server, –proxy-auto-detect, or –proxy-pac-url), so I just created a bash alias to launch it with the correct proxy settings.

misc .i3/config settings

One thing I didn’t like about the default configuration was that when you move a window to a workspace that’s on another screen, the focus wouldn’t move with it. I modified the keybindings to make it work the way I wanted:

bindsym $mod+Shift+1 move container to workspace 1; workspace 1
bindsym $mod+Shift+2 move container to workspace 2; workspace 2
bindsym $mod+Shift+3 move container to workspace 3; workspace 3
...

I also found the workspace back_and_forth to be pretty useful:

bindsym Mod1+Tab workspace back_and_forth

This will toggle between whatever two workspaces you were on last. I use it to switch between monitors quickly. On a related note, I find that usually whatever I have on one screen is related to what I have on the other, and when I want to switch workspaces I want to do it on both screens, so I added some shortcuts to do that quickly (assuming all the even numbered workspaces are on one screen and the odd ones are on another).

bindsym $mod+Control+1 workspace 1; workspace 2
bindsym $mod+Control+2 workspace 3; workspace 4
bindsym $mod+Control+3 workspace 5; workspace 6
bindsym $mod+Control+4 workspace 7; workspace 8
bindsym $mod+Control+5 workspace 9: workspace 10

This will also immediately make back_and_forth work when switching to another set of workspaces.

And I changed all the movement bindings to use the vim movement keys (hjkl, instead of jkl;). You can find the my config here, if you want to save yourself some typing (or vim macroing).

extra credit: fixing dbus issues in tmux

After switching to i3, I would get dbus related warning messages when launching some programs (like gvim) from tmux and gvim would also show me an annoying error box on startup (something about configuration). I didn’t look too deeply into what was going on and why, but I found out that adding

unset DBUS_SESSION_BUS_ADDRESS

to my .bashrc fixes the problem.

conclusion

So now I have this bastardized setup with i3 and gnome, which definitely won’t win any prizes for elegance or purity, but I am pretty happy with it so far. No more tabbing through a dozen applications just to tab past the one I wanted and then having to bend my pinky in unnatural ways to shift-alt-tab back. No more laboriously reaching for the mouse and dragging a window over to another screen.

Having said that, I will probably give Arch a spin in the not too distant future.

random addendum

i3 works fine with keepass autotype if anyone’s wondering.

Posted in tools | Tagged , , , , | Leave a comment

Automatically setting local git config options

I like to use my personal .gitconfig at work, too, so that I have the same aliases and settings. Unfortunately, my personal .gitconfig also has my personal email address in it, which I don’t want to use to commit at work. The obvious solution to this is to use the local .git/config to overwrite it with my work email address, but this is pretty error prone. If I clone a lot of repos, I’ll probably forget it every once in a while. To solve this, I wrote a git alias that will automatically set some local git options when I clone from a certain URL:

    cl = "!f() { git clone $* ; case $* in *your.host.com*) git config user.name \"Your Name\" && git config user.email \"your.email@company.com\" ;; esac }; f"

Or more readably:

!f() { 
    git clone $*;
    case 
        $* in *your.host.com*) git config user.name \"Your Name\" && git config user.email \"your.email@company.com\" ;;
    esac 
}; f

It’s a shell case statement, checking if the arguments given to the clone command (the $*) contain *your.host.com* (see here). Another clause in the case statement could then handle a second host if that ever becomes necessary.

But then, I wouldn’t want to put my work repository host address and my work email into my personal .gitconfig (which is publicly available on github), so I use the git [include] option to include another config file:

[include]
    path = "/home/username/.local_gitconfig"

I put the alias in this .local_gitconfig file, use cl instead of clone and problem solved.

Posted in tools | Tagged , | Leave a comment

python multiprocessing with pipes

One thing the multiprocessing documentation will not tell you about pipes: subprocesses will inherit all pipe file descriptors of the parent process. That means in order to really close a pipe, you will have to close it in every subprocess.

Here’s a simple case. Have one process pipe out numbers from 0 to 9, another process take the numbers and square them and another process take those numbers and double them.

def test_pipes():
    pipe1 = mp.Pipe(True)
    p1 = mp.Process(target=prod, args=(pipe1,))
    p1.start()

    pipe2 = mp.Pipe(True)
    p2 = mp.Process(target=square, args=(pipe1, pipe2,))
    p2.start()

    pipe3 = mp.Pipe(True)
    p3 = mp.Process(target=double, args=([pipe1], pipe2, pipe3,))
    p3.start()

    pipe1[0].close()
    pipe2[0].close()
    pipe3[0].close()

    try:
        while True:
            print pipe3[1].recv()
    except EOFError:
        print "Finished"
  • Better not declare all pipes to begin with, so you don’t have to close them in every subprocess.
  • Remember to close them all in the main thread.
  • Pass all the pipes to all the processes that will need to close them.
def double(unused_pipes, in_pipe, out_pipe):
    for pipe in unused_pipe:
        close, _ = pipe
        close.close()

    closep, in_conn = in_pipe
    closep.close()

    out_conn, _ = out_pipe
    try:
        while True:
            x = in_conn.recv()
            out_conn.send(x * 2)
    except EOFError:
        out_conn.close()

There are more high-level multiprocessing classes (queues, pools, etc.) and there are also ways to make pipes work, but if you’re thinking “I have a simple problem, I’ll just use the simplest thing there is” think again.

Full example code here.

Posted in code | Tagged , , | Leave a comment

counting words in python

I went to a functional program meetup group event in Berlin (link) where solutions to the same task were presented in some functional and not-so-functional languages. The task was as follows: read a text file, count up the number of times each word appears in it, and output the 10 most common words. I went and presented a solution in python, which probably didn’t impress any of the xmonad toting haskell programmers, but, for what it’s worth, it looked very similar to solutions in other more functional languages with dynamic typing such as clojure.

You can find the slides of the talk here. Or read on for the bloggified version.

First, split up and lower case the words:

word_list = [x for x in re.split("\W", open("moby-dic.txt").read().lower()) if x != '']

Then we might be all boring and idiomatic and use the standard library Counter object:

def word_count_1(wl, most_common=10):
    return collections.Counter(wl).most_common(most_common)

Or try to be more functional:

def word_count_2(wl, most_common=10):
    def _increment_key(d, word):
        d[word] += 1 
        return d

    return sorted(reduce(_increment_key, wl, 
                         collections.defaultdict(int)).iteritems(),
                  key=lambda x: x[1], reverse=True)[:most_common]

Now if we modify the task from “find the 10 most common words” to “find the most common words with high probability”, we could get around counting all the words. Instead we sample some subsection of the word list for its most common words and then just count up those words in the complete file.

def word_count_3(word_list, step=10, num_seed=20):
    most_common_set = set([word for word, num in word_count_2(word_list[::step], num_seed)])

    return word_count_2([word for word in word_list if word in most_common_set])

This does give us the correct result for the file we have, and takes half the time.

Finally, if we modify the task further, we could just give a decent guess at the most common words by counting up every Nth word and multiplying the result by N.

def word_count_4(word_list, step=10):
    most_common = word_count_2(word_list[::step])

    return [(w, n * step) for (w, n) in most_common]

For our file, the average error for doing this is 2.8%. Not too bad and twice as fast as the previous solution.

Finally, for bigger files, we’d probably want a lazy solution:

def lazy_line_reader(file_name, fmt):
    return (word for line in open(file_name)
                 for word in fmt(line))


def lazy_count(file_name, most_common=10):
    def _increment_key(d, word):
        d[word] += 1 
        return d

    return sorted(reduce(_increment_key, 
                         lazy_line_reader(file_name, line_format),
                         collections.defaultdict(int)).iteritems(),
                  key=lambda x: x[1], reverse=True)[:most_common]

The github repo is here.

Posted in code | Tagged | Leave a comment

hg diff with gvimdiff

It’s easy to set up vimdiff to show hg diffs of a single file, but if you want to see diffs of all modified files, you run into problems. The hg diff command (with extdiff extension) will pass the directory paths to the diffing application, but unfortunately vimdiff does not handle directory diffs. If you google around, the solution you find is to use the DirDiff plugin for vim.

Since I’m not a big fan of the DirDiff plugin and because, really, I just want to have a command that opens diffs of all changed files one after the other, I wrote a small python script to do just that:

#!/usr/bin/python

import subprocess

output = subprocess.check_output(["hg", "status", "-m"])

stats = [line.split(' ') for line in output.split('\n') if line != '']

for stat in stats:
    subprocess.call(["hg", "d", stat[1]])

It calls hg status to find all modified files, then calls ‘hg d’ on each of them. To use it, save it to a file, make it executable and copy / symlink it to some directory on your path (e.g. /usr/local/bin).

‘hg d’ then needs to be configured in .hgrc:

[extensions]
hgext.extdiff =

[extdiff]
cmd.d = gvimdiff
opts.d = --nofork

The ‘nofork’ option is needed to make the shell wait for one gvimdiff instance to finish before opening the next.

For some reason, using plain vimdiff to see diffs doesn’t work at all for me, even if I call it directly. I’m not sure why, but I see no reason why the script wouldn’t work for vimdiff, too.

Also available as a gist.

Posted in tools | Tagged , , | Leave a comment

Python decorator to limit calls

Situation: you have a recursive function that has a bug in it and never hits the base case. You end up with an exception about having exceeded the maximum recursion depth. You’re most interested in seeing how the first few recursive calls pan out, but getting at this information is not easy. You can add a print statement, but you’ll have to do a lot of scrolling to find the output (as the infinite recursion spams your screen). You can modify the function to make it terminate early, but that’s a hassle… unless you use a decorator.

def limit_calls(limit, limit_return=None):
    count = [0]

    def _inner_decorator(fn):
        def _inner_function(*args, **kwargs):
            if count[0] < limit:
                count[0] += 1
                return fn(*args, **kwargs)
            else:
                return limit_return

        return _inner_function

    return _inner_decorator

This function will limit the number of times a function can be called. If it is called again after reaching that limit, the function will immediately return whatever is specified in limit_return.

Usage would look like this:

@limit_calls(5, 1)
def fact(num):
    if num == 1:
        return 1
    else:
        return num * fact(num - 1)

So here we are limiting the number of calls to 5 and after the 5th call, the function will return 1.

Posted in code | Tagged , , | Leave a comment

Nuggets, three ways

Suppose you want to know if it’s possible to exactly get some number of nuggets by buying some combination of boxes of 6, 9, and 20.

Here’s a recursive solution that counts down.

def nuggets1(num):
    if num == 0:
        return True
    elif num > 0:
        return nuggets1(num - 20) or nuggets1(num - 9) or nuggets1(num - 6)
    else:
        return False

NB: The order in which the function calls itself makes a big different in terms of performance.

Also recursive, but counting up.

def nuggets2(num):
    def _inner_nuggets(current):
        if current == num:
            return True
        elif current < num:
            return (_inner_nuggets(current + 20) or _inner_nuggets(current + 9) or
                    _inner_nuggets(current + 6))
        else:
            return False

    return _inner_nuggets(0)

And some good old iteration.

def nuggets4(num):
    return any(twenties * 20 + nines * 9 + sixes * 6 == num
                    for sixes in xrange((num / 6) + 1)
                    for nines in xrange((num / 9) + 1)
                    for twenties in xrange((num / 20) + 1))

Twenties going into the innermost loop might not be intuitively obvious.

Posted in code | Leave a comment

Getting a playing cards point score

So suppose you have playing cards represented as two letter strings, for example “AS” for the ace of spades or “4H” for the 4 of hearts. Now suppose you want to get the point value for each card.

One way to go about it might be the if-block from hell:

def get_score(card):
    if card[0] == '2':
        return 2
    if card[0] == '3':
        return 3
    # ...
    # ...
    # ...
    if card[0] == 'K':
        return 13
    if card[0] == 'A':
        return 14

(Granted, you could simplify it a bit by checking if it can be converted to a number first.)

You could also create a map to do it:

score_map = {
        '2': 2
        '3': 3
        # ...
        # ...
        # ...
        'K': 13
        'A': 14
        }

def get_score(card):
    return score_map[card[0]]

Or you could do:

def get_score(card):
    return '--23456789TJQKA'.index(card[0])

Granted, the mapped one would have better performance, but I still think this one is kind of nifty. I came across it on Udacity in Design of Computer Programs by Peter Norvig. I have tried a few of the online programming courses that are springing up all over the place, and that one was the best so far (along with the Scala one Coursera).

Posted in code, Uncategorized | Leave a comment

Hello world… again!

This is the third time I am making a start on this blog now. First time around I never actually got around to posting anything because I got sidetracked by messing with the theme and presentation and getting everything to look just the way I wanted, which of course never happened. Second time, I did write a few posts over the course of a month, but then I started to make grander and grander plans for future posts until they got so big, I didn’t feel like doing the research for them. So for the third time around the block, I figure I will try to just consistently post something, even if it’s just a snippet.

And three times lucky, right? The perfect is the enemy of the good. Worse is better.

We’ll see how it goes.

Posted in blablabla | Leave a comment

Windows 8 on the Acer 1825ptz

Since nothing exciting has been happening on my laptop recently, and after finding out that Microsoft has an Acer 1825pt in their lab, I figured I might try to throw Windows 8 on my Acer 1825ptz.

I used this guide and everything went down without a hitch. Drivers mostly installed, too (except for Bluetooth), though the G sensor doesn’t seem to work at all, which is kind of a bummer. Still, not bad for a dev preview. Performance is on par with Windows 7 and the few applications I tried (browsers, UltraVNC, dropbox, daemon tools, virtual box + Ubuntu 10.04) seem to work fine, too.

Windows 8 on Acer 1825PTZ

So in case anyone has an Acer 1825ptz lying around and is wondering how it plays with Windows 8, I’d say it’s already usable.

In other news, I’m surprised to find that I rather like the chrome on Windows 8. After figuring that I’m permanently out of sync with the Microsoft UX Design team, and that I have to go hunting for the option to switch back to the “classic” theme until I’m all wrinkly and grey-haired, they go and get rid of the overly rounded corner bubbly look. Probably going to be the first time since Windows 2000 that I’m sticking with the default.

Posted in tools | Tagged , , | 1 Comment