Showing posts with label Ubuntu. Show all posts
Showing posts with label Ubuntu. Show all posts

Network audio with PulseAudio made (somewhat) easy

I figured it was long past time to buckle down and learn how to make PulseAudio do my bidding and redirect audio across a network link. And I was surprised to learn that it's actually not hard to set up. In fact you don't need to touch any config files in /etc. Which you would never know, from reading most of the documentation that is out there.

While network audio is still kind of flaky at times, you only "pay" for it if you use it (people complain about PulseAudio a lot, but in my experience it works very reliably when used locally), and it can come in very handy.

Background

Throughout, I'll refer to the two computer roles as audio source and audio sink (where audio data is generated/decoded and where the speakers are attached, respectively). Note that these may differ from the PulseAudio concepts of the same names.

Ubuntu ships with PulseAudio, and many apps (among the common graphical apps and music players) understand how to talk to PulseAudio now (possibly through a compatibility layer). Except maybe Flash, but everyone who uses Flash is already used to it not being a proper citizen. The default setup is to have a per-user instance of PulseAudio running alongside the X session. This means that someone has to be logged in to X on both ends. If your audio sink is headless then you might have a single system-wide PulseAudio instance instead. Configuring that is not covered here.

PulseAudio runs as a service on port 4713. We need to first make that service discoverable on the machine with the audio sink, and then provide a way for the machine with the audio source to authenticate to the sink (so that you're not just letting anyone on your network play their crappy music).

Initial steps

On the machine with the audio sink:

  • Install and run paprefs &
  • Go to the Network server tab.
  • Check Enable network access to local sound devices and Allow other machines on LAN to discover local sound devices. Autodiscovery uses Avahi, which you'll need to re-enable, if you ever disabled it.

(You might have to restart PulseAudio or X for the settings to take effect.)

I will describe two ways you can get the machine with the audio source to supply credentials to the sink so it can play music. The first one is likely to be more generally useful.

Using .pulse-cookie

The ~/.pulse-cookie file is a shared secret that can be used for authentication. Just copy that file from either machine (sink or source) to the other so that they are the same on both ends.

At the audio source, install padevchooser and run it (padevchooser &). A menu will appear in your notification area. Under Default server you should see an entry for the sink, assuming it is on the same local network (it should be named username@hostname). Select it.

Now run the application of your choice and play some audio!

Piggybacking on X11 forwarding

This method has the advantage of not requiring working autodiscovery. So you can use it on a LAN without Avahi running, or over a non-local network. All you need is to be able to SSH from the sink to the source. We will forward the audio from the source to the sink on a TCP port.

For this method, we'll transfer the credentials that the source will need by writing them to the X11 root window. PulseAudio supplies a program that does exactly this. On the machine with the audio sink, run:

sink$ start-pulseaudio-x11

(You can double-check that this is working by running xprop -root | grep PULSE and checking that there is an entry for PULSE_COOKIE, among others.)

Now initiate an SSH connection to the source, tunneling a port of your choice on that end over to your local PulseAudio server:

sink$ ssh -X -R 9998:localhost:4713 source

Run the application of your choice and play some audio! As you are doing so, set the PULSE_SERVER variable, which now enables your remote application to talk to your local audio hardware via the tunneled port.

source$ PULSE_SERVER=localhost:9998 rhythmbox &

Conclusion and caveats

Et voila, now you've decoupled your music player from your speakers. So you can play music from your laptop using the speakers at your desktop. Or you can play music from your desktop using the speakers in your living room. Some things to watch out for.

  • You may need to kill and restart pulseaudio (or X) to get it to pick up some of those configuration changes.
  • PulseAudio will not move a stream while it is running, so if you use the menu to change the destination, the change will not take effect until the next song begins, unless you pause and restart playback in your audio application.
  • PulseAudio is kind of finicky and some of it still feels like black magic at times. You may need to restart it if you suddenly get errors about things not working, especially if they were just working a minute ago.

Sources and further reading: pulseaudio-discuss, ArchLinux wiki, Ubuntu forums

Usability gems in GNU/Linux

My post Thank you, Ubuntu attracted some attention (hello, Hacker News!). A couple of commenters were skeptical of my assertion that GNU/Linux is more usable than the major proprietary operating systems— and rightly so, since I didn't really elaborate on it at the time. For example, commenter Dubc wrote:

I think Ubuntu is a fine choice indeed, but you are going to either have to use another word than usability, or redefine the term to meet your presumptions.

So I'd like to expand on a few of the things that make Ubuntu such a pleasure to use, for those who aren't familiar with its ins and outs, especially because I so rarely see people take the time to articulate some of these nifty constructs at a high level.

I will happily grant that at the application level, Windows and Mac OS have a much more consistent base of polished applications. And Apple has an attention to detail and psychology that others would do well to learn from.

It's often said that Apple takes the time to get the little things right. Unfortunately, at times I think they should have instead spent a bit more time thinking through the big things in Mac OS. There are some fairly glaring architectural/design/HCI problems on the Mac (and on Windows, too) that hamper users needlessly. And because these are issues with how one interacts with the system at its most basic levels, no amount of polish on the "little things" can really satisfactorily outweigh them.

What that boils down to is this: yes, you will wrestle with GNU/Linux for a few hours or days or weeks when you get it set up. But if you use Windows or Mac OS, you will wrestle with it every minute of every day you're using it. Forever. And that's just not what "usability" means to me.

Here are some illustrative examples:

apt

An anonymous commenter wrote:

in Mac installing software is a single drag-and-drop function

Oh, if only that were the case. The flow for installing software on Windows/Mac looks something like this (some steps omitted for brevity):

  1. Open a web browser and search for the software you want.
  2. Navigate to the vendor's web page, fish around for the download link, and start downloading the software. Wait.
  3. Find the file you downloaded and open the installer/archive.
  4. Accept the license agreement.
  5. Drag the item to the dock (Mac only).
  6. Answer some setup questions. Wait.
  7. Delete the installer/archive.

These steps are error-prone, and unsophisticated users will quickly find themselves with malware on their systems. We should help users to do the right thing by default when installing software, and subjecting them to the vagaries of the wide-open web isn't the right thing.

On Ubuntu:

  1. Open Synaptic and search for the software you want.
  2. Click install. Wait.

Whatever the heck you want, you can find it in Synaptic. And there's no distinction between software that was preinstalled with your system and software that you choose to install later. It all works the same way and gets security updates in the same way. And it looks like Mac OS is finally starting to head in this direction with the App Store for Mac software.

But, what's more, each of the thousands of pieces of software available in the Ubuntu (or whatever distro you use) repository has an audit trail, and— unlike the apps you get in the Mac App Store, or any "App Store," really— Ubuntu has the means, motive, and opportunity to do whatever it takes to make them "just work," no matter where and when and for whom it was originally written. There is a vast body of software that is just considered to be part of your operating system and is maintained alongside it, and Ubuntu will vouch for that software just as it will vouch for the software at the core of your system.

It's not clear whether you will ever see that sort of large-scale integration work in a proprietary OS. Package management is really one of the highlights of the free software ecosystem.

Remote access

X11 has pretty much decoupled where a program runs and where the program can be used. You have a few basic composable tools— SSH, X forwarding, and port forwarding— and all of a sudden the vast majority of applications can be invoked remotely. So you can, very easily, interact with a program that's using the resources (CPU, filesystem, network bandwidth) of a computer sitting somewhere else. There are a few gotchas with respect to X11 (watch out for high-latency network links, for example), but by and large it works.

"But wait," you say, "Mac OS has SSH and X11 too." Except that most of the apps you want to use can't be invoked remotely, because they aren't X11 apps. So you have a litany of tricks. VNC and NX are appropriate sometimes, but they're clunky. Mac OS has "Back to My Mac" which is a slightly more general tool (and costs $99/year). But typically, whenever you work remotely you have to work differently too. It's a far cry from the situation on GNU/Linux, where you have a critical mass of applications you can use remotely and they just work, more or less transparently.

The result is that Mac and Windows users are typically highly dependent on a single piece of hardware. If you have a nice light laptop, more power to you. They say the best computer is the one you always have with you, after all. But even better than that is not having to carry a computer around. It's incredibly liberating, not to mention convenient. You can actually go places without having to pack a bag.

On GNU/Linux, one barely even perceives the concept of "other computers." Every computer you use, no matter how far away it is, is so accessible to you, so immediate, that it might as well be the one sitting on your lap right now.

Of course, Microsoft and Apple don't really have any incentive to make remote access terribly effective. Since they generally sell you a license to use a particular instance of their software on a particular computer, it would cut into their bottom line to make remote access actually useful. So they won't.

Window managers

The Windows/Mac window managers (the part of your OS that lets you manipulate windows and switch between them) are pretty bad. They weren't always this way— they have become noticeably clunkier on the large displays that are so common today.

On large displays, maximizing (zooming) windows is not useful much of the time. Maximized windows are just too big. Instead you have to move and resize windows to obtain a useful multi-window arrangement on your screen. But Windows and Mac OS give you very little help in this regard. To produce these arrangements you usually have to click on some tiny button/target and manually move windows around.

For example, moving or resizing windows on Windows or Mac OS typically requires you to point at the titlebar or resize handle or window border— both of which are just a few pixels wide.

Fitts's Law? Bueller? Bueller? Having enabled alt-dragging to move or resize on any capable X window manager (I use Openbox, but even the Ubuntu default window manager supports this), the entire window becomes your drag target. The entire window. (Alternatively, if you use a tiling window manager, even the whole concept of manually moving windows around becomes basically moot.)

In fairness to Apple and Microsoft, they have taken some steps in the right direction recently. Windows 7 "docks" windows to one side of the screen when you ask it to. You can configure some recent Macs to let you drag windows with three fingers (or so I've heard). And Mac recently gained Spaces (i.e. virtual desktops) too.

Unfortunately, I think these are the exceptions that prove the rule. Mac OS and Windows users have waited for years only to get a fraction of the window management facilities that you could have set up in your X11 window manager. Supposing you spent an hour setting up your WM to your liking, I figure you would earn back that time in improved productivity in just a few weeks, rather than having to wait years for Microsoft or Apple to implement a sensible workflow. But more importantly, your blood pressure would go down, immediately.

People really are more effective when they can set up their computers to work they way they want. But on Windows and Mac OS they aren't given the tools they need to do so. I wouldn't disparage these operating systems so much if they came with window managers that could at least be configured to be minimally adequate. But they don't.

The window manager is the one program you're using all the time, even when you're using other programs. It's your primary vehicle for multitasking and your command center for managing your work on a second-by-second basis. So it's really important to get this right.

Conclusion

Having read this post, you might decide that you would brand these issues not as "usability" issues but as something else. Which is completely fine by me. A rose by any other name, after all…

Many of the cumbersome rituals that Windows and Mac OS have whittled away at over the years are completely unnecessary in Ubuntu. And this is why, while I've had occasion to use a Mac (at various times since 2004 or so) and Windows (only occasionally), sitting down at one always feels like death by a thousand cuts. They're superficially simpler, but really quite tiresome once you get beneath the surface. It seems that hey are optimized for learnability at the expense of usability. That is is exactly the wrong optimization to make, if you use a computer for a living.

Life is just too short for that.

Now, it's not like Ubuntu is a paragon of usability out of the box, either, though some of the things I mention above do just work by default. The difference— the key difference— is that you can make it into one without much hassle.

Thank you, Ubuntu

Ubuntu 10.10, the Maverick Meerkat, will be released in just a couple of weeks. That got me reflecting on the fact that I have been a happy user of Ubuntu for what must be over 5 years now. That's a long time!

The GNU/Linux variants are the only OSes I've used where I really have the flexibility to define my own workflow (example). So they are a pleasure to use (ok, most of the time). I use a computer for many, many hours a day nearly every day. And the time spent customizing software and learning it is a drop in the bucket when it's amortized over the months and years I'm going to spend using it. Sure, Windows and Mac OS are a bit more learnable and easier to get started with— but they are much less usable. And for me, and most other people who sit at a computer for a living, that is precisely the wrong optimization to make.

There's plenty more to love about Ubuntu: for starters, that it runs on every piece of hardware you throw at it; how with a modest amount of effort, you can make all the computers you use behave exactly the same; and how great apt is (really, it takes the fear and hassle out of installing software, and it's an experience that no proprietary desktop OS comes close to).

Ubuntu is far from perfect, but it is pretty marvelous, and all the GNU/Linux operating systems have come a long way in the last 5 years. When I step back, I'm a bit astonished that Ubuntu or anything like it even exists at all. It works, it's powerful, it's free of charge, and, with small carve-outs, all of it is free for anyone to do anything they wish with it.

One thing I rarely stop thinking about is how technology can be made to be an instrument of empowerment. And I believe that one necessary step in that direction is ensuring that you are the master of all these amazing devices you carry around with you all the time: that they serve you and carry out your will, and not the other way around. Ubuntu has this vast collection of software you can use as the substrate for doing anything, and the question isn't "Will the creators of this software give you permission to do this?" but rather "Who the hell is going to stop you?".

I find this an incredibly heartening idea, almost a cousin of the concept of Turing's universal machine— the possibility, realizable in software, that you are limited by nothing other than your imagination. Unfettered computation is really a magical thing. And Ubuntu is a wonderful demonstration of that assertion, though by no means the only one.

So, to everyone that helped to make this possible (Canonical; the Ubuntu community; Debian developers; kernel developers; upstream maintainers and contributors of all stripes; and yes, even the folks working on other downstreams, like RH/Fedora— your code makes its way into Ubuntu too):

You have truly helped to make something wonderful, and it's a real gift to humanity. Thank you.

Thinkpad TrackPoint Scrolling in Ubuntu Maverick/10.10

Update: these steps seem to work on Ubuntu Natty/11.04 for me, as well.

My fellow citizens, our long national nightmare of having to use bad pointing devices is over.

Starting with Ubuntu Maverick/10.10, configuring scrolling with the middle button and the TrackPoint is now really easy:

  • Install gpointing-device-settings (sudo aptitude install gpointing-device-settings)
  • Run gpointing-device-settings &, enable the Use wheel emulation using button 2, and enable both vertical and horizontal scrolling.

(gpointing-device-settings is not new but Maverick is the first time it has really worked well for me.)

Update: some people are reporting that g-d-s settings are lost after suspend. If you run into this, you might try these alternative instructions.

Backups and rdiff-backup

I always thought nearlyfreespeech.net's advice on backups was good advice:

You should adopt a backup policy that assumes we are storing crates of sweaty dynamite on top of the servers that hold your important data. (Even though we aren't.) [NFSN FAQs]

If you only have one copy of something, stop what you are doing, obtain a disk, and replicate it.

Here are some brief notes on my backup setup, including some things I've learned since I last wrote about backups. (I had a disk failure last December and restored everything from backup. No tears, no sweat. In fact the exact same thing seems to happen about once every year, which I suppose is a good testimonial. I'm probably due for a disk failure real soon now.)

  • My first line of defense is backing up to a secondary HDD in my machine. I mostly use rdiff-backup now (and only rsync for huge files, like disk images). This system seems to work well. rdiff-backup creates reverse diffs on each backup so you can retrieve old versions. All the diffs go in the rdiff-backup-data subdir; if you remove that you just get a plain mirror, like what rsync would do.
  • I wrote a FUSE filesystem, rdiff-snapshot-fs, that displays rdiff's repository format as a series of mirrors in order to make it easier to browse historical snapshots. Doing a restore of individual files from time to time is key to ensuring your system is working when you really need it.
  • Rather than scheduling backups with cron and having to leave my computer on at night or, alternatively, having backups happen while I'm working, I bound a hotkey to a script that backs up and then puts the computer into suspend. I run it when I leave the computer for the day, every day.
  • I also rsync to other backup backup locations, including a portable HDD that stays in a safe place when I'm not using it.
  • When restoring from a mirror, the -c flag to rsync is useful. It makes rsync compare the checksum of the data being copied back with the checksum of the original. Then if you have multiple backups of the same stuff you can easily identify and reconcile any differences between them.
  • I did try rsnapshot. Unfortunately it caused my system load average to shoot through the roof, making the system unresponsive while backups were being made. I have no idea why this is but a few other people have reported the same thing.

Extracting the audio track from videos, and downmixing

On Ubuntu/Debian, you can use ffmpeg (in the package of the same name) to extract the audio only from video files:

$ ffmpeg -i input.ogm -ab 128k -vn output.ogg

ffmpeg detects the input and output formats automatically, so you can also convert to an MP3 (for example) just by specifying an output filename of the appropriate extension.

When converting audio for listening while working out or while traveling, it's also useful to downmix stereo to mono so you can listen with just one earbud without missing anything:

$ ffmpeg -i input.ogm -ab 128k -ac 1 -vn output.ogg

(Thanks to Rene Moser for the pointer. Also check out sox if you are looking to apply more sophisticated audio transformations.)

Further reading: ffmpeg command-line tool documentation

rdiff-snapshot-fs

I use rdiff-backup for backups. It creates a mirror of your data, just like plain old rsync, but also stores a series of reverse diffs that lets you restore historical versions of each file. For the most part it works well, but the interface for browsing and restoring from backups is somewhat clunky:

$ # List all snapshots
$ rdiff-backup -l /backup
Found 3 increments:
    increments.2010-06-01T23:00:00-07:00.dir   Tue Jun  1 23:00:00 2010
    increments.2010-06-02T23:00:00-07:00.dir   Wed Jun  2 23:00:00 2010
    increments.2010-06-03T23:00:00-07:00.dir   Thu Jun  3 23:00:00 2010
Current mirror: Fri Jun  4 23:00:00 2010
$ # List contents of a snapshot
$ # (note, lists specified dir recursively)
$ rdiff-backup --list-at-time 2010-06-01T23:00:00 /backup/foo/bar
foo/bar/one
foo/bar/two
foo/bar/more/three
foo/bar/more/four
$ # Restore a file from backup
$ rdiff-backup -r 2010-06-01T23:00:00 /backup/foo/bar/one ~/one.restore

I wrote rdiff-snapshot-fs, which is an alternative to rdiff-backup's restore interface. It's a virtual filesystem (powered by FUSE) that lets you browse all your snapshots and their contents. Now you can examine each snapshot as if it were backed by a real mirror:

$ ./rdiff-snapshot-fs.py /backup /view
$ ls /view
2010-06-01T23:00:00-07:00
2010-06-02T23:00:00-07:00
2010-06-03T23:00:00-07:00
$ ls /view/2010-06-01T23:00:00-07:00/foo/bar
one
two
more
$ cp /view/2010-06-01T23:00:00-07:00/foo/bar/one ~/one.restore

Some background: another tool, called archfs, provides similar functionality, but it has performance issues, and I never got it to successfully load my backup repository (granted, my homedir backup repo has 400 snapshots and a current mirror size of 450GB). I concur with Jon Dowland's assessment that the problem stems from archfs trying to reconstruct historical snapshot data up front before it's needed. (Many problems in software, as well as in life, are really problems caused by being insufficiently lazy!)

More info about rdiff-snapshot-fs, including how to obtain the source, can be found at http://web.psung.name/rdiff-snapshot-fs.

(By the way, FUSE is really interesting to play with and FUSE-Python makes it a snap to get started. More remarks about this soon, possibly.)

Disclaimer: This is something I cooked up in my spare time. rdiff-snapshot-fs is liable to eat your data and scare your pets. The file sizes and modes that it reports are known to be unreliable under certain circumstances (though the file data itself is generated correctly, as far as I can tell). But, if you know no fear, you may find it somewhat more useful than the standard rdiff-backup interface.

Thinkpad TrackPoint Scrolling in Ubuntu Lucid/10.04

Update: for (easier) instructions for Ubuntu Maverick/10.10 and later, click here.

Another Ubuntu release, another set of X.org shakeups.

Some things in X changed in Lucid (xorg 1:7.5+5 and higher), breaking existing Thinkpad TrackPoint scrolling configurations that modify files in /etc/hal/fdi/policy (like those you may have seen in this previous post). You can use gpointing-device-settings to apply the same policy, but I found that even that stops working after a suspend/resume cycle.

Samson Yeung pointed out to me the following fix which can be applied on Ubuntu Lucid/10.04:

Create a new file /usr/lib/X11/xorg.conf.d/20-thinkpad.conf with the following contents (on Ubuntu Maverick/10.10 and later, save the file to /usr/share/X11/xorg.conf.d/20-thinkpad.conf instead):

Section "InputClass"
    Identifier "Trackpoint Wheel Emulation"
    MatchProduct "TrackPoint"
    MatchDevicePath "/dev/input/event*"
    Driver "evdev"
    Option "EmulateWheel" "true"
    Option "EmulateWheelButton" "2"
    Option "Emulate3Buttons" "false"
    Option "XAxisMapping" "6 7"
    Option "YAxisMapping" "4 5"
EndSection

Then restart X.

The configuration above works for both Thinkpad laptops with TrackPoints and the Thinkpad TrackPoint keyboard.

Source: instructions derived from these instructions by Samson Yeung. Thanks, Samson!

Firefox 3.6

Mozilla released Firefox 3.6 last week. There are many noticeable improvements compared to Firefox 3.5, but two that stand out to me.

First, performance has improved quite a bit. And not just Javascript performance. Startup time is improved; the awesomebar responds instantly to keystrokes; Javascript and redraw/redisplay are faster. I no longer perceive a huge speed difference between Firefox and Google Chrome on most web apps, and the UI has become more responsive. (A few things—startup time, redraw/redisplay/scrolling, and creating/closing new windows and tabs—are still minorly annoying when I use Firefox alongside Chrome—but 3.5 and 3.6 are like night and day.)

Second, Firefox 3.6's implementation of HTML5 media, or at least audio, is superb. Having spent lots of time hacking on and listening to Zeya on Chrome, Firefox 3.5, and Firefox 3.6, the latter is the one where things most often just seem to work as spec'd. Moreover it's Firefox 3.6 (and only Firefox 3.6) that can consistently maintain the illusion that I'm using a native media player rather than a web-based one. That's colossal.

So, kudos to the Firefox developers.

PSA: the ubuntu-mozilla-daily PPA has nightly builds of Firefox 3.5, 3.6, and 3.7 packaged for all Ubuntu releases since Hardy. It's a low-hassle way to teach an old dog new tricks.

Manipulating Picasa Web Albums programmatically

I was pleased to find out that the Picasa Web Albums API makes it quite easy to programmatically upload and download photos from Picasaweb. So any desktop app that manipulates photos or graphics can easily be extended so that it can interact with web albums. The possibilities are manifold… below is some code that demonstrates a couple of the basic capabilities.

Like everything else on Ubuntu/Debian, you only need about 15 seconds to obtain all you need to get started with this. Just install the python-gdata package:

$ sudo aptitude install python-gdata

Uploading albums to Picasaweb

The following function creates a new Picasaweb album from a sequence of photos and associated captions.

#!/usr/bin/python
import gdata.photos.service
import os.path

def create_new_album(album_title, email, password, photos):
    """
    Creates a new Picasa Web Album with the specified images.

    album_title: title of album, e.g. "Spring Break 2009"
    email: Google account name, e.g. yourname@gmail.com
    password: Google account password
    photos: sequence of tuples where each entry is (filename, caption)
            e.g. [("/tmp/photo0001.jpg", "A huge turtle"), ...]
    """
    # Authenticate to Picasa Web Albums.
    gd_client = gdata.photos.service.PhotosService()
    gd_client.email = email
    gd_client.password = password
    gd_client.source = 'MyPhotoUploader' # Fill in the name of your program here.
    gd_client.ProgrammaticLogin()

    # Create the album.
    album = gd_client.InsertAlbum(title = album_title, summary = '')
    album_url = '/data/feed/api/user/%s/albumid/%s' % \
        (album.user.text, album.gphoto_id.text)

    # Insert the photos.
    for filename, caption in file_info:
        gd_client.InsertPhotoSimple(
            album_or_uri=album_url,
            title=os.path.basename(filename), # This shows up as 'Filename' on Picasaweb
            summary=caption,
            filename_or_handle=filename,
            content_type="image/jpeg")

Downloading albums from Picasaweb

Normally you can't download entire albums directly from the web (unless you have the Picasa client software installed). You can however use the API for this purpose (the parts in blue below are the core, non-interactive part of the program):

#!/usr/bin/python
#
# This interactive script prompts the user for an album to download.

import gdata.photos.service
import urllib

def main():
    "Downloads a Picasa Web Album of the user's choice to the current directory."
    gd_client = gdata.photos.service.PhotosService()
    username = raw_input("Username? ")            # Prompt for a Google account username.
    print_album_names(gd_client, username)        # Enumerate the albums owned by that account.
    album_id = raw_input("Album ID? ")            # Prompt for an album ID.
    download_album(gd_client, username, album_id) # Download the corresponding album!

def print_album_names(gd_client, username):
    "Enumerates the albums owned by USERNAME."
    albums = gd_client.GetUserFeed(user = username)
    for album in albums.entry:
        print '%-30s (%3d photos) id = %s' % \
            (album.title.text, int(album.numphotos.text), album.gphoto_id.text)

def download_album(gd_client, username, album_id):
    "Downloads all the photos in the album ALBUM_ID owned by USERNAME."
    photos = gd_client.GetFeed('/data/feed/api/user/%s/albumid/%s?kind=photo'
                               % (username, album_id))
    for photo in photos.entry:
        download_file(photo.content.src)

def download_file(url):
    "Download the data at URL to the current directory."
    basename = url[url.rindex('/') + 1:]  # Figure out a good name for the downloaded file.
    print "Downloading %s" % (basename,)
    urllib.urlretrieve(url, basename)

if __name__ == '__main__':
    main()

More info

Picasaweb API developer's guide (Python client). From that page you can also find more about bindings for .NET, Java, and PHP.

I hereby release the code in this post into the public domain.

Modifying SSH port forwardings mid-session

I frequently use SSH port forwarding to access services on computers I'm connected to (e.g. VNC, web servers, Zeya). For example,

ssh -L 8001:localhost:8080 foobar

connects port 8001 on my local machine to whatever service is running on foobar port 8080.

Sometimes I'll discover mid-session that I wish to connect to a new service I've just started up remotely, or that I forgot to add the -L flag for some service I wanted. I could always just disconnect, add the appropriate port forwardings, and reconnect.

However, I just learned that SSH also supports some escape sequences, one of which lets you break out to a command line, where you can change port forwardings mid-session without disconnecting.

With the default settings, type ~C at the beginning of your session or after a newline. You'll see a command prompt:

ssh>

At this prompt, you can add additional forwardings using the same syntax that ssh accepts:

  • Local forwarding to remote service: -L local_port:hostname:hostport
  • Remote forwarding to local service: -R remote_port:hostname:hostport
  • Dynamic forwarding, e.g. for SOCKS: -D port

Further reading:

Assorted notes

Many random notes (some for my own reference), each of which is too short to warrant a full blog post:

Emacs

It had always bothered me, just a little, that C-v and M-v, in addition to scrolling, move the cursor to the bottom or top of the screen. This has the odd effect that C-v M-v is not a no-op. Turns out that setting the variable scroll-preserve-screen-position appropriately can fix this. (via emacs-devel)

Chrome

  • Have you ever wondered why running Firefox instances get broken when new versions are installed? On GNU/Linux systems the package manager, which runs as root, pays no mind to what non-root users are doing. Some apps, like Firefox, run into major trouble because they'll load additional files after startup that can't be mixed and matched with the previous versions (this isn't really a problem for programs that are essentially just one binary). Chrome acquires immunity to this with its Zygote mode, which basically means do not load anything from disk after startup. A simple idea, but it takes some work to follow through with it.
  • Among visitors to this blog, Chrome is the second most popular browser, with a 9.5% share! Chrome leads IE (6.6%), Safari (5.9%), and Opera (4.3%), and trails only Firefox (72.6%). You can sort of see what a non-representative sample of web users happens across this blog.

Unix/Ubuntu

  • Soundconverter (aptitude install soundconverter) is a handy GTK/GNOME program for converting (transcoding) audio between formats. It is easy to use, transfers all the music metadata, and seems to take full advantage of multicore processors. (I use it to downsample my music, mostly in FLAC, to Ogg or MP3 for use on portable devices.)

  • When using find with xargs, xargs will get tripped up by filenames with spaces (it will treat each space-delimited component as a different argument). You can get around this by changing the delimiter in both find and xargs to \0 instead of space, as follows:

    find . -iname '*.tmp' -print0 | xargs -0 rm

    You can usually achieve the same effect with find ... -exec but I can never remember how to use it.

HTML, HTML5

Some things I picked up while working on Zeya:

Getting more "flow" from your window manager

Working on a Thinkpad keyboard is pure bliss. The Trackpoint lets me point (and scroll!) without moving my wrists; within Emacs/screen, even it is superfluous. Being able to immediately act on one's intentions is an important factor in attaining a state of flow, and there is a huge psychological difference between being able to do something "now" and "wait, hang on for 500ms so I can reach the touchpad." When I'm really in the zone, I can actually notice my concentration dissipating if I need to reach for the arrow keys, or worse, the mouse or touchpad (this is hard to explain to people who don't use Emacs or Vim!).

But window managers are still awful

Yet, even if I'm using Emacs and/or a web browser 90% of the time, and even supposing they left nothing to be desired, there's another program I have to interact with near 100% of the time: the window manager.

It's a shame, but few of the usability lessons we learned over the years made it to any of the common window managers— Windows, Mac, or Metacity. The window manager acts an intermediary sitting between the user and every single app, but WM functions are often marginalized and too hard to activate. This is especially unfortunate, because now that huge monitors are commonplace, we actually need better WMs to help us effectively use that screen space— maximize and minimize alone don't cut it anymore! (Jeff Atwood pointed this out all the way back in 2007.)

How can we do better? Well, what does "better" even mean? Here are the assumptions I'm operating under, about what kinds of operations are easy or hard to perform:

  • "Reaching is hard": reaching touch-typeable keys is easier than reaching the function keys (F1 … F12), the arrow keys, or the numpad.
  • "Pointing is hard": typing a small number of keystrokes is easier than using the mouse (or any other pointing device).
  • "Fitts's law": pointing at a large target with the mouse (e.g. a window) is easier than pointing at a small target (e.g. an icon, menu, button, border, or title bar).

You may be able to see where the problems are. Windows and Metacity map a bunch of window management functions to far-away placed keys like Alt+F4 and Alt+F10. Mac OS has better bindings for some functions, but doesn't map Maximize at all by default, instead providing access via itty-bitty buttons on each window. On both Windows and Mac OS, the fastest way to move (or resize) a window is by dragging it by its title bar (or resize handle). It doesn't get any worse than that.

Dragging resize handles… seriously? Fitts's law, anyone? This is nonsense.

This is the main reason that I can only use Windows or Mac OS for about 30 seconds before my blood pressure starts to go up. They are not usable. Having to fish around with the pointer every time you want to do something is also a great way to develop RSI.

Building a better WM

I've played around with a few scattered ideas with the goal of making a better WM for myself. I've implemented these in my Openbox config, although any reasonably configurable WM will do.

But, I would like to stress here, the details are unimportant. If you can find the parts of your workflow that are unnecessarily slow, and eliminate them, more power to you.

1. Manage windows and workspaces with shortcuts that don't require function keys or arrow keys. For example, Alt+F10 (maximize a window) and Ctrl+Alt+LeftArrow (switch workspaces) are out. In my setup they are replaced by Win+1 and Win+j, respectively.

2. Move and resize windows with alt-dragging (actually, Win-key dragging). When you want to move or resize the window, the entire window is your drag target. No need to aim for the title bar, or the resize handle, or the window border. Fitts's law, suckers.

To move windows: point at the window, hold down Win, and drag with the left mouse button. To resize windows: point at the window anywhere near the corner you want to move, hold down Win, and drag with the right mouse button.

Kudos to Metacity (and most other X WMs) for shipping with something similar by default.

3. Arrange windows using the keyboard.

It took me the longest time to realize that this was something that would actually be useful.

Most people only have a couple of common ways they like to arrange windows on the screen— for example, one window filling the entire screen, or two half-width windows side by side. What this means is that in the common case, you are really not asking for windows to be placed at specific pixel positions on your screen. Hence, the mouse isn't actually needed.

I use a couple of different window management "idioms," which are probably best illustrated by example:

  • Win+s i j k RET, for example expands the current window until it meets the nearest edge to the north, west, and south. Before and after:

    Win+s activates window-expanding and i, j, k, and l indicate north, west, south, and east, respectively.

  • Win+s Win+s means "expand the current window as much as possible without covering anything else," which seems like a common thing to want. Before and after:

    Openbox's smart window placement policy attempts to place a second window within the space left by the first one. So a quick Win+s Win+s makes a second window neatly fill up all the space left unused by the first one!

    For more complex arrangements, it's very fast to drag a window to an approximate location (see alt-dragging, above), then use Win+s Win+s to make it neatly fill the available space.

  • Win+w is analagous to Win+s except that it moves a window to the nearest edge to the north/west/south/east instead of resizing it.

An alternative strategy is to bind keys to move windows to a small number of fixed "slots," such as the two halves of a screen. Jeff Atwood mentions this strategy and its implementation in WinSplit Revolution.

4. Configure single-key shortcuts for launching common apps. For example, Win+b for a web browser. Not always a window-manager responsibility, but it can really streamline one's work.

Conclusion

Although I am still experimenting with some aspects of this setup, I am fairly happy with it. For better or for worse it does feel a lot like a "normal" window manager, just one that is highly streamlined (and doesn't give me hypertension). I'd be curious to see what additional improvements can be gained by giving up some of the associated flexibility and moving to a more restrictive model (e.g. something like a tiling WM).

For the curious, I've posted Openbox configuration recipes for tips 2 and 3 here.

AUCTeX and preview-latex

AUCTeX is a TeX mode for Emacs that adds various conveniences for editing LaTeX (and other macro packages). One of its killer features is inline previews of math and graphics, using preview-latex:

With AUCTeX and preview-latex, you can properly edit your document and equations in their high-level (TeX source) representation, while getting near-instant confirmation that your equations will turn out the way you want. It's pretty much the best of both worlds.

AUCTeX is easy to set up on Ubuntu, although it takes slightly more work than the usual apt-get invocation. Here is a HOWTO for my future reference (and yours). This has been tested on Ubuntu 9.04 with a post-v23-release emacs-snapshot:

Install AUCTeX.

$ sudo aptitude install auctex

Add the following init code to your .emacs file.

(load "auctex.el" nil t t)
(load "preview-latex.el" nil t t)

Open a LaTeX file in Emacs and press C-c C-p C-b (M-x preview-buffer) to generate math/graphics previews for the entire buffer. After you update an equation, press C-c C-p C-p (M-x preview-at-point) to refresh the preview.

Further reading: AUCTeX, AUCTeX manual

Making your own page-a-day calendar

Anomalously, today's post is about a DIY physical artifact.

A while back, I made a custom page-a-day calendar as a gift for my girlfriend. Each page tears off and has a picture on it. (Unfortunately, I don't have any photos of the finished product.)

With just a little effort, you can make one of these things and have it look quite professional. You can fill the pages with whatever photos, comics, etc. you want. And, I can virtually guarantee you, your recipient has never had a page-a-day calendar typeset in Computer Modern.

Here are skeletal instructions for making your own. You'll need a printer, ink or toner, most of a ream of paper, some padding compound, a paper cutting device/facility, cardboard, LaTeX, and time.

1. Get source images

Acquire 365 images from Flickr, your photo collection, your favorite CC-licensed webcomic, or whatever strikes your fancy. This post on curl may come in handy. Some notes: (1) Layout is much easier if the images are the same aspect ratio. (2) Consider upsampling the images if needed, e.g. with imagemagick, so you can print at a respectable DPI. Henceforth I'll assume you've named the images imgs/001.jpg, imgs/002.jpg, etc. If this is not the case, simply adjust the code in Steps 2 and 3 accordingly.

2. Use this LaTeX skeleton

Make a new TeX file and fill it with this:

\documentclass[17pt,oneside,final,showtrims]{memoir}
\usepackage{marvosym}

\setstocksize{11in}{8.5in}

\settrims{0in}{0in}

\settrimmedsize{4in}{6in}{*}
\settypeblocksize{3.5in}{1.75in}{*}
\setlrmargins{0.25in}{*}{*}
\setulmargins{0.05in}{*}{*}
\setheadfoot{0.01in}{0.1in}
\setheaderspaces{*}{*}{*}
\setmarginnotes{0.25in}{3.5in}{0in}

\checkandfixthelayout

\pagestyle{empty}

\usepackage[final]{graphicx}

\pagestyle{empty}

\newcommand{\daypage}[6] {
  \marginpar{\includegraphics[height=3.4in]{imgs/#1.jpg}}
  \begin{center}
    \Large{#2} \\
    \HUGE{\textbf{#3}} \\
    \large{#4}

    \vspace{0.4in}
    \small{#5}

    \vspace{0.2in}
    \scriptsize{\textit{#6}}
  \end{center}
  \newpage
}

\begin{document}
  % Cover page
  \marginpar{\includegraphics[height=3.4in]{imgs/cover.png}}
  \newpage

  \include{tex-days}
\end{document}

Salient points:

  • The \daypage command generates a new page. You supply arguments specifying the parameters for each page: the filename of the image to include, the day and date, a line indicating whatever holiday it might be, etc. Play around with the layout, especially if you're using images of different aspect ratios than I did or if you have a calendar stand of a particular size.
  • If you want a cover page, supply a cover.png; otherwise, remove the corresponding lines from the template.

3. Generate the pages

The template above includes tex-days.tex, which might look something like this:

[...]
  \daypage{182}{Sunday}{03}{Jul 2005}{~}{~}
  \daypage{183}{Monday}{04}{Jul 2005}{Independence Day}{~}
  \daypage{184}{Tuesday}{05}{Jul 2005}{~}{~}
  \daypage{185}{Wednesday}{06}{Jul 2005}{~}{~}
  \daypage{186}{Thursday}{07}{Jul 2005}{~}{~}
[...]

You can generate a skeletal version of this, sans holidays, with a quick Python program. I've provided a sample tex-days.tex file for the year 2010.

The first argument on each line indicates the filename, e.g. 182 indicates that 182.jpg should be included. Make sure these match the filenames you are using. The sample file assumes your images are named 1.jpg, 2.jpg, etc. If this is not the case, either create your own version or rename your files.

If you're interested in embellishing the output, the 5th and 6th arguments on each line provide supplementary text to go on each page (#6 is printed in smaller type than #5). You can fill in, by hand or programmatically, whatever notations you want here, e.g., holidays, birthdays, anniversaries, or a countdown to whatever.

Arguments 2, 3, and 4 give the day of week, date, and month/year respectively that are displayed, in case that wasn't clear.

4. Produce and print

Run the file through pdflatex and print it! Make sure the alignment is consistent across pages.

The showtrims argument in the template file makes LaTeX print trim marks on each page. However, you really only need trim marks on the first page. If you're obsessive-compulsive, you could print the first page with trim marks and the rest without to guarantee the marks won't show on the finished product.

5. Trim it

I took the stack of paper, with pieces of thin cardboard above and below it, to my local Kinko's (now Fedex Kinko's, I guess). I asked them to cut the stack along the trim marks (2 cuts, since 2 of the edges already run up again against the page edges). They did this for a fee of just $1/cut.

6. Bind it

Get some padding compound, e.g. Sparco padding compound. (I bought a quart, so I can probably make gift calendars/notepads for years.) Align the cut pages, leaving one piece of cardboard on the bottom, and put the stack in a vise. (In a jam, "under a pile of hardcover books" will do.) Using a paintbrush, paint the top edge of the stack with padding compound. Wait for it to dry. Paint another coat.

If you have random loose paper, padding compound is also handy for recycling it into notepads.

7. Mount it

This is not really needed, but is a nice touch. Find an old stand for a page-a-day calendar. Glue the cardboard backing in.

I hereby place the LaTeX template and LaTeX snippets in this post into the public domain.

Extracting select pages from a PDF

Ever needed to make a new PDF containing a subset of the pages of an existing PDF?

The pdfnup tool is nominally for printing PDFs n-up (n logical pages to a sheet), but it can be told to cut and paste pages into a new PDF, like so:

pdfnup original.pdf --nup 1x1 --pages 1,3,5,7,21-25 --outfile subset.pdf

On Ubuntu, you can install pdfnup with a simple sudo aptitude install pdfjam.

Further reading: pdfjam

Update: pdftk also seems like an intriguing option, especially for more complex operations.

Configuring Radeon R600/R700 devices on Ubuntu Jaunty

Update, 18 Mar 2009: Ubuntu Jaunty Jackalope 9.04 and later now support R600/R700 hardware out of the box. Make sure you update to at least xserver-xorg-video-radeon 1:6.12.1-0ubuntu1, libdrm2 2.4.5-0ubuntu2, and linux-image-generic 2.6.28.11.11. If you have done so, you can stop reading this page now.

Update, 5 Mar 2009: fixed the instructions for the DRM modules. You need to build from the origin/r6xx-r7xx-support branch.

I got a new computer with an ATI Radeon 3450 graphics card (R600 series). Here are the steps I took to configure it on Ubuntu Jaunty Jackalope 9.04 using free software (open source) drivers. The instructions below might also be useful for other Radeon hardware, in particular, R700-series cards.

Happily, Ubuntu 9.04 does drive the 3450 out-of-the-box (it boots into X and is usable, using the free radeon driver). Its performance was, however, kind of poor on my machine. I noticed "wiping" and flickering when scrolling (e.g. in firefox or gnome-terminal) or moving windows around.

Building the development versions of the radeonhd and drm drivers fixed these issues and significantly improved desktop performance. Everything is quite slick-looking now, even while driving a 1900x1080 display. Kudos to the driver developers, and to AMD/ATI for cooperating with driver development. Ubuntu does package radeonhd, so sooner or later, after this code gets released, an out-of-the-box installation will provide this level of performance as well.

For historical reasons there are two free drivers for ATI Radeon devices: radeon and radeonhd. They have approximate feature-parity these days. I used radeonhd because I found instructions for it first. :) Instructions are adapted from these sources: radeonhd r600-r700 instructions, Ubuntu community documentation for RadeonHD. (For your reference, the Radeon feature matrix describes what features are supported on what models.)

Download the build prerequisites:

$ sudo aptitude install git-core build-essential
$ sudo aptitude build-dep xserver-xorg-video-radeonhd

Clone the repos for radeonhd and drm:

$ git clone git://anongit.freedesktop.org/git/xorg/driver/xf86-video-radeonhd
$ git clone git://anongit.freedesktop.org/mesa/drm

Build and install radeonhd:

$ cd xf86-video-radeonhd
$ ./autogen.sh --prefix=/usr
$ make
$ sudo make install
$ cd ..

Build and install the drm modules:

$ cd drm/linux-core
$ git checkout origin/r6xx-r7xx-support
$ make radeon.o drm.o
$ sudo cp radeon.ko /lib/modules/`uname -r`/kernel/drivers/gpu/drm/radeon/
$ sudo cp drm.ko /lib/modules/`uname -r`/kernel/drivers/gpu/drm/

Now we'll configure X to use the new drivers. If you don't have an xorg.conf file with stuff in it, auto-generate one:

# X -configure
# mv /root/xorg.conf.new /etc/X11/xorg.conf

Change the Device section. I modified the Driver line and added the following options:

Section "Device"
    Driver "radeonhd"
    Option "AccelMethod" "exa"
    Option "DRI" "on"
    Option "VideoOverlay" "off"
    Option "OpenGLOverlay" "on"
    Option "TexturedVideo" "off"
    [...]
EndSection

Verify that the DRI module is loaded and that non-root applications can access it:

Section "Module"
    Load  "dri"
    [...]
EndSection

Section "DRI"
    Mode 0666
EndSection

Then reboot your computer. That should do it.

The Ubuntu netboot/network installers

The Ubuntu netboot (network) installers are fantastic, and for how good they are, they are hardly ever publicized.

The idea of just downloading your OS whenever and wherever you need to install it is kind of astounding. It makes the Windows/Mac experience of having to dig up some DVD and type in a 25-digit number seem positively archaic. It's the difference between walking across the street to buy bottled water, and turning on the tap and just having water appear. It completely changes the economics of the game when you no longer have to think of your operating system as a scarce resource.

As I understand it, there are three ways to do a netboot install. I've previously written about installing Ubuntu by booting from files downloaded to your hard disk. It's also easy to do a netboot install from either a CD or a USB key, and the procedures are very similar.

All three methods are nice because they have small initial downloads, about 10 MB; they then download the rest of your OS (and only the latest version of each package) at install time. It's a waste of time to download a ~700MB CD image if you're going to upgrade half of your packages right after installation. (Software is usually out of date by the time you install it! Especially during the development period for each release.) The installer is small enough that you can even burn it to one of those business card CDs.

The netboot installers are also versatile. They will install, at your request, any (or all!) of the following: Ubuntu desktop, Ubuntu server, Kubuntu desktop, Xubuntu desktop, Edubuntu desktop, and many more. They also support installing to encrypted LVM for full-disk encryption. (The netboot installers are based on the ncurses Debian installer.)

The hard disk method has the advantage that, of course, you don't need to use any external media. However, in my experience, the CD and USB key installers are a little less flaky. Unlike a hard disk installation, they also work even if you don't have Grub already installed.

For either the CD or USB key methods, you can find the appropriate files here:

http://archive.ubuntu.com/ubuntu/dists/jaunty/main/installer-i386/current/images/netboot/

(This is for Jaunty/i386. If you want a different release or have a different architecture, like amd64, adjust the URL accordingly.)

For CD installation: download mini.iso and burn it to your CD.

For USB media installation: download boot.img.gz and follow the instructions here. It will boil down to doing something like this:

# zcat boot.img.gz > /dev/sdX1
# aptitude install mbr
# install-mbr /dev/sdX

Then boot from your new media into the installer.

Ubuntu Jaunty Jackalope

(I am catching up on all the things that I've been meaning to blog about in the past few months.)

There are good reasons already to upgrade to the Jaunty Jackalope (development release). Either for good, or just to install the following two packages:

(1) git 1.6.x (release announcement).

(2) Firefox 3.1 beta. I have to say, I wasn't sold on Firefox 3.0. But Firefox 3.1 has convinced me to switch back from Epiphany. First, it is blazing fast. Second, in continual usage for several weeks now, it seems to be pretty crash-proof. Third, it actually has a bookmarks system that I would use. When Google can get you what you want 0.5 seconds after you type it in, you really have to rethink the idea of poking around through menus to find your favorite sites. Anyway, my gratitude goes to everyone involved.

Epiphany had also been starting to get on my nerves lately. It seems to crash at least once every other day. The address bar is really laggy sometimes (if you've ever used SSH over a high-latency connection, you know how irritating this is). And it is not as fast as Firefox 3.1, at least yet.

Jaunty is interesting for another reason. In this release, Ubuntu is attempting to make bzr repositories available for the packaging+source of every single package. I am looking forward to seeing what people will do with this. If it could make it easier for casual developers to get the source for a package, poke around to fix a bug, isolate their patches and send them to Ubuntu (or upstream), it could be a huge force multiplier.

Stupid screen tricks

I use GNU Screen extensively to manage my work. Here are a few Screen tips and shortcuts I've collected or developed.

1. Add a status bar to screen. If you use screen inside screen, or screen on more than one machine, having a status bar is essential so you always know where you are. I have the following line in my .screenrc:

caption always "%{= kw}%-w%{= BW}%n %t%{-}%+w %-= @%H - %LD %d %LM - %c"

Now whenever I'm within screen, the bottom line looks like this, showing the names of all windows (and highlighting the active one), the hostname, and the date and time:

0 emacs  1 bash  2 farm5        @ulysses - Sunday 26 October - 16:06

2. Change the screen command key. For Emacs users, the default screen command key, C-a, is unacceptable. The following line in .screenrc changes it to C-o, which is still bound in Emacs, but less objectionable:

escape ^Oo

3. Shortcut resuming screen. Whenever I SSH to my desktop machine the first thing I do— always— is resume my previous screen session. The following command runs ssh and resumes screen in one fell swoop:

ssh -t ulysses "screen -d -r"

I stick that in a script called homebase and bind a hotkey, [Windows]-h, to

gnome-terminal -e ~/scripts/homebase

in Openbox so I'm only ever one key away.

4. Use nested screens. My desktop machine has good connectivity, so from my screen there, I connect to a bunch of remote machines which I use for my work, and of course, I use screen on those hosts too.

To send a C-o to a screen instance which is itself inside a screen window, you need to escape it and type C-o o. So, for example, if C-o n is usually used to go to the next window, you'll need to use C-o o n to go to the next window in a nested instance. This setup is hard to beat for managing connections to multiple computers. It sounds complicated, but your muscle memory will take care of it soon.

5. Generate new windows automatically. Whenever I SSH into another host from inside screen, I usually want a dedicated window for that connection. The following snippet in my .bashrc makes it so that when I'm inside screen and type ssh SOME-HOST, it creates a new window and titles it SOME-HOST, then invokes ssh in the new window.

# Opens SSH on a new screen window with the appropriate name.
screen_ssh() {
    numargs=$#
    screen -t ${!numargs} ssh $@
}
if [ $TERM == "screen" -o $TERM == "screen.linux" ]; then
    alias ssh=screen_ssh
fi

(Caveat: this doesn't quite work when you specify a host and a command on the same line, e.g. ssh some-host "ls ~".)

6. Managing DISPLAYs. The disconnect-and-resume-anytime way of working can sometimes be a curse. Shells inside screen windows don't get environment variables like $DISPLAY updated whenever you resume a session. So if you carelessly launch an X program from inside one, it may end up on a display which is either long gone or not the one you intended to use. The following simple trick automagically sets DISPLAY to the display at the last place you resumed a screen session (i.e. probably where you are sitting right now).

First, write the value of $DISPLAY to a file whenever you resume screen. One way to do this is by using a shell alias like the following whenever you resume, instead of using screen -d -r directly:

alias sc='echo $DISPLAY>~/.last-display; screen -d -r'

Alternatively, the invocation from #3, above, might now look like this:

ssh -X -t ulysses "echo \$DISPLAY>~/.last-display; screen -d -r"

Now, this shell alias here sets the display appropriately, so that, for example, here xterm runs xterm on your "current" display:

alias here='DISPLAY=`cat ~/.last-display`'