Mikhael Goikhman <[EMAIL PROTECTED]> wrote on Sat, 2 Nov 2002 03:08:09 +0000:
>
> Subject: Re: FVWM: Idea: Improved Window List Menu
>
> On 01 Nov 2002 18:04:38 -0500, Richard Lister wrote:
> > 
> > As I mentioned in another thread, I've already implemented this.
> > I ported it to the 2.5.4 perllib, which was very straightforward.
> > 
> > Let me know if you want me to post it. I've included a screenshot
> > of the menu generated.
> 
> Actually, I will need to write a lot of small modules in addition to the
> current 6 in cvs to test trackers, I may use your module as a test case.


Hi Mikhael

I've added optional page and desk-level resolution and some
documentation to the script. I've attached it here.


> > Is there any info online about the automatic trackers you are
> > implementing? 
> 
> Such info is not even in my head yet. I only know that this should be
> somewhat powerful and easy to use and extend. :)

As far as I understand it the trackers will make most of the stuff
I've done in handlers redundant. Is that correct?


> Take a look at TrackerFactory.pm in cvs or ftp.fvwm.org to get some ideas.

Will do this later.


> My plan is to get some module and see how trackers may help it.
> For example, your module may use 3 trackers, WindowList (with "wininfo"
> and probably "icons" options), ConfigInfo and PageInfo. Other trackers
> may be: Colorsets, GlobalInfo and "stack" option to the WindowList.
> Nothing is final at all, even the names "tracker" and "grabber".
> 
> You are as the library user may help with ideas and design.
> If you want to participate, install cvs.
> Just please keep in mind that this is not my first priority for November.

Ditto on November :-)  But I'll take a look when I get a bit of time.


A bug in my script ... sticky windows are only listed on the desk
where they originated (were made sticky). This is not a problem
when changing page, as there seems to be a M_CONFIGURE_WINDOW packet
sent for each window in that case and I set the desk from that handler.
But these packets are not send for a change desk event. Why is that?
I could probably get round this by doing a Send_Windowlist when
M_NEW_DESK is caught, but that seems clunky :-)

Cheers
Ric


Richard J Lister
Dept of Neuroscience
Georgetown University Medical Center
http://cns.georgetown.edu/~ric/

#!/usr/bin/perl -w

=head1 VERSION

  $Id: FvwmLister.pl,v 2.2 2002/11/05 23:57:10 ric Exp $

=head1 AUTHOR

  Ric Lister <http://cns.georgetown.edu/~ric/>

=head1 DESCRIPTION

  Substitute for fvwm builtin WindowList, but written in Perl
  and easy to customize. Unlike FvwmIconMan or FvwmWinlist the
  module does not draw its own window, but instead creates a
  fvwm menu and asks fvwm to pop it up.

  By defining a set of regular expressions in @show, windows may
  be sorted into sections based on the regexp matching their
  title.

  Similarly, matches in @dontshow will be excluded from the list.

  Any windows not matching an element of @show or @dontshow will
  be placed in the last section of the menu.

=head1 USAGE

  Place this in e.g. your .fvwm2rc StartFunction
    
    Module FvwmLister

  Invoke the menu from .fvwmrc like this, for example:
    
    Key Menu A N SendToModule FvwmLister Root c c

  The arguments are any valid Menu arguments.

  Set module options, e.g.:

    *FvwmLister: Show ^Galeon|^Netscape
    *FvwmLister: Show ^emacs

  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:

    *FvwmLister: DontShow ^Fvwm
    *FvwmLister: DontShow ^gkrellm

  will cause the menu to ignore windows with title beginning Fvwm or
  gkrellm.

  Other options:
    
    *FvwmLister: OnlyIconic {on|off}    ## show only iconified windows
    *FvwmLister: MiniIcon {0|1|2}       ## show mini icon: 0=never,
                                        ## 1=always, 2=icon windows only
    *FvwmLister: AllDesks {on|off}      ## show windows from all desks
    *FvwmLister: AllPages {on|off}      ## show windows from all pages
    *FvwmLister: ShowPosition {on|off}  ## show window position in ()
    *FvwmLister: Maxlen 32              ## max length in chars of entry
    *FvwmLister: Menu foobar            ## name of menu to popup
    *FvwmLister: MenuStyle foobarstyle  ## name of MenuStyle to apply
    *FvwmLister: Debug {-1,0,1,...}     ## level of debug info output

=cut

## default user options
my $opt = {
  onlyiconic   => 0,        ## only list iconified windows
  mini_icon    => 2,        ## 0=never, 1=always, 2=iconic windows only
  alldesks     => 0,        ## list windows on all desks
  allpages     => 0,        ## list windows on all pages
  showposition => 1,        ## show window position
  maxlen       => 32,       ## max chars for window name in menu
  menu         => "Lister", ## name of fvwm menu to create
  menustyle    => "Lister", ## name of fvwm menu style to use
};

## 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);

## or hard-code it
# $screen->{w} = 1600;
# $screen->{h} = 1200;


##
## USER SHOULD NOT NEED TO CHANGE ANYTHING BELOW HERE
##

