#!/usr/bin/perl -w # Record the icon position of all iconified Firefox windows in a way that can # be replayed as fvwm commands to re-iconify them and re-position their icons. # This is descending into some sort of baroque hell, really. # # Arguments: # --file FILE save to file instead of /tmp/WindowRecord.out # --name NAME look for windows with NAME and CLASS instead # --class CLASS of Navigator/Minefield # --allpos restore uniconified position too # # BUGS: really only works well if it's invoked from page 0,0 of your # virtual desktop. Doesn't cope at all with windows with the same name. # Hardcodes my desktop size instead of getting it from the M_NEW_PAGE # message. Probably many others. # # Copyright: GPL. See the end for the formal verbiage. use strict; use lib `/u/cks/lib/i386-linux/fvwm-cvs/bin/fvwm-perllib dir`; use FVWM::Module; use IO::File; my $prog = "FvwmWindowRecorder"; my $fname = "/tmp/WindowRecord.out"; my $name = "Navigator"; my $class = "Minefield"; my $allpos; my $module = new FVWM::Module( Name => $prog, Mask => M_WINDOW_NAME | M_RES_NAME | M_RES_CLASS | M_ICONIFY | M_END_WINDOWLIST | M_CONFIGURE_WINDOW, EnableOptions => { "file=s" => \$fname, "class=s" => \$class, "name=s" => \$name, "allpos" => \$allpos, }, Debug => 1, ); # ... it is probably obvious that I don't understand Perl OO. { package WinData; use Class::Struct; struct('wid' => '$', 'name' => '$', 'res' => '$', 'class' => '$', 'icon_x' => '$', 'icon_y' => '$', 'iconified' => '$', 'win_x' => '$', 'win_y' => '$'); } # Track windows by FVWM Window ID. Each entry is a WinData structure, # with fields that may or may not be defined. my %byid; sub setup_id { my $event = shift; my $wid = $event->_win_id; if (! defined $byid{$wid}) { $byid{$wid} = WinData->new(wid => $wid); $byid{$wid}->icon_x(0); $byid{$wid}->icon_y(0); } return $byid{$wid}; } # Register the event handlers # TODO: the first two lines of each of these functions are generic. # Maybe Perl provides some way of condensing these. $module->add_handler(M_CONFIGURE_WINDOW, sub { my ($self, $event) = @_; my $wi = setup_id($event); $wi->win_x($event->_frame_x); $wi->win_y($event->_frame_y); }); $module->add_handler(M_WINDOW_NAME, sub { my ($self, $event) = @_; my $wi = setup_id($event); $wi->name($event->_name); }); $module->add_handler(M_RES_NAME, sub { my ($self, $event) = @_; my $wi = setup_id($event); $wi->res($event->_name); }); $module->add_handler(M_RES_CLASS, sub { my ($self, $event) = @_; my $wi = setup_id($event); $wi->class($event->_name); }); $module->add_handler(M_ICONIFY, sub { my ($self, $event) = @_; my $wi = setup_id($event); $wi->iconified(1); $wi->icon_x($event->_icon_x); $wi->icon_y($event->_icon_y); }); # " turns into \", $ turns into $$. sub fvwm_quote { my $sstr = $_[0]; $sstr =~ s'"'\"'g; $sstr =~ s'\$'$$'g; return $sstr; } # This one signals that the module should be terminated $module->add_handler(M_END_WINDOWLIST, sub { my $fh = new IO::File "> $fname" or die "Can't open $fname: $!"; print $fh "# Written by $prog\n\n"; # Order the windows by increasing X and then increasing Y. # This works out more or less the way I want. my @v = sort { $a->icon_x <=> $b->icon_x || $a->icon_y <=> $b->icon_y } values %byid; foreach my $wd (@v) { next unless ($wd->iconified); next unless ($wd->class eq $class && $wd->res eq $name); # This is not expected to work for out of screen icon # positions. next if ($wd->icon_x > 2560 || $wd->icon_y > 1024); next if ($wd->icon_x < 0 || $wd->icon_y < 0); my $nstr = sprintf('Next ("%s", "%s", "%s")', fvwm_quote($wd->name), fvwm_quote($wd->res), fvwm_quote($wd->class)); if ($allpos && defined $wd->win_x && defined $wd->win_y) { print $fh "$nstr Iconify False\n"; printf $fh "$nstr Move %d%s %d%s\n", $wd->win_x, 'p', $wd->win_y, 'p'; } print $fh "$nstr Iconify True\n"; printf $fh "$nstr Move %d%s %d%s\n", $wd->icon_x, 'p', $wd->icon_y, 'p'; } print $fh "\n# Done\n"; close $fh; $module->terminate; }); # Ask fvwm to send us its list of windows $module->send("Send_WindowList"); # Enter the main loop $module->event_loop; # What I get is a stream of # M_WINDOW_NAME # M_RES_NAME / M_RES_CLASS sets # M_ICONIFY with icon_x and icon_y # M_CONFIGURE_WINDOW with uniconified window position & size # and some sundry others. # how to see this: # 'Module FvwmGtkDebug' # Setup -> Unselect all events, Start monitoring events # Tools -> Send_WindowList # Stored Events -> page through them # I could pluck pager information out of the M_NEW_PAGE message. # --- # # Copyright (C) 2010 Chris Siebenmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA