2005-12-18

Why Terminator doesn't support $WINDOWID

While getting X11 out of 800x600 (why in 2005 does Xorg still not manage to auto-detect the best resolution for the graphics hardware and connected display when Mac OS and even MS Windows have been doing so without problem for years?) and installing Terminator on someone's new machine the other day, I noticed that vim(1) behaved differently running in an XTerm window than it does in a Terminator window.

To cut a long trawl through the vim source short, It turns out that it looks to see if $WINDOWID gives it a window it can get/set the title of using Xlib, and enables its "title" setting if so. It then uses either the X11 mechanism or escape sequences to set the window's title. If you think I'm being vague, take a look at the code. It's a great example of over-complicated chronic-featuritis nested-#ifdef-hell multi-platform-the-bad-way old-style C. (If you're going to use #ifdef, it shouldn't be within the scope of a function.)

We can tease the Xlib Window out of Sun's AWT implementation like this:

private static long getX11WindowIdForFrame(Frame frame) {
long windowId = 0;
try {
Field peerField = Component.class.getDeclaredField("peer");
peerField.setAccessible(true);
Class xWindowPeerClass = Class.forName("sun.awt.X11.XWindowPeer");
Method getWindowMethod = xWindowPeerClass.getMethod("getWindow", new Class[0]);
Object getWindowResult = getWindowMethod.invoke(peerField.get(frame), new Object[0]);
windowId = Long.class.cast(getWindowResult).longValue();
} catch (Exception ex) {
ex.printStackTrace();
}
return windowId;
}

But it turns out to be quite difficult to make good use of this in Terminator.

For one thing, it's quite disruptive to the rest of the code, getting a Frame at the right point and passing it through. We already have two-stage initialization in various places in Terminator, and this would require a few more, or a lot more parameter-passing, because you don't add a component to a hierarchy until the component's constructed.

More importantly, setting $WINDOWID would interact badly with the planned ability to turn tabs into windows (and maybe vice versa). We'd have no way of changing an existing process tree's $WINDOWID, so any code making use of the $WINDOWID would end up fiddling with the window it started in, rather than the window it was currently in.

Maybe I've not looked hard enough, but I've not seen anything else rely on $WINDOWID, and there's no reason to do so for setting window titles from tty-based programs because you can just send the relevant escape sequences (the "tsl" and "fsl" capabilities). You can even query the terminal capabilities if you want to know if such a thing is supported (the "hs" capability). Basically, Vim is doing the wrong thing, and it's source code is a frightening example of what you end up with if you decide to work around every bit of brain-damage in the universe. (If there's one area of Unix that sports a lot of ugly lesions, it's terminals.)

Any Terminator user who wants Vim to update their title can add "set title" to their "~/.vimrc" and have it do the right thing anyway.

So given a choice between support for a poor design decision on Vim's part or the ability to add a fairly cool feature, I'll go with the freedom, and throw away my cool hack to get the X11 window id. Where by "throw away" I mean "give to Google via my blog".