Preferences for the Drive Mount Applet

In my previous article, I outlined the thought process behind the redesign of the drive mount applet. Although it ended up without any preferences, I don’t necessarily think that it doesn’t need any preferences.

A number of people commented on the last entry requesting a particular preference: the ability to hide certain drives in the drive list. Some of the options include:

  1. Let the user select which individual drives to display
  2. Let the user select which classes of drive to display (floppy, cdrom, camera, music player, etc).
  3. Select whether to display drives only when they are mounted, or only when they are mountable (this applies to drives which contain removable media).

Of these choices, the first is probably the simplest to understand, so might be the best choice. It could be represented in the UI as a list of the available drives with a checkbox next to each. In order to not hide new drives by default, it would probably be best to maintain a list of drives to hide rather than drives to show.

It does bring up the question of how to identify what a “drive” is. On my Ubuntu system, the first USB mass storage device I plug in usually gets the same mount point. If we identify drives by their mount point, hiding that mount point will effectively hide all of those drives. Perhaps the HAL UDI would be appropriate here.

The third choice is also interesting: why display an icon for a removable media drive if there is no media in the drive? This sort of feature could probably be implemented independently of the previously discussed choice. It is also the sort of change that probably needs to be addressed in gnome-vfs and HAL though. Fixing it at that level would also provide the same benefit to other GnomeVFSVolumeMonitor using apps, such as Nautilus.

Features vs. Preferences

As most people know, there has been some flamewars accusing Gnome developers of removing options for the benefit of “idiot users”. I’ve definitely been responsible for removing preferences from some parts of the desktop in the past. Probably the most dramatic is the drive mount applet, which started off with a preferences dialog with the following options:

  • Mount point: which mount point should the icon watch the state of?
  • Update interval: at what frequency should the mount point be polled to check its status?
  • Icon: what icon should be used to represent this mount point. A selection of various drive type icons were provided for things like CDs, Floppys, Zip disks, etc.
  • Mounted Icon and Unmounted Icon: if “custom” was selected for the above, let the user pick custom image files to display the two states.
  • Eject disk when unmounted: whether to attempt to eject the disk when the unmount command is issued.
  • Use automount-friendly status test: whether to use a status check that wouldn’t cause an automounter to mount the volume in question.

These options (and the applet in general) survived pretty much intact from the Gnome 1.x days. However the rest of Gnome (and the way people use computers in general) had moved forward since then, so it seemed sensible to rethink the preferences provided by the applet:

  1. Nautilus’s volume handling has matured a lot since then, and been pushed down to the platform as the GnomeVFSVolumeMonitor API. This API makes it possible to enumerate mounted volumes and mount points on the system, so we can do a lot better than providing an entry box and file chooser to select a mount point.
  2. The GnomeVFSVolumeMonitor provides asynchronous notification of mount/unmount events, removing the need for the applet to poll the status. If the applet isn’t polling, then there is no reason for it to provide the update interval preference.
  3. The GnomeVFSVolumeMonitor API provides icon names for volumes depending on the drive type. If we can detect that a disk is a floppy or a cdrom or whatever, why ask them what sort of icon to use? This change also means that the icon can be picked from the user’s selected icon theme, providing better integration with the rest of the desktop (not to mention the accessibility benefits when the HighContrast icon theme is used).
  4. Certain types of volumes always make sense to eject on unmount. Other volumes don’t. Since we know the volume type, we should be able to just do the right thing.
  5. Since the applet is no longer directly checking the mount point status, the “Use automout-friendly status test” preference doesn’t make sense. But even if it was applicable, it is the sort of preference that only has one sane value: assuming both types of status check work, why wouldn’t you want to use the one that works with automounters?

The other major change I made was due to a change in the types of volumes people mount: USB devices. If you have a fixed number of mount points/devices you care about, then the old model works pretty well. If you have a large number of devices, and rarely plug them all in at once, you probably don’t want to create drive mount applets for all of them. My solution was to alter the drive mount applet to display a button for each user mountable volume on the system rather than one applet per mount point.

The result was an applet with no preferences. However, I’d contend that it has more features than before. It has been improved further since then, to provide media-type specific options (e.g. start the movie player if you insert a DVD Video disc).

Re: Pixmap Memory Usage

Glynn: I suspect that the Pixmap memory usage has something to do with image rendering rather than applets in particular doing something stupid. Notice that most other GTK programs seem to be using similar amounts of pixmap memory.

To help test this hypothesis, I used the following Python program:

import gobject, gtk
win = gtk.Window()
win.set_title('Test')
win.connect('destroy', lambda w: gtk.main_quit())
def add_image():
    img = gtk.image_new_from_stock(gtk.STOCK_CLOSE,
                                   gtk.ICON_SIZE_BUTTON)
    win.add(img)
    img.show()
gobject.timeout_add(30000, add_image)
win.show()
gtk.main()

According to xrestop, this program has low pixmap memory usage when it starts, but jumps up to similar levels to the other apps after 30 seconds.

Update: of course, this issue has already been debugged by markmc. It is the SHM segment allocated for drawing pixbufs.

