Hi Mikhael, :) Find attached a patch that includes the WindowList tracker & updated FvwmWindowMenu module which actually utilises the new tracker.
Comments most welcome. SCoTT. :)
Index: modules/ChangeLog =================================================================== RCS file: /home/cvs/fvwm/fvwm/modules/ChangeLog,v retrieving revision 1.1110 diff -u -r1.1110 ChangeLog --- modules/ChangeLog 1 Jun 2004 11:46:54 -0000 1.1110 +++ modules/ChangeLog 2 Jun 2004 11:28:48 -0000 @@ -1,3 +1,10 @@ +2004-06-01 Scott Smedley <[EMAIL PROTECTED]> + + * FvwmWindowMenu/FvwmWindowMenu.in: + * FvwmWindowMenu/FvwmWindowMenu.1: + Rewrote module to use new WindowList tracker. + Updated man page. + 2004-06-01 Dominik Vogt <[EMAIL PROTECTED]> * FvwmConsole/FvwmConsoleC.c (main): Index: modules/FvwmWindowMenu/FvwmWindowMenu.1 =================================================================== RCS file: /home/cvs/fvwm/fvwm/modules/FvwmWindowMenu/FvwmWindowMenu.1,v retrieving revision 1.2 diff -u -r1.2 FvwmWindowMenu.1 --- modules/FvwmWindowMenu/FvwmWindowMenu.1 12 Nov 2003 21:27:41 -0000 1.2 +++ modules/FvwmWindowMenu/FvwmWindowMenu.1 2 Jun 2004 11:28:50 -0000 @@ -128,8 +128,8 @@ .rm #[ #] #H #V #F C .\" ======================================================================== .\" -.IX Title "FVWMWINDOWMENU 1" -.TH FVWMWINDOWMENU 1 "2003-11-12" "perl v5.8.1" "FVWM Modules" +.IX Title "FVWMWINDOWMENU.IN 1" +.TH FVWMWINDOWMENU.IN 1 "2004-06-02" "perl v5.8.1" "FVWM Module" .SH "NAME" FvwmWindowMenu \- open configurable fvwm menu listing current windows .SH "SYNOPSIS" @@ -149,13 +149,14 @@ module does not draw its own window, but instead creates an \&\fIfvwm\fR menu and asks \fIfvwm\fR to pop it up. .PP -By defining a set of regular expressions using Show, windows may -be sorted into sections based on the regexp matching their -name, icon name, class or resource. +By defining a set of regular expressions, windows may +be sorted into sections based on a regexp matching the window +name, class or resource and included in the menu. .PP -Similarly, matches in DontShow will be excluded from the list. +Similarly, another set of regular expressions can be used to exclude +items from the menu. .PP -Any windows not matching an instance of Show or DontShow will +Any windows not matching an instance of the include or exclude list will be placed in the last section of the menu. .SH "USAGE" .IX Header "USAGE" @@ -186,41 +187,44 @@ \&\fBMenu\fR and \fBPopup\fR. If the module was started with \*(L"\-g\*(R" switch, it additionally supports \fBPostBar\fR (not implemented yet). .PP -Set module options for windows to show or not show. The syntax is: +Set module options for windows to include (Show) or exclude (DontShow). +The syntax is: .PP -.Vb 2 -\& *FvwmWindowMenu: Show type = pattern -\& *FvwmWindowMenu: DontShow type = pattern +.Vb 6 +\& *FvwmWindowMenu: ShowName pattern +\& *FvwmWindowMenu: ShowClass pattern +\& *FvwmWindowMenu: ShowResource pattern +\& *FvwmWindowMenu: DontShowName pattern +\& *FvwmWindowMenu: DontShowClass pattern +\& *FvwmWindowMenu: DontShowResource pattern .Ve .PP -where type is one of \fIname\fR, \fIicon\fR, \fIclass\fR or \fIresource\fR. Pattern is -a perl regular expression that will be evaluated in m// context. +Pattern is a perl regular expression that will be evaluated in m// context. See \fIperlre\fR\|(1). .PP For example: .PP .Vb 2 -\& *FvwmWindowMenu: Show resource = Galeon|Navigator|mozilla-bin -\& *FvwmWindowMenu: Show name = ^emacs +\& *FvwmWindowMenu: ShowResource ^gvim +\& *FvwmWindowMenu: ShowName Galeon|Navigator|mozilla-bin|Firefox .Ve .PP -will define two sections containing respectively browsers, and emacs. -Number of sections is unlimited. The strings are perl regular -expressions that will be evaluated in m// context. See \fIperlre\fR\|(1). +will define two sections containing respectively browsers, and GVim. A third +section will contain all other windows. .PP Similarly: .PP .Vb 2 -\& *FvwmWindowMenu: DontShow name = ^Fvwm -\& *FvwmWindowMenu: DontShow class = Gkrellm +\& *FvwmWindowMenu: DontShowName ^Fvwm +\& *FvwmWindowMenu: DontShowClass Gkrellm .Ve .PP will cause the menu to ignore windows with name beginning with Fvwm or class gkrellm. .PP Other options: -.IP "*FvwmWindowMenu: \fIOnlyIconic\fR {on|off}" 4 -.IX Item "*FvwmWindowMenu: OnlyIconic {on|off}" +.IP "*FvwmWindowMenu: \fIOnlyIconified\fR {on|off}" 4 +.IX Item "*FvwmWindowMenu: OnlyIconified {on|off}" show only iconified windows .IP "*FvwmWindowMenu: \fIAllDesks\fR {on|off}" 4 .IX Item "*FvwmWindowMenu: AllDesks {on|off}" @@ -228,8 +232,8 @@ .IP "*FvwmWindowMenu: \fIAllPages\fR {on|off}" 4 .IX Item "*FvwmWindowMenu: AllPages {on|off}" show windows from all pages -.IP "*FvwmWindowMenu: \fIMaxlen\fR 32" 4 -.IX Item "*FvwmWindowMenu: Maxlen 32" +.IP "*FvwmWindowMenu: \fIMaxLen\fR 32" 4 +.IX Item "*FvwmWindowMenu: MaxLen 32" max length in chars of entry .IP "*FvwmWindowMenu: \fIMenuName\fR MyMenu" 4 .IX Item "*FvwmWindowMenu: MenuName MyMenu" @@ -254,7 +258,11 @@ .ie n .IP "%x, %y" 4 .el .IP "%x, \f(CW%y\fR" 4 .IX Item "%x, %y" -the window x or y coordinates on current page +the window x or y coordinates w.r.t. the page the window is on. +.ie n .IP "%X, %Y" 4 +.el .IP "%X, \f(CW%Y\fR" 4 +.IX Item "%X, %Y" +the window x or y coordinates w.r.t. the desk the window is on. .IP "%d" 4 .IX Item "%d" the window desk number @@ -293,6 +301,12 @@ .Vb 1 \& Key Super_R A A SendToModule FvwmWindowMenu Post Root c c WarpTitle .Ve -.SH "AUTHOR" -.IX Header "AUTHOR" -Ric Lister <http://cns.georgetown.edu/~ric/> +.SH "AUTHORS" +.IX Header "AUTHORS" +.RE +.IP "Ric Lister <http://cns.georgetown.edu/~ric/>" +.IX Item "Ric Lister <http://cns.georgetown.edu/~ric/>" +.RE +.PD 0 +.IP "Scott Smedley" +.IX Item "Scott Smedley" Index: modules/FvwmWindowMenu/FvwmWindowMenu.in =================================================================== RCS file: /home/cvs/fvwm/fvwm/modules/FvwmWindowMenu/FvwmWindowMenu.in,v retrieving revision 1.3 diff -u -r1.3 FvwmWindowMenu.in --- modules/FvwmWindowMenu/FvwmWindowMenu.in 12 Nov 2003 21:27:41 -0000 1.3 +++ modules/FvwmWindowMenu/FvwmWindowMenu.in 2 Jun 2004 11:28:51 -0000 @@ -28,9 +28,7 @@ use lib "@FVWM_PERLLIBDIR@"; use FVWM::Module; -use General::FileSystem "-quiet"; use General::Parse; -use Getopt::Long; my $moduleType = ""; my $moduleClass = "FVWM::Module"; @@ -47,293 +45,178 @@ } } -# default user options -my $opt = { - onlyiconic => 0, # only list iconified windows - alldesks => 0, # list windows on all desks - allpages => 0, # list windows on all pages - maxlen => 32, # max chars for window name in menu - menuname => "MenuFvwmWindowMenu", # name of fvwm menu to create - menustyle => undef, # name of fvwm menu style to use - itemformat => "%m%n%t%t(+%x+%y) - Desk %d", # format of menu items - function => "WindowListFunc", -}; - -# get size of your screen -my $screen; - -# I would like a better way of getting screen dimensions! -open(XWININFO, "xwininfo -root|") || die "can't run xwininfo"; -while (<XWININFO>) { - $screen->{w} = $1 if /Width:\s*(\d+)/; - $screen->{h} = $1 if /Height:\s*(\d+)/; -} -close(XWININFO); - - -# some global vars -my %window; -my $desk = 0; -my $page_nx = 0; -my $page_ny = 0; - -# show = window matches to display in order, each entry defines a section -# dontshow = window matches to ignore, none of these will show up in the menu -my (@show, @dontshow); - # init the module # set Debug = 0 for no messages at all -# set Debug = 1 to see my messages about window decisions +# set Debug = 1 to see messages about window decisions # set Debug = 2 to see also perllib messages about communication my $module = new $moduleClass( Name => "FvwmWindowMenu", - Mask => M_WINDOW_NAME | M_STRING | - M_NEW_PAGE | M_NEW_DESK | - M_ADD_WINDOW | M_CONFIGURE_WINDOW | - M_RES_NAME | M_RES_CLASS | M_ICON_NAME | - M_DESTROY_WINDOW | M_END_WINDOWLIST | - M_ICONIFY | M_DEICONIFY | M_MINI_ICON | - M_CONFIG_INFO | M_SENDCONFIG, + Mask => M_STRING, Debug => 0, ); $module->debug("starting " . $module->name); -# process module options -$module->addHandler(M_CONFIG_INFO, sub { - my ($module, $event) = @_; - - my $modname = $module->name; - return unless $event->_text =~ /^\*$modname(.*)$/; - $_ = $1; - - # get show array - if ( /^show\s+(\w+)\s*=\s*(.*)$/i ) { - (my $type = $1) =~ tr/A-Z/a-z/; - push(@show, { type => $type, pattern => $2 }); - } - - # get dontshow array - elsif ( /^dontshow\s+(\w+)\s*=\s*(.*)$/i ) { - (my $type = $1) =~ tr/A-Z/a-z/; - push(@dontshow, { type => $type, pattern => $2 }); - } - - # match format string in quotes - elsif ( /^itemformat\s+([\"\'])(.*)\1/i ) { - $opt->{itemformat} = $2; - } - - # get all other options, arg of 'off' sets opt to 0 - elsif ( /^(\w+)\s*(\w+)/i ) { - (my $arg1 = $1) =~ tr/A-Z/a-z/; - $opt->{$arg1} = ($2 =~ /^off$/i) ? 0 : $2; - } - - # should probably do more error checking -}); +my $configTracker = $module->track('ModuleConfig', + DefaultConfig => { + OnlyIconified => 'off', + AllDesks => 'off', + AllPages => 'off', + MaxLen => 32, + MenuName => 'MyMenu', + MenuStyle => '', + Debug => 0, + Function => 'MyWindowListFunc', + ItemFormat => '%m%n%t%t(+%x+%y) - Desk %d', + ShowName => '', + ShowClass => '', + ShowResource => '', + DontShowName => '', + DontShowClass => '', + DontShowResource => '', + }); +my $pConfig = $configTracker->data(); +my $winTracker = $module->track("WindowList", "!stack icons names winfo"); -# handler to get page and desk info -$module->addHandler(M_NEW_PAGE, sub { - my ($module, $event) = @_; - - $desk = $event->_desk; - - my $width = $event->_vp_width || 1; - my $height = $event->_vp_height || 1; - - $page_nx = int($event->_vp_x / $width); - $page_ny = int($event->_vp_y / $height); -}); - - -$module->addHandler(M_NEW_DESK, sub { - my ($module, $event) = @_; - $desk = $event->_desk; -}); - - -$module->addHandler(M_STRING, sub{ +$module->addHandler(M_STRING, sub { my ($module, $event) = @_; my ($action, $args) = getToken($event->_text); return unless $action; if ($action =~ /^Post|Menu|Popup$/i) { - &PopupMenu($action, $args); + PopupMenu($action, $args); } elsif ($action =~ /^ShowBar$/i) { if ($moduleType ne "gtk") { $module->debug("Not started with Gtk support", 0); return; } - &PopupTaskBar(); + PopupTaskBar(); } else { $module->debug("Unknown action $action", 0); } }); - -$module->addHandler(M_ADD_WINDOW|M_CONFIGURE_WINDOW, sub { - my ($module, $event) = @_; - - $window{$event->_win_id}->{desk} = $event->_desk; - $window{$event->_win_id}->{x} = $event->_frame_x; - $window{$event->_win_id}->{y} = $event->_frame_y; -}); - - -$module->addHandler(M_DESTROY_WINDOW, sub { - my ($module, $event) = @_; - delete $window{$event->_win_id}; -}); - - -$module->addHandler(M_WINDOW_NAME, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{name} = $event->_name; -}); - -$module->addHandler(M_ICON_NAME, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{icon} = $event->_name; -}); - -$module->addHandler(M_RES_CLASS, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{class} = $event->_name; -}); - -$module->addHandler(M_RES_NAME, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{resource} = $event->_name; -}); - - - -$module->addHandler(M_ICONIFY, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{iconic} = 1; -}); - - -$module->addHandler(M_DEICONIFY, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{iconic} = 0; -}); - - -$module->addHandler(M_MINI_ICON, sub { - my ($module, $event) = @_; - $window{$event->_win_id}->{mini_icon} = $event->_name; -}); - - # does all the work and pops up the menu -sub PopupMenu { +sub PopupMenu ($$) { my ($action, $args) = @_; - my $command = $action =~ /^Popup$/i? "Popup": "Menu"; - - my @section; + my $command = ($action =~ /^Popup$/i ? "Popup" : "Menu"); - # loop on list of windows - foreach my $id ( keys %window ) { + my @aSection; - $module->debug("\t$id - " . $window{$id}->{name}); + # loop on list of all windows + my $pData = $winTracker->data(); + foreach my $id (keys(%{$pData})) + { + my $p = $pData->{$id}; + $module->debug("\t$id - " . $p->{window_name}); + + if ($pConfig->{AllDesks} =~ /off/i && + $p->{desk} != $winTracker->pageInfo('desk_n')) + { + $module->debug("\t\tnot on this desk"); + next; + } - # skip windows in the dontshow array - my $dont_show_me; - foreach ( @dontshow ) { - my $p = $_->{pattern}; - $dont_show_me = 1, last - if $window{$id}->{$_->{type}} =~ /$p/; + if ($pConfig->{AllPages} =~ /off/i && + ($p->{page_x} != $winTracker->pageInfo('page_x') || + $p->{page_y} != $winTracker->pageInfo('page_y'))) + { + $module->debug("\t\tnot on this page"); + next; } - $module->debug("\t\tin dontshow list"), next - if $dont_show_me; + if ($pConfig->{OnlyIconified} =~ /on/i && !$p->{iconified}) + { + $module->debug("\t\tnot iconified"); + next; + } - # skip windows not on this desk if alldesks not set - $module->debug("\t\tnot on this desk"), next - unless $opt->{alldesks} || $window{$id}->{desk} == $desk; - - # show only iconic windows if onlyiconic set - $module->debug("\t\tskipping because not iconified"), next - if $opt->{onlyiconic} && ! $window{$id}->{iconic}; - - # skip window if its top left is not on this page - $module->debug("\t\tnot on this page"), next - if ! $opt->{allpages} && ( - $window{$id}->{x} < 0 || - $window{$id}->{x} >= $screen->{w} || - $window{$id}->{y} < 0 || - $window{$id}->{y} >= $screen->{h}); - - # loop on sections and choose which one to file in - my $sectctr = 0; - foreach ( @show ) { - my $p = $_->{pattern}; - last if $window{$id}->{$_->{type}} =~ /$p/ ; - ++$sectctr; + if (($pConfig->{DontShowName} ne '' && + $p->{window_name} =~ /$pConfig->{DontShowName}/i) || + ($pConfig->{DontShowClass} ne '' && + $p->{res_class_name} =~ /$pConfig->{DontShowClass}/i) || + ($pConfig->{DontShowResource} ne '' && + $p->{res_name} =~ /$pConfig->{DontShowResource}/i)) + { + $module->debug("\t\tin dontshow list"); + next; } - # if we got here add the window to the last section - # which is 'none of the above' - $module->debug("\t\tadding to section $sectctr"); - &AddToSection(\$section[$sectctr], $id); + my $section = 3; + if ($pConfig->{ShowName} ne '' && + $p->{window_name} =~ /$pConfig->{ShowName}/i) { + $section = 0; + } elsif ($pConfig->{ShowClass} ne '' && + $p->{res_class_name} =~ /$pConfig->{ShowClass}/i) { + $section = 1; + } elsif ($pConfig->{ShowResource} ne '' && + $p->{res_name} =~ /$pConfig->{ShowResource}/i) { + $section = 2; + } + $module->debug("\t\tadding to section $section"); + AddToSection(\$aSection[$section], $id); } # tell fvwm to start the menu - $module->send("DestroyMenu $opt->{menuname}"); - $module->send("AddToMenu $opt->{menuname} 'Desk $desk, " - . "Page $page_nx $page_ny' Title"); + $module->send("DestroyMenu recreate $pConfig->{MenuName}"); + $module->send("AddToMenu " . $pConfig->{MenuName} . " 'Desk " . + $winTracker->pageInfo('desk_n') . ", Page " . + $winTracker->pageInfo('page_x') . ' ' . + $winTracker->pageInfo('page_y') . "' Title"); # now loop on sections sending menu entries to fvwm - while ( @section ) { - my $s = shift @section; + while ( @aSection ) { + my $s = shift @aSection; if ($s) { $module->send($s); - # add separator after section unless it is the last - $module->send("+ \"\" Nop") if @section; + $module->send("+ \"\" Nop") if @aSection; } } # set a menustyle if one given - $module->send("ChangeMenuStyle $opt->{menustyle} $opt->{menuname}") - if $opt->{menustyle}; + $module->send("ChangeMenuStyle $pConfig->{MenuStyle} $pConfig->{MenuName}") + if ($pConfig->{MenuStyle} ne ''); # popup the menu with args we were sent - $module->send("$command $opt->{menuname} $args"); + $module->send("$command $pConfig->{MenuName} $args"); } # build a line containing the fvwm menu entry for a window -# then add it to the appropriate member of the global array @section +# then add it to the appropriate member of the global array @aSection # args: pointer to section, window id -sub AddToSection { +sub AddToSection ($$) { my ($s, $id) = @_; - my $format = $opt->{itemformat}; + my $format = $pConfig->{ItemFormat}; + my $p = $winTracker->data($id); # hack: insert __%__ instead of % to avoid bogus substitution later $format =~ s/%%/__%____%__/g; # make format string substitutions $format =~ s/%t/\t/g; - $format =~ s/%n/&Shorten($window{$id}->{name}, $opt->{maxlen})/ge; - $format =~ s/%i/&Shorten($window{$id}->{icon}, $opt->{maxlen})/ge; - $format =~ s/%c/&Shorten($window{$id}->{class}, $opt->{maxlen})/ge; - $format =~ s/%r/&Shorten($window{$id}->{resource}, $opt->{maxlen})/ge; - $format =~ s/%x/$window{$id}->{x}/g; - $format =~ s/%y/$window{$id}->{y}/g; - $format =~ s/%d/$window{$id}->{desk}/g; - - $format =~ s/%m/__%__$window{$id}->{mini_icon}__%__/g; - - if ($window{$id}->{iconic}) { - $format =~ s/%M/__%__$window{$id}->{mini_icon}__%__/g; - } - else { + $format =~ s/%n/&Shorten($p->{window_name})/ge; + $format =~ s/%i/&Shorten($p->{icon_name})/ge; + $format =~ s/%c/&Shorten($p->{res_class_name})/ge; + $format =~ s/%r/&Shorten($p->{res_name})/ge; + $format =~ s/%X/$p->{x}/g; + $format =~ s/%Y/$p->{y}/g; + $format =~ s/%x/$p->{frame_x}/g; + $format =~ s/%y/$p->{frame_y}/g; + $format =~ s/%d/$p->{desk}/g; + + # TODO: doesn't handle EWMH icons yet. + $format =~ s/%m// if ($p->{mini_icon_name} eq 'ewmh_mini_icon'); + $format =~ s/%m/__%__$p->{mini_icon_name}__%__/g; + + # %M is strange - does anyone really want this behaviour? -- SS. + if ($p->{iconified}) { + $format =~ s/%M/__%__$p->{mini_icon_name}__%__/g; + } else { $format =~ s/%M//g; } @@ -345,13 +228,14 @@ # add the entry to the section # support two ways for now: window context (new), window id param (old) - $$s .= qq(+ "$format" WindowId $id $opt->{function} $id\n); + $$s .= qq(+ "$format" WindowId $id $pConfig->{Function} $id\n); } # shorten a string to given length and append ellipses -sub Shorten { - my($string, $length) = @_; +sub Shorten ($) { + my ($string) = @_; + my $length = $pConfig->{MaxLen}; my $r = substr($string, 0, $length); $r .= "..." if length($string) > $length; @@ -359,7 +243,6 @@ return $r; } - sub PopupTaskBar () { my ($w, $h) = (180, 60); @@ -367,7 +250,10 @@ $window->set_title("FvwmWindowMenuBar"); $window->set_border_width(5); $window->set_usize($w, $h); - $window->set_uposition(($screen->{w} - $w) / 2, ($screen->{h} - $h) / 2); + + my $screenW = $winTracker->pageInfo('vp_width'); + my $screenH = $winTracker->pageInfo('vp_height'); + $window->set_uposition(($screenW - $w) / 2, ($screenH - $h) / 2); my $frame = new Gtk::Frame(); $window->add($frame); @@ -390,9 +276,6 @@ } -# main loop -$module->send("Send_ConfigInfo"); -$module->send("Send_WindowList"); $module->send( "Style FvwmWindowMenuBar UsePPosition, !Title, !Borders, " . "StaysOnTop, WindowListSkip, CascadePlacement, SloppyFocus" @@ -425,13 +308,14 @@ module does not draw its own window, but instead creates an I<fvwm> menu and asks I<fvwm> to pop it up. -By defining a set of regular expressions using Show, windows may -be sorted into sections based on the regexp matching their -name, icon name, class or resource. +By defining a set of regular expressions, windows may +be sorted into sections based on a regexp matching the window +name, class or resource and included in the menu. -Similarly, matches in DontShow will be excluded from the list. +Similarly, another set of regular expressions can be used to exclude +items from the menu. -Any windows not matching an instance of Show or DontShow will +Any windows not matching an instance of the include or exclude list will be placed in the last section of the menu. =head1 USAGE @@ -457,28 +341,32 @@ B<Menu> and B<Popup>. If the module was started with "-g" switch, it additionally supports B<PostBar> (not implemented yet). -Set module options for windows to show or not show. The syntax is: +Set module options for windows to include (Show) or exclude (DontShow). +The syntax is: - *FvwmWindowMenu: Show type = pattern - *FvwmWindowMenu: DontShow type = pattern + *FvwmWindowMenu: ShowName pattern + *FvwmWindowMenu: ShowClass pattern + *FvwmWindowMenu: ShowResource pattern + *FvwmWindowMenu: DontShowName pattern + *FvwmWindowMenu: DontShowClass pattern + *FvwmWindowMenu: DontShowResource pattern -where type is one of I<name>, I<icon>, I<class> or I<resource>. Pattern is -a perl regular expression that will be evaluated in m// context. +Pattern is a perl regular expression that will be evaluated in m// context. See perlre(1). For example: - *FvwmWindowMenu: Show resource = Galeon|Navigator|mozilla-bin - *FvwmWindowMenu: Show name = ^emacs + *FvwmWindowMenu: ShowResource ^gvim + *FvwmWindowMenu: ShowName Galeon|Navigator|mozilla-bin|Firefox + +will define two sections containing respectively browsers, and GVim. A third +section will contain all other windows. -will define two sections containing respectively browsers, and emacs. -Number of sections is unlimited. The strings are perl regular -expressions that will be evaluated in m// context. See perlre(1). Similarly: - *FvwmWindowMenu: DontShow name = ^Fvwm - *FvwmWindowMenu: DontShow class = Gkrellm + *FvwmWindowMenu: DontShowName ^Fvwm + *FvwmWindowMenu: DontShowClass Gkrellm will cause the menu to ignore windows with name beginning with Fvwm or class gkrellm. @@ -487,7 +375,7 @@ =over 4 -=item *FvwmWindowMenu: I<OnlyIconic> {on|off} +=item *FvwmWindowMenu: I<OnlyIconified> {on|off} show only iconified windows @@ -499,7 +387,7 @@ show windows from all pages -=item *FvwmWindowMenu: I<Maxlen> 32 +=item *FvwmWindowMenu: I<MaxLen> 32 max length in chars of entry @@ -531,7 +419,11 @@ =item %x, %y -the window x or y coordinates on current page +the window x or y coordinates w.r.t. the page the window is on. + +=item %X, %Y + +the window x or y coordinates w.r.t. the desk the window is on. =item %d @@ -573,8 +465,10 @@ Key Super_R A A SendToModule FvwmWindowMenu Post Root c c WarpTitle -=head1 AUTHOR +=head1 AUTHORS + +=item Ric Lister <http://cns.georgetown.edu/~ric/> -Ric Lister <http://cns.georgetown.edu/~ric/> +=item Scott Smedley =cut Index: perllib/ChangeLog =================================================================== RCS file: /home/cvs/fvwm/fvwm/perllib/ChangeLog,v retrieving revision 1.46 diff -u -r1.46 ChangeLog --- perllib/ChangeLog 24 Apr 2004 19:13:13 -0000 1.46 +++ perllib/ChangeLog 2 Jun 2004 11:28:52 -0000 @@ -1,3 +1,8 @@ +2004-06-01 Scott Smedley <[EMAIL PROTECTED]> + + * FVWM/Tracker/WindowList.pm: + actually implemented tracker & all observables. + 2004-04-24 Mikhael Goikhman <[EMAIL PROTECTED]> * FVWM/Tracker.pm: Index: perllib/FVWM/Tracker/WindowList.pm =================================================================== RCS file: /home/cvs/fvwm/fvwm/perllib/FVWM/Tracker/WindowList.pm,v retrieving revision 1.4 diff -u -r1.4 WindowList.pm --- perllib/FVWM/Tracker/WindowList.pm 25 Oct 2003 03:00:01 -0000 1.4 +++ perllib/FVWM/Tracker/WindowList.pm 2 Jun 2004 11:28:53 -0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2003 Mikhael Goikhman +# Copyright (c) 2004 Mikhael Goikhman, Scott Smedley # # 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 @@ -20,11 +20,25 @@ use FVWM::Tracker qw(base); +my $windowEvents = M_ADD_WINDOW | M_CONFIGURE_WINDOW | M_DESTROY_WINDOW | + M_ICONIFY | M_DEICONIFY; +my $nameEvents = M_RES_NAME | M_RES_CLASS | M_WINDOW_NAME | M_VISIBLE_NAME | + M_ICON_NAME; +my $stackEvents = M_RESTACK | M_RAISE_WINDOW | M_LOWER_WINDOW; +my $iconEvents = M_ICON_LOCATION | M_ICON_FILE | M_DEFAULTICON | M_MINI_ICON; + sub observables ($) { return [ "window added", "window deleted", "window properties updated", + "window moved", + "window resized", + "window iconified", + "window deiconified", + "window name updated", + "window stack updated", + "window icon updated", ]; } @@ -36,41 +50,38 @@ my $self = $class->FVWM::Tracker::new($module); $self->{options} = [ @options ]; - return $self; } -sub addRequestedInfoHandlers ($$$$) { +sub addRequestedInfoHandlers ($$) { my $self = shift; - my $handlerC = shift; - my $handlerD = shift; - my $handlerP = shift; + my $handler = shift; my $useWInfo = 1; my $useNames = 1; my $useStack = 0; my $useIcons = 0; - foreach my $option (@{$self->{options}}) { + foreach (@{$self->{options}}) { /^(\!?)winfo$/ and $useWInfo = $1 ne '!'; /^(\!?)names$/ and $useNames = $1 ne '!'; /^(\!?)stack$/ and $useStack = $1 ne '!'; /^(\!?)icons$/ and $useIcons = $1 ne '!'; } - my $mask1 = 0; - my $mask2 = 0; - my $xmask2 = 0; - $mask1 |= M_ADD_WINDOW | M_CONFIGURE_WINDOW if $useWInfo; - $mask2 |= M_RES_NAME | M_RES_CLASS | M_WINDOW_NAME | M_VISIBLE_NAME | M_ICON_NAME - if $useNames; - $mask2 |= M_RESTACK | M_RAISE_WINDOW | M_LOWER_WINDOW if $useStack; - $mask2 |= M_ICON_LOCATION | M_ICON_FILE | M_DEFAULTICON | M_MINI_ICON - if $useIcons; - $xmask2 |= MX_VISIBLE_ICON_NAME if $useNames; - - $self->addHandler($mask1, $handlerC) if $mask1; - $self->addHandler(M_DESTROY_WINDOW, $handlerD); - $self->addHandler($mask2, $handlerP) if $mask2; - $self->addHandler($xmask2, $handlerP) if $xmask2; + my $mask = 0; + $mask |= $windowEvents if $useWInfo; + $mask |= $nameEvents if $useNames; + $mask |= $stackEvents if $useStack; + $mask |= $iconEvents if $useIcons; + + # Adding MX_VISIBLE_ICON_NAME to $nameEvents does not work. + my $xmask = 0; + $xmask |= MX_VISIBLE_ICON_NAME if $useNames; + + $self->addHandler($mask, $handler) if $mask; + $self->addHandler($xmask, $handler) if $xmask; + $self->addHandler(M_NEW_PAGE | M_NEW_DESK, sub { + $self->handlerPageInfo($_[1]); + }) if $useWInfo; } sub start ($) { @@ -78,37 +89,55 @@ $self->{data} = {}; - ### TODO $self->addRequestedInfoHandlers(sub { my $event = $_[1]; - $self->calculateInternals($event->args); - }, sub { - my $event = $_[1]; - $self->calculateInternals($event->args); - }, sub { - my $event = $_[1]; - $self->calculateInternals($event->args); + $self->calculateInternals($event); }); $self->requestWindowListEvents; - - ### temporary - $self->deleteHandlers; - my $result = $self->SUPER::start; - $self->deleteHandlers; - ### TODO $self->addRequestedInfoHandlers(sub { my $event = $_[1]; - $self->calculateInternals($event->args); - }, sub { - my $event = $_[1]; - $self->calculateInternals($event->args); - }, sub { - my $event = $_[1]; - $self->calculateInternals($event->args); + my ($winId, $oldHash) = $self->calculateInternals($event); + return unless defined $winId; + my $type = $event->type(); + if ($type & M_ADD_WINDOW) { + $self->notify("window added", $winId); + } elsif ($type & M_CONFIGURE_WINDOW) { + $self->notify("window properties updated", $winId, $oldHash); + # The "window properties updated" observable is very broad & + # occurs as a result of many different operations - here we + # try & determine if some common operations occur & invoke more + # specific observables. This may reduce the amount of parsing + # external modules have to do. + my $p = $self->{data}->{$winId}; + if ($p->{frame_width} != $oldHash->{frame_width} || + $p->{frame_height} != $oldHash->{frame_height}) + { + $self->notify("window resized", $winId, $oldHash); + } + elsif ($p->{desk} != $oldHash->{desk} || + $p->{x} != $oldHash->{x} || + $p->{y} != $oldHash->{y}) + { + $self->notify("window moved", $winId, $oldHash); + } + # We can easily add other specific observables here as required. + } elsif ($type & M_DESTROY_WINDOW) { + $self->notify("window deleted", $winId, $oldHash); + } elsif ($type & M_ICONIFY) { + $self->notify("window iconified", $winId, $oldHash); + } elsif ($type & M_DEICONIFY) { + $self->notify("window deiconified", $winId, $oldHash); + } elsif ($type & $nameEvents || $type & MX_VISIBLE_ICON_NAME) { + $self->notify("window name updated", $winId, $oldHash); + } elsif ($type & $stackEvents) { + $self->notify("window stack updated", $winId, $oldHash); + } elsif ($type & $iconEvents) { + $self->notify("window icon updated", $winId, $oldHash); + } }); return $result; @@ -116,12 +145,83 @@ sub calculateInternals ($$) { my $self = shift; - my $args = shift; + my $event = shift; + my $args = $event->args; my $data = $self->{data}; + my $winId = $args->{win_id}; + + my $oldHash = undef; + if (defined $data->{$winId}) { + # make a copy of the hash cos we are about to modify it. + my %tmp = %{$data->{$winId}}; + $oldHash = \%tmp; + } + + # There are some fields that are not unique to all events. To ensure + # we don't clobber them, we rename some fields. For example, the 'name' + # field of M_MINI_ICON events is renamed to 'mini_icon_name'. + foreach ('name', 'x', 'y', 'width', 'height') { + if (defined $args->{$_}) { + (my $name = lc($event->name())) =~ s/^.*?_//; + $name .= "_$_" if ($name !~ /$_$/); + $args->{$name} = $args->{$_}; + delete $args->{$_}; + } + } + + # Please leave this (commented-out) code here. I often enable it + # when debugging -- SS. +# print(STDERR "Got " . $event->name() . " event\n"); +# foreach (sort(keys %{$args})) { +# print(STDERR " $_=" . $args->{$_} . "\n"); +# } + + @{$data->{$winId}}{keys %{$args}} = values %{$args}; + + if (defined $args->{frame_x} && defined $self->{page_info}) { + # frame_x & frame_y are _relative_ coords of the window to the + # current page - calculate the _absolute_ coords - x & y. + my $pW = $data->{$winId}; + $pW->{x} = $self->pageInfo('vp_x') + $args->{frame_x}; + $pW->{y} = $self->pageInfo('vp_y') + $args->{frame_y}; + $pW->{page_x} = int($pW->{x} / $self->pageInfo('vp_width')); + $pW->{page_y} = int($pW->{y} / $self->pageInfo('vp_height')); + } - ### TODO + my $type = $event->type(); + if ($type & M_ADD_WINDOW || $type & M_DEICONIFY) { + $data->{$winId}->{iconified} = 0; + } elsif ($type & M_ICONIFY) { + $data->{$winId}->{iconified} = 1; + } elsif ($type & M_DESTROY_WINDOW) { + delete $data->{$winId}; + } + + return wantarray ? ($winId, $oldHash) : $winId; } +sub handlerPageInfo ($$) { + my $self = shift; + my $event = shift; + my $args = $event->args; + + @{$self->{page_info}}{keys %{$args}} = values %{$args}; + + if ($event->type() & M_NEW_PAGE) { + $self->{page_info}->{page_x} = int($args->{vp_x} / $args->{vp_width}); + $self->{page_info}->{page_y} = int($args->{vp_y} / $args->{vp_height}); + } +} + +sub pageInfo ($;$) { + my $self = shift; + my $id = shift; + my $data = $self->{page_info}; + return $data unless defined $id; + return $data->{$id}; +} + + sub data ($;$) { my $self = shift; my $id = shift; @@ -158,24 +258,31 @@ This tracker defines the following observables: - "window added", - "window deleted", - "window properties updated", - -NOT USABLE YET. + "window added", + "window deleted", + "window properties updated", + "window moved", + "window resized", + "window iconified", + "window deiconified", + "window name updated", + "window stack updated", + "window icon updated", =head1 SYNOPSYS Using B<FVWM::Module> $module object (preferably): - my $windowsTracker = $module->track("ModuleConfig"); - my $windows = $windowsTracker->data; - my $windowSizeX = $windows->{$winId}->{'x'}; + my $windowsTracker = $module->track("WindowList", $options); + my $windows = $windowsTracker->data; + my $windowSizeX = $windows->{$winId}->{'x'}; or: - my $windowsTracker = $module->track("WindowList"); - my $windowSizeX = $windowsTracker->data($winId)->{'x'}; + my $windowsTracker = $module->track("WindowList", $options); + my $windowSizeX = $windowsTracker->data($winId)->{'x'}; + +Default $options string is: "!stack !icons names winfo" =head1 OVERRIDDEN METHODS @@ -190,7 +297,7 @@ =item B<data> [I<window-id>] Returns array ref of window hash refs. or one window hash ref if -I<window-id> is given. The hash keys are not finalized yet. +I<window-id> is given. =item B<dump> [I<window-id>] @@ -198,9 +305,25 @@ =back -=head1 AUTHOR +=head1 METHODS -Mikhael Goikhman <[EMAIL PROTECTED]>. +=over 4 + +=item B<pageInfo> [I<field>] + +Returns hash ref of page/desk info, or actual hash value using B<field> as a key (if specified). + +=back + +=head1 AUTHORS + +=over 4 + +=item Mikhael Goikhman <[EMAIL PROTECTED]> + +=item Scott Smedley + +=back =head1 SEE ALSO