Hi, when trying to understand how to tweak/extend FVWM using Perl (FVWM::Module API), I found this request in the mailing list archives by chance.
On Sat, 24 Nov 2012 04:28:02 -0800, Piotr Isajew wrote: > I wonder, if there is a command to do "smart maximize" in fvwm? > The command would move and resize current window to use the > largest free area of desktop, but not covering any other > windows. Is there something like this? As it looks like an interesting task (neither trivial nor overly complicated), I have given it a try. Although I'm not sure whether I'm using the API in an optimal way (unfortunately haven't been able to find many examples out there), I'm quite impressed how easy programming FVWM has become (if one likes using Perl). In order to test my solution put the attached perl script into your ModulePath, load it and define a key binding for it, e.g.: Key m WTSF 4 SendToModule smart_maximize.pl dummy It's probably far from perfect, but at least may serve as a starting point.
#!/usr/bin/perl # Time-stamp: "2013-06-29 11:13:03 ah" # implement "smart maximize" as described in: # http://www.mail-archive.com/fvwm@lists.math.uh.edu/msg16809.html use warnings; use strict; use List::Util qw(min); use lib `fvwm-perllib dir`; use FVWM::Module; my $module = new FVWM::Module(Mask => M_STRING); my $pageTrackerWL = $module->track('WindowList'); my $vp_width = $pageTrackerWL->pageInfo->{vp_width}; my $vp_height = $pageTrackerWL->pageInfo->{vp_height}; $module->add_handler(M_STRING, \&smartMaximize); $module->event_loop; # main function smartMaximize gets called when issuing an FVWM command like: # SendToModule smart_maximize.pl dummy sub smartMaximize { my ($module, $event) = @_; # the window to be maximized my $own = $pageTrackerWL->data(${$event->args}{win_id}); # we want the window to actually grow my $minArea = $own->{width} * $own->{height} + min($own->{width}, $own->{height}); # Find all free rectangles in the current page where the window could be # displayed. This works as follows: # The list of display rectangles starts with one preliminary display # rectangle: the whole page. # Then one after another all windows are applied to the list of the # preliminary display rectangles. # If a window overlaps with a rectangle, it splits the rectangle into at # most four smaller rectangles: the one above the window, the one below it, # the one left of it, the one right of it. # Any rectangle not bigger than the window to be maximized gets discarded # at once. # After all windows have been applied to the rectangle list, the display # rectangles are not preliminary any longer, as they won't be split into # smaller rectangles any longer. # The biggest display rectangle will be used as the new window location. my @rectangles = (&createRectangle($own->{page_nx} * $vp_width, ($own->{page_nx} + 1) * $vp_width, $own->{page_ny} * $vp_height, ($own->{page_ny} + 1) * $vp_height)); # apply all windows to the display rectangle list foreach my $other ($pageTrackerWL->windows) { unless ($other->{win_id} == $own->{win_id} or $other->{desk} != $own->{desk}) { @rectangles = &applyWindow($other, $minArea, @rectangles); } } # take the rectangle with the largest area as destination my $dest; foreach my $rect (@rectangles) { if ($dest) { # first level criterion: bigger if ($rect->{area} > $dest->{area} # second level criterion: just as big, but more left or ($rect->{area} == $dest->{area} and ($rect->{x_min} < $dest->{x_min} # third level criterion: # just as big, just as left, but more up or ($rect->{x_min} == $dest->{x_min} and $rect->{y_min} < $dest->{y_min})))) { $dest = $rect; } } else { $dest = $rect; } } if ($dest) { # move the window to the display rectangle and let it fill it my $x = $dest->{x_min} - $own->{page_nx} * $vp_width; my $y = $dest->{y_min} - $own->{page_ny} * $vp_height; $module->send("WindowId $own->{win_id} Move ${x}p ${y}p Warp"); $module->send("WindowId $own->{win_id} Maximize grow grow"); } } # Apply a window to the current display rectangle list. # This may change the rectangle list. sub applyWindow { my ($other, $minArea, @oldRectangles) = @_; my $splitter = {x_min => $other->{page_nx} * $vp_width + $other->{x}, x_max => $other->{page_nx} * $vp_width + $other->{x} + $other->{width}, y_min => $other->{page_ny} * $vp_height + $other->{y}, y_max => $other->{page_ny} * $vp_height + $other->{y} + $other->{height}}; my @newRectangles; # apply the window to all display rectangles foreach my $rect (@oldRectangles) { if (&doRectanglesOverlap($rect, $splitter)) { # the window splits the rectangle into 0-4 new, smaller rectangles if ($rect->{x_min} < $splitter->{x_min}) { # new rectangle left of splitter window my $r = &createRectangle($rect->{x_min}, $splitter->{x_min} - 1, $rect->{y_min}, $rect->{y_max}); push @newRectangles, $r if $r->{area} >= $minArea; } if ($rect->{x_max} > $splitter->{x_max}) { # new rectangle right of splitter window my $r = &createRectangle($splitter->{x_max} + 1, $rect->{x_max}, $rect->{y_min}, $rect->{y_max}); push @newRectangles, $r if $r->{area} >= $minArea; } if ($rect->{y_min} < $splitter->{y_min}) { # new rectangle above splitter window my $r = &createRectangle($rect->{x_min}, $rect->{x_max}, $rect->{y_min}, $splitter->{y_min} - 1); push @newRectangles, $r if $r->{area} >= $minArea; } if ($rect->{y_max} > $splitter->{y_max}) { # new rectangle below splitter window my $r = &createRectangle($rect->{x_min}, $rect->{x_max}, $splitter->{y_max} + 1, $rect->{y_max}); push @newRectangles, $r if $r->{area} >= $minArea; } } else { # As window and display rectangle do not overlap, # the display rectangle is not affected by the window. push @newRectangles, $rect; } } return @newRectangles; } sub createRectangle { my ($x_min, $x_max, $y_min, $y_max) = @_; return {x_min => $x_min, x_max => $x_max, y_min => $y_min, y_max => $y_max, area => ($x_max - $x_min) * ($y_max - $y_min)}; } sub doRectanglesOverlap { my ($r1, $r2) = @_; return not ($r1->{x_max} < $r2->{x_min} or $r1->{x_min} > $r2->{x_max} or $r1->{y_max} < $r2->{y_min} or $r1->{y_min} > $r2->{y_max}); }
Regards, Andreas -- Andreas Hoenen <andr...@hoenen-terstappen.de> GPG: 1024D/B888D2CE A4A6 E8B5 593A E89B 496B 82F0 728D 8B7E B888 D2CE
pgpd6uc8PSYpv.pgp
Description: PGP signature