Switch users from XScreenSaver

Joao: you can configure XScreenSaver to show a “Switch User” button in it’s password dialog (which calls gdmflexiserver when run). This lets you start a new X session after the screen has locked. This feature is turned on in Ubuntu if you want to try it out.

Of course, this is not a full solution, since it doesn’t help you switch to an existing session (you’d need to guess the correct Ctrl+Alt+Fn combo). There is code in gnome-screensaver to support this though, giving you a list of sessions you can switch to.

DSB-R100 USB Radio Tuner

Picked up a DSB-R100 USB Radio tuner off EBay recently. I did this partly because I have better speakers on my computer than on the radio in that room, and partly because I wanted to play around with timed recordings.

Setting it up was trivial — the dsbr100 driver got loaded automatically, and a program to tune the radio (gnomeradio) was available in the Ubuntu universe repository. I did need to change the radio device from /dev/radio to /dev/radio0 though.

One of the issues with the gnomeradio is the UI for tuning the radio. The following controls in the main window are used for this purpose:

  1. The slider on the left hand side of the window.
  2. The rewind and fast forward buttons (which are actually scan forward and backward).
  3. The track backward and forward buttons (which actually move back or forward by 0.05MHz).
  4. The presets option menu (which is initially empty).

What you can’t do from the main window is type in a frequency with the keyboard. You can type in frequencies directly when entering presets though, which is nice. These controls could probably be reduced to just an entry field for the frequency (possibly a spin button), and the presets option menu. The scanning feature seems most useful in setting up the presets: create a preset for each radio station that can be tuned and be done with it.

There are a few other small complaints:

  • The button for turning the radio on or off (the button with a speaker on it) doesn’t change appearance like most other mute controls.
  • The recording feature doesn’t use GStreamer. It’d be nice if it offered the same audio profiles for recording as Sound Juicer and other apps.
  • The input selection and volume control should probably also use GStreamer, so that they can work with the ALSA mixer.

I haven’t yet looked into software for doing timed recordings. Other people have though, so I could probably use those scripts as a base.

Playing with Google Maps API

I finally got round to playing with the Google Maps API, and the results can be seen here. I took data from the GnomeWorldWide wiki page and merged in some information from the Planet Gnome FOAF file (which now includes the nicknames and hackergotchis).

The code is available here (a BZR branch, but you can easily download the latest versions of the files directly). The code works roughly as follows:

  • Convert the locations info GnomeWorldWide page into an XML file, adding information from the Planet Gnome FOAF file using the makexml.py script.
  • When the main page loads, it requests the XML file previously generated. For each person element in the XML file, a marker is created on the map.
  • When a marker is clicked, an info window is displayed, which is the result of applying an XSLT transformation to the XML node.

HTTP resource watcher

I’ve got most of the features of my HTTP resource watching code I was working on for GWeather done. The main benefits over the existing gnome-vfs based code are:

  • Simpler API. Just connect to the updated signal on the resource object, and you get notified when the resource changes.
  • Supports gzip and deflate content encodings, to reduce bandwidth usage.
  • Keeps track of Last-Modified date and Etag value for the resource so that it can do conditional GETs of the resource for simple client side caching.
  • Supports the Expires header. If the update interval is set at 30 minutes but the web server says that the it won’t be updated for an hour, then use the longer timeout til the next check.
  • If a permanent redirect is received, then the new URI is used for future checks.
  • If a 410 Gone response is received, then future checks are not queued (they can be restarted with a refresh() call).

I’ve also got some code to watch the HTTP proxy settings in GConf, but that seems to trigger a hang in libsoup (bug 309867).

While I wrote the code for use in GWeather, it could be quite useful for other tasks that require watching an HTTP resource such as:

  • HTTP calendar backend of evolution-data-server.
  • A stock ticker applet like gtik.
  • Possibly an RSS reader.

The code is available in my Bazaar archive:

baz get http://www.gnome.org/~jamesh/arch/james@jamesh.id.au/http-resource--devel--0

Bryan’s Bazaar Tutorial

Bryan: there are a number of steps you can skip in your little tutorial:

  1. You don’t need to set my-default-archive. If you often work with multiple archives, you can treat working copies for all archives pretty much the same. If you are currently inside a working copy, any branch names you use will be relative to your current one, so you can still use short branch names in almost all cases (this is similar to the reason I don’t set $CVSROOT when working with CVS).
  2. If you have a directory which contains only the files you want to import into your Bazaar archive, the following command will add them all, and convert the directory into a Bazaar working copy:
    cd background-channels
    baz import -a bclark@redhat.com--gnomearchive/background-channels--dev--0.1

    No need for init-tree, add or commit.

  3. Running archive-mirror in your working copy will mirror that archive, so doesn’t need my-default-archive set.
  4. Other people probably don’t want to set your archive as their default. Also, they can ommit the register-archive call entirely:
    baz get http://gnome.org/~clarkbw/arch/background-channels--dev--0.1

    This checks out the branch, and registers the archive as a side effect.

  5. If you want to find out what is inside an archive, the following command is quite convenient:
    baz abrowse http://gnome.org/~clarkbw/arch

