Hack and /

Automate your Desktop with wmctrl

Kyle Rankin

Issue #167, March 2008

Why move, resize and shade windows by hand when a program can do it for you?

Okay, I'll admit it; I'm addicted to automation. A Roomba vacuums for me, my main router checks its DSL connection and automatically resets my DSL modem if it's down, my porch light is motion-sensitive, and my bin directories are full of homegrown scripts I use to automate mundane computer tasks. There is something so satisfying when you can reduce a long series of steps down to a single script and just run that script.

When most people think of automation with scripts, they think about the command line. After all, most scripts are concerned with standard command-line fare, such as pipes, simple logic, redirection and parsing text output. These days, much of the work on the desktop is done without a terminal, so it would be nice if you could automate some of those more mundane graphical tasks too. A tool called wmctrl can do exactly that. wmctrl provides a command-line interface to standard window management tasks, so you can resize and move windows, change desktops, toggle sticky and rolled-up statuses on a window and much more, all from a shell script.

wmctrl is a common package in most modern distributions, so you should be able to install it with your distribution's package manager. Otherwise, you can obtain the source from wmctrl's main Web site (www.sweb.cz/tripie/utils/wmctrl) and build it. One of the great things about wmctrl is that it isn't window-manager-specific. It changes your windows via Extended Window Manager Hints (EWMH), and because most the popular window managers these days (such as GNOME's Metacity, KDE's KWin, Compiz Fusion and Fluxbox) support EWMH, not only will wmctrl likely work with your window manager, but also if you decide to change to a different window manager, your wmctrl scripts probably will work just the same.

Quake Terminal

One of the best ways to illustrate the power of wmctrl is to create a script that turns a regular terminal into a Quake terminal. For those of you who haven't played any games from the Quake series, when you press the ` key in Quake, a terminal pops down from the top of the screen so you can type commands. This type of terminal is very handy on a cluttered desktop, but you even could use this to create a type of “boss button” to make a window disappear quickly.

In this example, I create a terminal that I've titled “Quake Term”, but you can change this script to work with the title of any window on your desktop. If you are unsure how wmctrl will view your window's title, run wmctrl with the -l option to show information about all the windows on your desktop:

greenfly@minimus:~$ wmctrl -l
0x020000ba  0 minimus Quake Term
0x00e00031 -1 minimus Desktop
0x01200003 -1 minimus gkrellm
0x00800029 -1 minimus Top Expanded Edge Panel
0x00800003 -1 minimus Bottom Expanded Edge Panel
0x01000172  0 minimus greenfly.org - Mozilla Firefox

The very last field in this output is the title of a particular window, and this is the information wmctrl can use to identify windows for which you want to script actions. To create a basic Quake Term, you just need a single wmctrl command:

#!/bin/sh

wmctrl -r 'Quake Term' -b toggle,shaded

The -r option tells wmctrl the window title on which to act, and the -b option tells wmctrl either to add, remove or toggle up to two different window properties (in this case, the shaded state of my window). The wmctrl man page lists all the available properties you can tweak with this and any other options.

Note that wmctrl scripts work best if windows have unique titles. If you have multiple windows open with the same title, you might not shade the right one. Each terminal sets its title differently, but for instance, on a GNOME terminal, you can change the title within your profile settings (right-click on the terminal and select Edit Current Profile).

I use a modified version of the above command that not only shades the window, but also moves it to the back below any other windows. The script also keeps track of the toggled state with a temporary file so that I can be sure the shaded and stacked states stay in sync:

#!/bin/sh

# Unshade and bring to front
if [ -f /tmp/.quake.shaded ]; then
    wmctrl -r 'Quake Term' -b remove,below
    wmctrl -r 'Quake Term' -b remove,shaded
    rm /tmp/.quake.shaded
# Shade and send to back
else
    wmctrl -r 'Quake Term' -b add,shaded
    wmctrl -r 'Quake Term' -b add,below
    touch /tmp/.quake.shaded
fi

