Hack and /

Lightning Hacks

Kyle Rankin

Issue #170, June 2008

Instead of one large hack, this month, I cover a few of my favorite smaller hacks to manage windows, switch my display to a projector and perform binary diffs on large files.

One of the more interesting parts of any conference is the lightning talks. If you haven't experienced one, a lightning talk typically features a number of different speakers, each giving a short (5–20 minutes) presentation. Lightning talks take advantage of the fact that often a speaker has an interesting topic to present, but the topic won't fill an entire hour time slot. So, lightning talks round up each of these speakers one after the other in the same time slot. Because of the variety of information and the fast nature of lightning talks, they can be really informative, interesting and definitely fun.

As I was considering what topic to cover for this month's column, I realized I had a number of different hacks I'd like to mention, but none that could really fill a full column. In the spirit of lightning talks, I decided to put all of these hacks together in true rapid-fire fashion.

Move Windows to Their Default Location

In the March 2008 issue of Linux Journal, I introduced the wmctrl tool and discussed how to use it to move, resize, shade and do all sorts of window management tasks from the command line. I also introduced a few scripts I had written and bound to keys to resize and shade a few different windows on my desktop. Near the end of that article, I mentioned:

My next project is to create a “reset” script that moves all the windows on all of my desktops to precise locations and sizes, in case they all get moved around and resized. Sure, I could do all this by hand, but then I'd miss this great opportunity for automation.

Well, shortly after I wrote that, I completed my reset script. This script goes from desktop to desktop (or because I use Compiz, viewport to viewport) and moves and resizes windows per my specifications. I've added comments to explain particular sections:

#!/bin/sh

# First save my current viewport so I can return 
# to it after I'm done
SAVED_VP=`wmctrl -d | perl -ne '/VP: (\d+,\d+)/; print $1;'`

# Then, move to the first viewport (at 0,0). Because it 
# can take a second or two for this to take effect, 
# I've opted to create a while loop that will
# continue to attempt to switch to that viewport 
# until it detects it is actually there.
VP=0,0
while [ `wmctrl -d | perl -ne '/VP: (\d+,\d+)/; 
 ↪print $1;'` != $VP ];
do
    wmctrl -o $VP
done

# Now resize, move, and change state of particular 
# windows (see the wmctrl man page, or my wmctrl 
# column for more information on the options).
wmctrl -r 'Eterm Main 1' -e '0,0,0,645,420'
wmctrl -r 'Irssi Term' -e '0,469,0,810,500'
wmctrl -r 'Irssi Term' -b add,shaded
wmctrl -r 'Irssi Term' -b add,below
wmctrl -r 'gkrellm' -b add,sticky
wmctrl -r "Irssi Notify Term" -e '0,1180,550,100,230'

# I now switch to the second viewport. As my screen 
# is 1280x768, the second viewport is at 1280,0. 
# If I wasn't sure, I could switch to that viewport
# and check the output of wmctrl -d for the proper coordinates.
VP=1280,0
while [ `wmctrl -d | perl -ne '/VP: (\d+,\d+)/; 
 ↪print $1;'` != $VP ];
do
    wmctrl -o $VP
done

wmctrl -r "Mozilla Firefox" -e '0,5,0,1040,708'

# Finally I switch back to my original viewport 
# so I'm back where I started.
wmctrl -o $SAVED_VP

Although there are certainly a lot of commands in that script, it actually didn't take long to write. Most of the script is simply one wmctrl command after another, and I spent a majority of the time actually fine-tuning the locations of each window and figuring out the best way to switch viewports. If your desktop environment uses multiple desktops instead of one desktop with multiple viewports, you would use the -s option to change desktops instead of the -o option, which is used for viewports. You also would need to change the logic in the while loop to something more like:

DESKTOP=1
while [ `wmctrl -d | perl -ne '/^(\d+).*?\*/; 
 ↪print $1;'` != $DESKTOP ];
do
    wmctrl -s $DESKTOP
done

Toggle My Display for Presentations

Although I normally use my laptop with its own built-in screen, I frequently give presentations, so I need to display on both the LCD and the external VGA connector. Unfortunately, my laptop's function keys to toggle between those states don't currently work in Linux, so I've had to put it into a script paired with a keybinding.

The xrandr program works great with my laptop to toggle between displays, so my script first examines the output of xrandr to see whether the VGA port is connected, and if so, it adds it as a display. Otherwise, it disables VGA. I also added a line to echo some text to osd_cat. I installed this program so that I would get some output on the screen to let me know which mode my script had chosen. When I'm ready to output to a projector, I just connect it to my laptop and run the script. When I'm finished with the presentation, I disconnect it and run the script again:

#!/bin/sh

if xrandr | grep -q 'VGA connected'; then
    echo "LVDS + VGA" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
# choose my laptop screen's resolution by default, 
# if that fails try the auto-detected mode
    xrandr --output VGA --mode 1280x768@60 || xrandr 
 ↪--output VGA --auto
else
    echo "LVDS only" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
    xrandr --output VGA --off &
fi

I also created a separate version of the script that spans across both screens instead of mirroring. I chose to span below my current screen (with the --below LVDS option), but most people probably will prefer to use --right-of or --left-of:

#!/bin/sh

if xrandr | grep -q 'VGA connected'; then
    echo "LVDS + VGA span" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
    xrandr --output VGA --mode 1280x768@60 --below LVDS || xrandr 
 ↪--output
VGA --below LVDS --auto
else
    echo "LVDS only" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
    xrandr --output VGA --off &
fi

What's the Difference?

Recently, I was working on a remastered Knoppix DVD that I had sent out to a few people. After I had sent out the full remastered DVD, I found out that I needed to change a few small files on the DVD. Even though my home DSL speeds are pretty fast, the upload is still slow enough that it took overnight to transfer the 3GB+ DVD image. I didn't want to go through that again, especially as I had made only minor changes to the DVD.

I knew that binary diff tools existed, but I discovered that not all of them are equal. Some binary diff tools require enough RAM to store multiple copies of the file, which certainly wouldn't work with a 3GB image. Lucky for me, I found rdiff, a tool that works well with large files and doesn't require a lot of RAM. What's better is that rdiff works with any binary—you can use it for any large binary files from DVD images to virtual disks to multimedia files.

rdiff works via a three-stage process. In this example, I have two files, old.iso and new.iso, that have minor differences from each other. For the first stage, you create a signature file that rdiff uses to represent your original file:

$ rdiff signature old.iso old.signature

Now that you have a signature file, use it with rdiff to create a delta file that represents the differences between the old and new files:

$ rdiff delta old.signature new.iso new.delta

This new.delta file is now all that anyone needs to convert old.iso to new.iso. For me, this file ended up being around 150Kb, because I had made only a few changes. The delta file was much simpler to send around than the full image. If you want to test that the delta file will work, first create an md5sum of new.iso:

$ md5sum new.iso

Then, use rdiff to patch the old file with the delta to create the new file. This is the same command that everyone else with the original file will use:

$ rdiff patch odl.iso new.delta newtest.iso

Now that you have newtest.iso, create an md5sum of that file and compare it with the one you made for new.iso:

$ md5sum newtest.iso

As I said before, this method works not only with ISOs, but also with any binary file large or small. It's worth noting that rdiff works with the same binary diff method rsync uses. rdiff just lets you use the algorithm step by step on the command line.

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.