Panflute 0.5.0 released

Panflute 0.5.0, the successor to Music Applet, has been released. The source can be downloaded from Launchpad, and I’ve also set up a PPA for Panflute to make installation even easier (at least for users of Ubuntu and other Debian-based distributions).

Panflute 0.5.0 is essentially feature-compatible with Music Applet 2.5.1. There are only two notable differences in terms of functionality:

  • The applet now supports two-row layouts for fat panels, and offers greater flexibility in how widgets in the applet are arranged.
  • The time display can be switched to show remaining time instead of elapsed time.

Under the hood, of course, it’s a complete re-write of the code, splitting the music player abstraction layer off into a separate process, which can be used by other programs without needing to install the applet. This post explains the rationale for this fundamental change in more detail, but from the casual user it should (hopefully!) be invisible.

Unfortunately, Panflute doesn’t currently have any translations into other languages. I plan to use Launchpad to manage translations, but haven’t yet had a chance to set it up.

If you find any bugs in Panflute, please report them via Panflute’s bug tracker, also hosted on Launchpad.

And you thought your network was slow

RFC 1149, “A Standard for the Transmission of IP Datagrams on Avian Carriers”, describes how to use carrier pigeons to send Internet traffic between two sites. It was originally published as a joke (note the date on the document), and although there has been a proof-of-concept implementation of RFC 1149, namely using carrier pigeons to ping another computer, it understandably hasn’t seen widespread use.

Unless, perhaps, you live in South Africa, in which case it might be a more viable option than the conventional Internet service providers available:

Local news agency SAPA reported the 11-month-old pigeon, Winston, took one hour and eight minutes to fly the 80 km (50 miles) from Unlimited IT’s offices near Pietermaritzburg to the coastal city of Durban with a data card was strapped to his leg.

Including downloading, the transfer took two hours, six minutes and 57 seconds — the time it took for only four percent of the data to be transferred using a Telkom line.

[Pigeon transfers data faster than South Africa’s Telkom, Reuters, 10 September 2009]

Of course, this isn’t too surprising. After all, a snail pulling a cart with two DVDs for wheels has a faster average data transfer rate than an ADSL connection.

Granted, pigeon- and snail-based layer 2 technologies suffer from pretty sluggish latency, so they’re often not the best option for running your network. You’re generally going to be off using a cat or 5 instead.

[Hat tip to The Risks Digest, volume 25, issue 78, for the South Africa story.]

Comments Off

Never give an applet a tooltip

The following code creates a GNOME panel applet with three things in it: some text, a button, and some more text. The button has a tooltip, and everything else has a different tooltip. However, there is a bug here. Can you find it?

#! /usr/bin/env python
 
import gnomeapplet
import gtk
 
def applet_factory (applet, iid):
    applet.set_applet_flags (gnomeapplet.EXPAND_MINOR)
    box = gtk.HBox ()
    label1 = gtk.Label ("blah blah blah")
    button = gtk.Button ("Do something")
    label2 = gtk.Label ("Bob Loblaw")
 
    applet.set_tooltip_text ("Stuff")
    button.set_tooltip_text ("(doesn't actually do anything)")
 
    box.pack_start (label1)
    box.pack_start (button)
    box.pack_start (label2)
    applet.add (box)
    applet.show_all ()
 
    return True
 
if __name__ == "__main__":
    gnomeapplet.bonobo_factory ("OAFIID:name_of_some_applet_factory",
                                gnomeapplet.Applet.__gtype__,
                                "test",
                                "0.0.0",
                                applet_factory)

(Hint: re-read the title of this post.)

To make the applet easy to use, we consider Fitt’s law, which deals with how easy or difficult it is for the user to move the mouse pointer to a particular position on the screen. Specifically, the easiest position is obviously the current position, since no movement is required. Second easiest are the corners of the screen: the user just shoves the mouse in the right direction. Since the pointer stops at the edge of the screen, it’s impossible to overshoot. Third easiest are the edges of the screen, for a similar reason.

Since panels live along the edge of the screen, we want to make sure our button is positioned precisely at the edge of the screen, making it easier to click on. That’s why the above code sets the EXPAND_MINOR flag, telling the applet to fill all available room between the edge of the screen and the edge of the panel. Our expectation is that the button will then be stretched to fill this space.

However, it doesn’t! There’s a one-pixel border around the applet’s contents. If we push the mouse pointer to the edge of the screen, we wind up overshooting the button by one pixel. So close, yet so far.

(Fun fact: earlier versions of Windows suffered from a similar problem. The Start button was a pixel or two away from the corner of the screen, so clicking in the corner didn’t activate it. This was fixed in Windows XP, or possibly earlier — I don’t have ready access to a Windows 2000 machine to check.)

The cause of the problem is hard to see, and took me a good amount of debugging to find: adding a tooltip to an applet causes that one-pixel border to appear! Remove the tooltip, and the button gets those two extra pixels that let it fill the entire panel along the minor axis. One wouldn’t expect setting a tooltip to affect widget layout, but it does.

To get the effect we want, we set the tooltip on the box used to pack the widgets, instead of on the applet itself. The one-pixel-border effect only happens with the applet itself, for reasons I can’t explain. So we replace this:

    applet.set_tooltip_text ("Stuff")

With this:

    box.set_tooltip_text ("Stuff")

Now all is right with the world, where “world” is defined as “Panflute bug #412309“.

(Pedant’s corner: another bug in the code above is that middle- and right-clicks on the button don’t get propagated to the applet for handling there. Fixing that requires subclassing gtk.Button to override do_button_press_event and return False if event.button != 1. But that’s much easier to figure out. I omitted that detail from the example above for simplicity’s sake.)

Game changer

One of the recurring issues with Wallace, in those rare times when I’m actually working on it, is the difficulty of interfacing Wallace with the NES emulation engine. Emulators generally aren’t written with the goal of being driven by another program; as a result my most recent effort at it involved a lot of ugly hacks to Mednafen to make it spew some useful data to stdout, and to cleverly disguise controller inputs generated by Wallace as a movie file.

However, I discovered by accident while wasting time I could’ve been spending on Panflute instead of browsing the epic time sink that is TV Tropes that some modern console emulators have built-in scripting support, and in FCEUX‘s case, the scripting support is also part of the often-relatively-neglected Linux version. Said scripting support offers a relatively straightforward way to write add-on code that mucks around with the game running inside the emulator, which would be orders of magnitude more elegant and maintainable than the approaches Wallace has taken up until now.

What do I mean by “muck around”? Well, you could add a way to drag-and-drop enemies in Super Mario Bros.:

Or give Mega Man kill-anything laser eyes (maybe he defeated Mr. Flibble Man?) in Mega Man 2:

Or replace the control scheme in Super Mario Bros. 3 with the one in Kirby: Canvas Curse, drawing platforms and obstacles on the screen using the mouse instead of having direct control over Mario:

Or add head-to-head network play to Nintendo’s Tetris game. You know, the one that doesn’t actually have a multiplayer mode to begin with:

Note that all that craziness is being implemented using Lua scripts in the emulator, without doing any hacking of the ROMs themselves. In FCEUX, loading a Lua script gives it control of the emulation loop and do pretty much whatever it wants, including poking around in game memory and messing with controller inputs but also doing anything else that can be done in Lua.

For the time being I’m not working on porting Wallace to FCEUX+Lua, but that’s mostly because I need to get Panflute in a releasable state and resurrecting Wallace right now would be too great a distraction.