Subject: PATCH: WindowList tracker (perllib)

From: Scott Smedley <[email-masked]>
Date: Thu, 20 May 2004 01:58:05 +1000

An updated version of the WindowList tracker. This version fixes clobbering
of fields.

SCoTT. :)
Index: WindowList.pm
===================================================================
RCS file: /home/cvs/fvwm/fvwm/perllib/FVWM/Tracker/WindowList.pm,v
retrieving revision 1.4
diff -u -r1.4 WindowList.pm
--- WindowList.pm	25 Oct 2003 03:00:01 -0000	1.4
+++ WindowList.pm	19 May 2004 14:56:32 -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,21 @@
 
 use FVWM::Tracker qw(base);
 
+my $windowEvents = M_ADD_WINDOW | M_CONFIGURE_WINDOW | M_DESTROY_WINDOW;
+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 name updated",
+		"window stack updated",
+		"window icon updated",
 	];
 }
 
@@ -36,41 +46,36 @@
 	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, sub { $self->handlerNewPage($_[1]); });
 }
 
 sub start ($) {
@@ -78,37 +83,62 @@
 
 	$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 & $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,10 +146,56 @@
 
 sub calculateInternals ($$) {
 	my $self = shift;
-	my $args = shift;
+	my $event = shift;
+	my $args = $event->args;
 	my $data = $self->{data};
 
-	### TODO
+	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->{$_};
+		}
+	}
+
+	@{$data->{$winId}}{keys %{$args}} = values %{$args};
+
+	if (defined $args->{frame_x})
+	{
+		# frame_x & frame_y are _relative_ coords of the window to the
+		# current page - calculate the _absolute_ coords - x & y.
+		my $p = $self->{pageInfo};
+		$data->{$winId}->{x} = $p->{currentPageX} + $args->{frame_x};
+		$data->{$winId}->{y} = $p->{currentPageY} + $args->{frame_y};
+	}
+
+	return wantarray ? ($winId, $oldHash) : $winId;
+}
+
+sub handlerNewPage ($$) {
+	my $self = shift;
+	my $event = shift;
+	my $args = $event->args;
+
+	$self->{pageInfo}->{currentPageX} = $args->{vp_x};
+	$self->{pageInfo}->{currentPageY} = $args->{vp_y};
+	$self->{pageInfo}->{pageWidth} = $args->{vp_width};
+	$self->{pageInfo}->{pageHeight} = $args->{vp_height};
 }
 
 sub data ($;$) { 
@@ -161,22 +237,27 @@
     "window added",
     "window deleted",
     "window properties updated",
-
-NOT USABLE YET.
+	"window moved",
+	"window resized",
+	"window name updated",
+	"window stack updated",
+	"window icon updated",
 
 =head1 SYNOPSYS
  
 Using B<FVWM::Module> $module object (preferably):
 
-    my $windowsTracker = $module->track("ModuleConfig");
+    my $windowsTracker = $module->track("WindowList", $options);
     my $windows = $windowsTracker->data;
     my $windowSizeX = $windows->{$winId}->{'x'};
 
 or:
 
-    my $windowsTracker = $module->track("WindowList");
+    my $windowsTracker = $module->track("WindowList", $options);
     my $windowSizeX = $windowsTracker->data($winId)->{'x'};
 
+Default $options string is: "!stack !icons names winfo"
+
 =head1 OVERRIDDEN METHODS
 
 =over 4
@@ -198,9 +279,9 @@
 
 =back
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Mikhael Goikhman <[email-masked]>.
+Mikhael Goikhman <[email-masked]>, Scott Smedley.
 
 =head1 SEE ALSO