I simply bind Super-` to run the above script, and then I can toggle my terminal up and down with a quick key sequence.

Quake terminals are handy, but you can do much more powerful things with wmctrl. One of the most handy scripts I've created with wmctrl solves a problem I've had when I chat in IRC and browse the Web at the same time—it's a pain to resize both windows so you can see both, just to resize them back when you are done chatting or browsing. wmctrl lets you resize and move windows, provided you know how to describe the new window location and geometry. With this in mind, I've created a script that toggles between two states: normal mode and chat mode. In chat mode, my IRC window shrinks and moves so that it sits in a narrow strip at the top of the screen, and my Web browser resizes to be shorter so I can see both windows at the same time. Then, I can run the script again, and the windows move back to their normal locations.

To create the script, first arrange your two windows (in my example, one with “Irssi Term” in the title and one with “Firefox” in the title) how you normally want them, and then run a special wmctrl command to list all the windows on your desktop along with their geometry and size information:

greenfly@minimus:~$ wmctrl -lG
0x00e00031 -1 0    48   1280 768  minimus Desktop
0x01200003 -1 -130 100  62   367  minimus gkrellm
0x00800029 -1 0    0    1280 24   minimus Top Expanded Edge Panel
0x00800003 -1 0    1524 1280 25   minimus Bottom Expanded Edge Panel
0x01000172  0 6    96   1040 708  minimus greenfly.org - Mozilla Firefox
0x0201c24f  0 -2552 96   642  410  minimus Eterm Main 1
0x02000021  0 -2552 96   642  410  minimus Eterm Main 1
0x020000ba  0 938  96   810  500  minimus Irssi Term

In this output, the -G option adds four extra columns in the middle. These columns represent the x-offset, y-offset, width and height, respectively. So, in the case of Firefox, the x-offset is 6, the y-offset is 96, the width is 1040, and the height is 708. Jot down these values for the two windows you want to script, and then resize and move them to reflect your “chat mode”. Next, run the command again and jot down the new values.

wmctrl provides the -e argument that allows you to modify the position and size of a window. The argument actually takes five integer values in a row—g,x,y,w,h—where g is the gravity of the window (usually put 0 here), x and y are the x and y coordinates for the top-left corner of the window, and w and h are the width and height, respectively. So, if I had moved my Firefox terminal and wanted to move it back to the above coordinates, I would run the following:

wmctrl -r Firefox -e '0,6,0,1040,708'

If you look carefully, you might notice I changed the y coordinate to 0 instead of 96. I've found that in some window managers, the geometry the window manager reports to wmctrl is different from reality. Basically, you need to do a little trial and error and tweak the coordinates so that everything lines up just right. Once you are satisfied with your respective wmctrl commands, you can throw them in a script very similar to the one I used above for the Quake terminal:

#!/bin/sh

# Change to normal mode
if [ -f /tmp/.irssi.halfshaded ]; then
    wmctrl -r 'Irssi Term' -e '0,469,0,810,500'
    wmctrl -r Firefox -e '0,3,0,1040,708'
    rm /tmp/.irssi.halfshaded

# Change to chat mode
else
    wmctrl -r Firefox -e '0,3,223,1210,535'
    wmctrl -r 'Irssi Term' -e '0,0,0,1214,160'
    touch /tmp/.irssi.halfshaded
fi

I noticed that with the current window manager (Compiz), when I ran this command, some bug—either in wmctrl or, more likely, in the window manager—caused Firefox to move from my second desktop to my current desktop. If this happens to you, there's a simple fix. Simply add the following line above the if statement in the script:

wmctrl -o 1281,0

wmctrl has commands both for shifting to different desktops and also to different viewports. Because Compiz often uses multiple viewports instead of desktops, the above command moves me to the second viewport (my desktops are 1280x768, so 1281,0 corresponds to the top corner of my second viewport).

wmctrl has a lot of power. I recommend looking at its man page and reading about the large number of available options. The real power in wmctrl, however, lies in your ability to imagine new and interesting ways to script window manager actions. My next project is to create a “reset” script that moves all the windows on all my desktops to precise locations and sizes, in case they all are moved around and resized. Sure, I could do all that by hand, but then I'd miss this great opportunity for automation.

Kyle Rankin is a Senior Systems Administrator in the San Francisco Bay Area and the author of a number of books, including Knoppix Hacks and Ubuntu Hacks for O'Reilly Media. He is currently the president of the North Bay Linux Users' Group.