|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FVWM modules are spawned by the main fvwm executable. They usually listen to the window manager events, do some useful work and send back commands for execution. There are transient modules that exit immediately or shortly, and persistent modules that exit together with a window manager or when a user requests. Some modules may control windows or other modules. Some modules may supply a GUI, others may be non interactive.
First, we should understand when our module works. Usually a module does nothing (sleeps) and is awaken when something interesting happens. This is achieved using events. A module defines events that it is interesting to receive and set-ups event handlers (perl functions) to be called when the event happens. Then a module enters the event loop where it sleeps all the time until one or another event happens. Most of the module work is done in the event handlers. When an event is processed, the module enters the event loop again.
In our case, we should listen to an FVWM event M_NEW_PAGE. The list of all events may be found in FVWM::Constants. When we receive the event we want to get new page coordinates and display them using our special xmessage window.
Now, from theory to practice. The header of all modules written in Perl is pretty standard:
#!/usr/bin/perl -w
use lib `fvwm-perllib dir`;
use FVWM::Module;
Then create the actual module object:
my $module = new FVWM::Module(
Mask => M_NEW_PAGE | M_NEW_DESK,
Debug => 1,
);
The Debug option tells to print the event names that a module receives to help writing a module, it also echoes all sent commands. The Mask option tells which events a module wants to receive, in our case these are events generated on the page and desk changes.
To handle events, event handlers that are perl functions, should be defined. It is ok not to define any event handler for M_NEW_DESK and to define two event handlers for M_NEW_PAGE. But for our purposes one M_NEW_PAGE would be more than enough:
$module->addHandler(M_NEW_PAGE, \&gotNewPage);
It is a time to implement our "gotNewPage" function that will be called every time the desktop page is changed.
sub gotNewPage {
my ($module, $event) = @_;
my $width = $event->_vp_width;
my $height = $event->_vp_height;
if (!$width || !$height) {
# this may happen when doing DeskTopSize 1x1 on page 2 2
return;
}
my $page_nx = int($event->_vp_x / $width);
my $page_ny = int($event->_vp_y / $height);
# actually show the flash
$module->send("Exec xmessage -name FlashWindow \
-bg cyan -fg white -center -timeout 1 -button '' \
-xrm '*cursorName: none' -xrm '*borderWidth: 2' \
-xrm '*borderColor: yellow' -xrm '*Margin: 12' \
'($page_nx, $page_ny)'");
}
All event handlers are called with 2 parameters, a module and an event objects. The arguments of the event are described in FVWM::EventNames. Each event type has its own arguments. Our M_NEW_PAGE has 5 arguments: vp_x vp_y desk vp_width vp_height. We should do some calculations to get the page numbers from viewport coordinates.
The send method passes the command to fvwm for execution. It would be better to set-up the FlashWindow specially:
$module->send("Style FlashWindow StaysOnTop, NoTitle, NoHandles, \
BorderWidth 10, WindowListSkip, NeverFocus, UsePPosition");
Finally, all persistent modules should enter the event loop:
$module->eventLoop;
The full module source that we just wrote is available at ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/module-flash . To run it execute this fvwm command:
Module /path/to/module-flash
To kill the module, execute:
KillModule /path/to/module-flash
SendToModule /path/to/module-flash stop
SendToModule /path/to/module-flash format '[%d %d]'
To handle such commands, we should define M_STRING event handler.
use General::Parse;
my $format = "(%d, %d)"; # the default format
$module->mask($module->mask | M_STRING);
$module->addHandler(M_STRING, sub {
my ($module, $event) = @_;
my $line = $event->_text;
my ($action, @args) = getTokens($line);
if ($action eq "stop") {
$module->terminate;
} elsif ($action eq "format") {
$format = $args[0];
}
});
Now, let convert our module to be GTK+ based and not to be dependent on xmessage.
use lib `fvwm-perllib dir`;
use FVWM::Module::Gtk;
use Gtk;
init Gtk;
my $format = "(%d, %d)";
my $module = new FVWM::Module::Gtk(Mask => M_NEW_PAGE);
$module->addHandler(M_NEW_PAGE, sub {
my ($module, $event) = @_;
my $width = $event->_vp_width;
my $height = $event->_vp_height;
if (!$width || !$height) {
return;
}
my $page_nx = int($event->_vp_x / $width);
my $page_ny = int($event->_vp_y / $height);
# actually show the flash
my ($w, $h) = (80, 50);
my $string = sprintf($format, $page_nx, $page_ny);
my $window = new Gtk::Window('toplevel');
$window->set_title("FlashWindow");
$window->set_border_width(5);
$window->set_usize($w, $h);
$window->set_uposition(($width - $w) / 2, ($height - $h) / 2);
my $frame = new Gtk::Frame();
$window->add($frame);
$frame->set_shadow_type('etched_out');
my $label = new Gtk::Label($string);
$frame->add($label);
$window->show_all;
$SIG{ALRM} = sub { $window->destroy(); };
alarm(1);
});
$module->eventLoop;
The full module source that we just wrote is available at ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/module-gtkflash .
Learning the sources of FvwmPerl, FvwmDebug, FvwmGtkDebug modules may help too.
Last modified on August 26, 2005