Some things you might want to do:

  1. If you have a PGP key, create a signed archive. This will cryptographically sign all revisions. When people checkout your branches, the signatures get checked automatically (this is useful if the server hosting your mirror gets broken into and you need to verify that nothing has been tampered with). If you have already created the archive, you can turn on signing with baz change-archive (remember to update the mirror archive too).
  2. If you turn on signing, consider using a PGP agent like gnome-gpg. You can configure it in ~/.arch-params/archives/defaults.
  3. It is customary to name the archive directory the same as the archive name. This has the benefit that the branch name matches the last portion of the URL.
  4. If you haven’t set up a revision library, you should do so:
    mkdir ~/.arch-revlib
    baz my-revision-library ~/.arch-revlib
    baz library-config --greedy --sparse ~/.arch-revlib

HTTP code in GWeather

One of the things that pisses me off about gweather is that it occasionally hangs and stops updating. It is a bit easier to tell when this has occurred these days, since it is quite obvious something’s wrong if gweather thinks it is night time when it clearly isn’t.

The current code uses gnome-vfs, which isn’t the best choice for this sort of thing. The code is the usual mess you get when turning an algoithm inside out to work through callbacks in C:

  1. One function opens the URL with gnome_vfs_async_open().
  2. The callback that gets triggered on completion of the open calls gnome_vfs_async_read().
  3. The callback that gets triggered on the end of the read checks the status. If it is at the end of the stream, then process the data and close the stream. Otherwise, perform another read (which will loop back to this step).

This logic is repeated 5 times for the different weather data sources. To clean this up, I started looking at libsoup which doesn’t try to be a full file system abstraction, but provides a better API for the kind of things gweather does.

I put together a simple HttpResource class that wraps the relevant parts of libsoup for apps like gweather. It can be used like so:

  1. Create an HttpResource instance for the given URI.
  2. Connect a handler to the resource’s updated signal.
  3. Call the _set_update_interval() method to say how often the resource should be checked.
  4. Call the _refresh() method to kick off periodic freshness checks.
  5. When new data arrives, the updated signal is emitted.

Since the code is designed for periodic updates, I added some simple caching behaviour. If the server reports that the resource hasn’t been modified, we don’t need to emit the updated signal.

There are a few things that still need doing:

  • Some code to keep a SoupSession instance up to date with the proxy configuration settings in GConf.
  • Correct handling of the Expires: response header. If we are checking for updates every 30 minutes, but the server says the current weather report is current for the next hour, then we shouldn’t check again til then.
  • Support gzip and/or deflate content transfer encoding to reduce bandwidth.

This code should be pretty trivial to integrate into gweather when it is done, and should simplify the logic. I guess it would be useful for other applets too, such as gtik. The current code is available in my Bazaar archive:

baz get http://www.gnome.org/~jamesh/arch/james@jamesh.id.au/http-resource--devel--0

pkg-config patches

I uploaded a few patches to the pkg-config bugzilla recently, which will hopefully make their way into the next release.

The first is related to bug 3097, which has to do with the broken dependent library elimination code added to 0.17.

The patch adds a Requires.private field to .pc files that contains a list of required packages like Requires currently does, which has the following properties:

  • When verifying that a particular package name is available with “pkg-config --exists“, dependencies in both Requires and Requires.private are checked.
  • When running “pkg-config --cflags“, flags from dependencies in Requires are included.
  • When running “pkg-config --libs“, flags from dependencies in Requires are included.
  • When running “pkg-config --static --libs“, flags from dependencies in both Requires and Requires.private are included.

The purpose of this is to list dependencies that are not exposed in the API of the library in question while not making users of the library link directly to those dependencies. This means that private dependencies can be upgraded to new incompatible versions without breaking applications that only depend on them indirectly.

This is intended for cases like Cairo, which links to libpng, but doesn’t expose any of the libpng API itself. It is not intended for dependencies like gtk+ depending on pango. Of course, this header will cause the .pc file to be incompatible with pkg-config versions prior to 0.16, because those versions don’t tolerate unknown fields.

The other changes are related to the associated autoconf macros:

  • Add a PKG_CHECK_EXISTS() macro. This would be similar to PKG_CHECK_MODULES(), except that no variables would be set or substitutes — it would simply run the ACTION-IF-FOUND or ACTION-IF-NOT-FOUND arguments. It is basically a less heavy weight macro for cases where you just want to see if a set of modules is available (bug 3530).
  • Get rid of the caching behaviour in PKG_CHECK_MODULES(). Since 0.16, this macro has cached the result of the check based on the variable prefix passed as the first argument. Since pkg-config is quite fast and configure doesn’t store its cache between runs by default, this doesn’t result in any noticable speed improvement and causes build problems for configure scripts that call PKG_CHECK_MODULES multiple times with the same variable name prefix but different package lists (e.g. Eye of Gnome). It seems simplest to just remove the caching, resulting in a simpler and more reliable macro (bug 3550, patch not yet uploaded).

With these changes, hopefully 0.18 will fix up the last few small incompatibilities in the recent releases.