The laziness of a programmer, illustrated

September 4, 2010

At work, I have fallen into the bad habit of keeping a lot of iconified Firefox windows around, full of various things that I am going to read sometime (honest). As I've mentioned before, I have all of these iconified windows very carefully placed and organized so that I can find them again and keep track of them.

Naturally, this makes quitting and restarting Firefox kind of a pain. I have Firefox set to preserve all of the active windows and tabs over restarts, but it doesn't preserve the positions of the iconified windows (and it doesn't entirely preserve the regular window position either); any time I have to start Firefox again I have to re-position all of those icons. Generally this means that I don't; I never exit Firefox unless I'm forced to, because it's such a pain to get everything set up again.

(Which implies that I never log out, either; I just leave my screen locked.)

Recently I got tired of this (in the aftermath of my Fedora 13 upgrade, I've been restarting things more than usual). Thus I decided that clearly there had to be a way to fish around in the depths of X to find the current icon positions, so I could write a quick script that recorded them in a file and then shuffled the icons back into the right spots for me.

(This is less crazy than it sounds; I already have command line utilities to reposition windows, and X comes with a fair number of commands to poke at various aspects of window state.)

I'll cut to the chase: yes, except that it wasn't exactly a quick script. The most convenient way of doing this turned out to be writing an FVWM module in Perl that finds out all of this information and writes a file of FVWM commands that can be loaded back in to FVWM to (re)position and (re)iconify all of my Firefox windows just right. In the process of doing this I had to remember my Perl, look up a certain amount of Perl's OO support (my last serious Perl programming pretty much predates it), and figure out how to work with FVWM's underdocumented Perl bindings.

(FVWM has no current Python bindings for would be module authors.)

But all of this was less work than continuing to re-position all of my Firefox windows by hand. Honest.

(The resulting module is sort of theoretically general. If you are really interested, see here. As a bonus, you get to laugh at my hack-job Perl.)


Comments on this page:

From 134.95.198.212 at 2010-09-04 08:30:58:

That’s actually not terribly horrible. If I were a potential user of it, I wouldn’t particularly itch to fix it up. (I am eternally cursed with my good grasp of the language when I go shopping for irssi scripts. I always have to spend a long time pummelling them into shape before I can bear to use them.)

I think setup_id can be written like this:

sub setup_id {
	my ( $event ) = @_;
	my $wid = $event->_win_id;
	return $byid{$wid} ||= WinData->new(
		wid    => $wid,
		icon_x => 0,
		icon_y => 0,
	);
}

Also, I’d write fvwm_quote like this:

sub fvwm_quote { map { s(")(\")g; s(\$)($$)g; $_ } my @copy = @_ }

That way you can pass in any number of strings to quote, and get them all back quoted, in one fell swoop.

The first line of your event handlers is unfortunately not really generalisable as Perl 5 so far does not have formal subroutine parameters. And generalising only the second line is too much effort to go through for this particular script.

Particularly since the whole setup_id function isn’t really necessary.

If I’d written it, I’d have used a simple hash of hashes instead of the WinData class; that’s about it. One nice thing about Perl (which has its downsides too occasionally, though I find them far outweighed by its convenience) is called autovivification. What that means is that in expressions on the left side of some form of assignment, intermediate levels of data structure will automatically be created for you if they don’t already exist: you can write $hash{$foo}{$bar}{$baz} = $quux even if $hash{$foo} doesn’t already exist. A new hash will automatically be assigned to it on your behalf, as well as subsequently to $hash{$foo}{$bar}, so that you assignment to $hash{$foo}{$bar}{$baz} can succeed. Therefor, you can write this:

$module->add_handler(M_WINDOW_NAME, sub {
	my ($self, $event) = @_;
	my $wi = setup_id($event);
	$wi->name($event->_name);
});

as just this (assuming you had a hash %wininfo and not %byid):

$module->add_handler(M_WINDOW_NAME, sub {
	my ($self, $event) = @_;
	$wininfo{$event->_win_id}{name} = $event->_name;
});

There is no need to manually make sure that a hash exists in $wininfo{$event->_win_id}, as one will automatically be created if not.

Modulo lack of the WinInfo class and setup_id function, and hash access rather than method calls in the M_END_WINDOWLIST handler, the script would be essentially the same.

If I did feel like using objects for that purpose I’d probably have used Object::Tiny::Lvalue… no one uses Class::Struct these days. I’d also move setup_id into WinInfo as a class method summon_for_event or such and would write the repetitive handlers as instance methods, with the event handlers using the former to delegate to one of the latter. That way you could boil down the repetitive handler bits to just this:

my %handler = (
	pull_frame_xy => M_CONFIGURE_WINDOW,
	pull_name     => M_WINDOW_NAME,
	# ...
);

while (my ($method, $mask) = each %handler) {
	$module->add_handler($mask, sub {
		my ($self, $event) = @_;
		WinInfo->summon_for($event)->$method($event);
	});
}

However. that’s just over-engineering for your case.

Aristotle Pagaltzis

From 88.97.233.154 at 2010-09-06 08:08:21:

Wow, what a hammer. Have you considered using bookmarks? Either locally or in the cloud :)

By cks at 2010-09-06 23:27:15:

The problem with bookmarks is that I ignore them even more than I already ignore all of those iconified windows. There is effectively no chance that I will ever read anything that I have saved in a file or bookmarked as 'to read someday'.

(Possibly this is my real problem, of course.)

Written on 04 September 2010.
« Finally understanding the attraction of AJAX
A plan to deal with my feed reader problem »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sat Sep 4 01:10:22 2010
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.