use strict;
use lib `fvwm-perllib dir`;
use FVWM::Module;
use FVWM::EventNames;

## some global vars
my (%window, $page, $desk);

## 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 = -1 for no messages at all
## set Debug = 0 to see my messages about window decisions
## set Debug = 1 to see also perllib messages about communication
my $module = new FVWM::Module(
                              Name => "FvwmLister",
                              Mask => M_WINDOW_NAME | M_STRING |
                              M_NEW_PAGE | M_NEW_DESK |
                              M_ADD_WINDOW | M_CONFIGURE_WINDOW |
                              M_DESTROY_WINDOW | M_END_WINDOWLIST |
                              M_ICONIFY | M_DEICONIFY | M_MINI_ICON |
                              M_CONFIG_INFO | M_SENDCONFIG,
                              Debug => -1,
                              );

$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+(.*)$/i ) {
    push(@show, $1);
  }

  ## get dontshow array
  elsif ( /^dontshow\s+(.*)$/i ) {
    push(@dontshow, $1);
  }

  ## get mini_icon option
  elsif ( /^miniicon\s*(.*)/i ) {
    $opt->{mini_icon} = $1 if $1;
  }

  ## get all other options, arg of 'off' sets opt to 0
  elsif ( $1 =~ /^(\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
});


## handler to get page and desk info
$module->addHandler(M_NEW_PAGE, sub {
  my ($module, $event) = @_;

  $desk = $event->_desk;

  $page = {
    x => $event->_vp_x,
    y => $event->_vp_y,
    h => $event->_vp_height,
    w => $event->_vp_width,
  }
});


$module->addHandler(M_NEW_DESK, sub {
  my ($module, $event) = @_;
  $desk = $event->_desk;
});



$module->addHandler(M_STRING, sub{
  my ($module, $event) = @_;
  &PopupMenu($event->_text);
});


$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_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 {
  my($menu_args) = @_;

  my @section;

  ## loop on list of windows
  foreach my $id ( keys %window ) {

    $module->debug("\t$id - " . $window{$id}->{name});

    ## skip windows in the dontshow array
    my $dont_show_me;
    foreach ( @dontshow ) {
      $dont_show_me = 1, last if $window{$id}->{name} =~ /$_/;
    }

    $module->debug("\t\tin dontshow list"), next
        if $dont_show_me;

    ## 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} && (not $window{$id}->{iconic});

    ## skip window if its top left is not on this page
    if ( ( not $opt->{allpages} ) &&
        ($window{$id}->{x} < 0 || $window{$id}->{x} >= $screen->{w} ||
         $window{$id}->{y} < 0 || $window{$id}->{y} >= $screen->{h}) )
    {
      $module->debug("\t\tnot on this page");
      next;
    }

    ## loop on sections and choose which one to file in
    my $sectctr = 0;
    foreach ( @show ) {
      last if $window{$id}->{name} =~ /$_/ ;
      ++$sectctr;
    }

    ## 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);
  }


  ## tell fvwm to start the menu
  $module->send("DestroyMenu " . $opt->{menu});
  $module->send("AddToMenu " . $opt->{menu} . " \"Desk $desk, Page +" .
                $page->{x} . "+" . $page->{y} . "\" Title");

  ## now loop on sections sending menu entries to fvwm
  while ( @section ) {
    my $s = shift @section;

    if ( $s ) {
      $module->send($s);

      ## add separator after section unless this is last section
      $module->send("+ \"\" Nop") if ( @section );
    }
  }

  ## set a menustyle if one given
  $module->send("ChangeMenuStyle " . $opt->{menustyle} . " " . $opt->{menu})
      if $opt->{menustyle};

  ## popup the menu with args we were sent
  $module->send("Menu " . $opt->{menu} . " " . $menu_args);
}



## build a line containing the fvwm menu entry for a window
## then add it to the appropriate member of the global array @section
## args: pointer to section, window id
sub AddToSection {
  my($s, $id) = @_;

  ## shorten menu entry
  my $entry = substr($window{$id}->{name}, 0, $opt->{maxlen});

  ## add ellipses if we shortened entry
  $entry .= "..." if length($window{$id}->{name}) > $opt->{maxlen};

  ## add window position
  $entry .= "\t\t(" . $window{$id}->{x} . "x" . $window{$id}->{y} . ")"
      if $opt->{showposition};

  ## add desk number if we are in multi-desk mode
  $entry .= "\t(" . $window{$id}->{desk} . ")"
      if $opt->{alldesks};

  ## add mini_icon always (opt=1) or only to iconified programs (opt=2)
  my $mi = "";
  if ( ($opt->{mini_icon} == 1) or
       ($opt->{mini_icon} == 2 and $window{$id}->{iconic}) ) {
    $mi = "%" . $window{$id}->{mini_icon} . "%";
  }

  ## add the entry to the section
  $$s .= "+ \"$entry$mi\" WindowListFunc $id\n";
}



## main loop
$module->send("Send_ConfigInfo");
$module->send("Send_WindowList");
$module->eventLoop;

Reply via email to