Updated for CVS 2.5.32 now.
[fvwm.git] / bin / fvwm-perllib.in
blob4dece6b9cc89019aa97b41ec19cd4ad0a8e2f416
1 #!@PERL@
3 # Copyright (c) 2002-2009 Mikhael Goikhman
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 # Filter this script to pod2man to get a man page:
20 #   pod2man -c "Fvwm Utilities" fvwm-perllib | nroff -man | less -e
22 #use strict;  # comment to make it faster
24 BEGIN {
25 #       use vars qw($prefix $datarootdir $datadir $perllibdir);
26         $prefix = "@prefix@";
27         $datarootdir = "@datarootdir@";
28         $datadir = "@datadir@";
29         $perllibdir = "@FVWM_PERLLIBDIR@";
31         # try to do it as fast as possible
32         if ($ARGV[0] eq 'dir') {
33                 print $perllibdir;
34                 exit(0);
35         }
38 use Getopt::Long;
39 use lib $perllibdir;
40 use General::FileSystem '-die';
42 my $version = "@VERSION@";
43 my $version_info = "@VERSIONINFO@";
45 my $pager = $ENV{PAGER} || "less -e";
46 my $do_man = 0;
47 my $do_cat = 0;
48 my $do_raw = 0;
50 GetOptions(
51         "help|h|?"    => \&show_help,
52         "version|v|V" => \&show_version,
53         "man"         => \$do_man,
54         "cat"         => \$do_cat,
55         "raw"         => \$do_raw,
56         "dir"         => sub { print $perllibdir; exit(0); },
57 ) || wrong_usage();
59 if ($ARGV[0] eq 'man') {
60         $do_man = 1;
61         shift;
62 } elsif ($ARGV[0] eq 'cat') {
63         $do_cat = 1;
64         shift;
65 } elsif ($ARGV[0] eq 'raw') {
66         $do_raw = 1;
67         shift;
70 wrong_usage() if !$do_man && !$do_cat && !$do_raw || @ARGV > 1;
72 my $man_or_cat_str = $do_man || $do_raw ? "man" : "cat";
73 my $internal_pods = {};
75 $internal_pods->{index} = qq{
76         :head1 NAME
78         index - lists all available help topics
80         :head1 DESCRIPTION
82         Recent I<fvwm> versions install the Perl library that makes creating
83         fvwm modules in Perl possible and easy.
85         You may read the Perl library documentation locally by running:
87             % fvwm-perllib $man_or_cat_str <topic>
89         Available topics:
91             index
92             tutorial
93             events
94             {{CLASS_NAMES}}
96         For example:
98             % fvwm-perllib $man_or_cat_str FVWM::Module
100         :head1 AUTHOR
102         Mikhael Goikhman <migo\@homemail.com>.
105 $internal_pods->{tutorial} = q{
106         :head1 NAME
108         tutorial - common techniques for writting fvwm modules
110         :head1 TUTORIAL
112         :head2 What is a window manager
114         A window manager is a program that runs on top of the X Window
115         System and manages windows, menus, key and mouse bindings, virtual
116         desktops and pages, draws window decorations using defined colors or
117         images, title-bar buttons and fonts. The window manager defines
118         window placement and focus policies. It may also manage such things
119         as root background, mouse cursors, sounds, run applications and
120         do other nice things.
122         :head2 What is a module
124         In the unix traditions, different functionality may be implemented
125         by separate programs to reduce a bloat. A module is an optional
126         program that is intended to extend the window manager using a defined
127         module protocol.
129         Fvwm modules are spawned by the main I<fvwm> executable. They
130         usually listen to the window manager events, do some useful work and
131         send back commands for execution. There are transient modules that
132         exit immediately or shortly, and persistent modules that exit
133         together with a window manager or when a user requests.  Some
134         modules may control windows or other modules. Some modules may supply
135         a GUI, others may be non interactive.
137         :head2 Creating a simple module
139         Let's create a module that shows a flash window for one second when
140         you change pages. We will use I<xmessage> with nifty options for our
141         flash purposes, but you may use your fantasy to do this better.
143         First, we should understand when our module works. Usually a module
144         does nothing (sleeps) and is awaken when something interesting
145         happens. This is achieved using events. A module defines events that
146         it is interesting to receive and set-ups event handlers (perl
147         functions) to be called when the event happens. Then a module enters
148         the event loop where it sleeps all the time until one or another
149         event happens. Most of the module work is done in the event
150         handlers. When an event is processed, the module enters the event
151         loop again.
153         In our case, we should listen to an fvwm event I<M_NEW_PAGE>. The list
154         of all events may be found in man page "events". When we receive
155         the event we want to get new page coordinates and display them
156         using our special xmessage window.
158         Now, from theory to practice. The header of all modules written in
159         Perl is pretty standard:
161             #!/usr/bin/perl -w
163             use lib `fvwm-perllib dir`;
164             use FVWM::Module;
166         Then create the actual module object:
168             my $module = new FVWM::Module(
169                 Mask => M_NEW_PAGE | M_NEW_DESK,
170                 Debug => 1,
171             );
173         The B<Debug> option tells to print the event names that a module
174         receives to help writing a module, it also echoes all sent commands.
175         The B<Mask> option tells which events a module wants to receive, in
176         our case these are events generated on the page and desk changes.
178         To handle events, event handlers that are perl functions, should be
179         defined. It is ok not to define any event handler for I<M_NEW_DESK>
180         and to define two event handlers for I<M_NEW_PAGE>. But for our
181         purposes one I<M_NEW_PAGE> would be more than enough:
183             $module->add_handler(M_NEW_PAGE, \&got_new_page);
185         It is a time to implement our C<got_new_page> function that will be
186         called every time the desktop page is changed.
188             sub got_new_page {
189                 my ($module, $event) = @_;
191                 my $width  = $event->_vp_width;
192                 my $height = $event->_vp_height;
194                 if (!$width || !$height) {
195                     # this may happen when doing DeskTopSize 1x1 on page 2 2
196                     return;
197                 }
198                 my $page_nx = int($event->_vp_x / $width);
199                 my $page_ny = int($event->_vp_y / $height);
201                 # actually show the flash
202                 $module->send("Exec xmessage -name FlashWindow \
203                     -bg cyan -fg white -center -timeout 1 -button '' \
204                     -xrm '*cursorName: none' -xrm '*borderWidth: 2' \
205                     -xrm '*borderColor: yellow' -xrm '*Margin: 12' \
206                     '($page_nx, $page_ny)'");
207             }
209         All event handlers are called with 2 parameters, a module and an
210         event objects. The arguments for all events are defined in
211         L<FVWM::EventNames>. Each event type has its own arguments. Our
212         I<M_NEW_PAGE> has 5 arguments: vp_x vp_y desk vp_width vp_height.
213         We should do some calculations to get the page numbers from viewport
214         coordinates.
216         The B<send> method passes the command to I<fvwm> for execution.
217         It would be better to set-up the FlashWindow specially:
219             $module->send("Style FlashWindow StaysOnTop, NoTitle, NoHandles, \
220                 BorderWidth 10, WindowListSkip, NeverFocus, UsePPosition");
222         Finally, all persistent modules should enter the event loop:
224             $module->event_loop;
226         The full module source that we just wrote is available at
227         ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/module-flash .
228         To run it execute this fvwm command:
230             Module /path/to/module-flash
232         To kill the module, execute:
234             KillModule /path/to/module-flash
236         :head2 Using event trackers
238         In fact, the task of calculating page coordinates, or managing
239         information about all windows, or gathering colorset, module or
240         global information is so often, that there are existing
241         implentation in the form of event trackers. Tracker is an instance
242         of L<FVWM::Tracker> superclass. Currently these tracker classes are
243         available (see their man pages):
245             FVWM::Tracker::Colorsets
246             FVWM::Tracker::GlobalConfig
247             FVWM::Tracker::ModuleConfig
248             FVWM::Tracker::PageInfo
249             FVWM::Tracker::Scheduler
250             FVWM::Tracker::WindowList
252         Using a tracker is easy, something along lines:
254             my $tracker = $module->track("WindowList");
255             my $colorset_tracker = $module->track("Colorsets");
257         Our module that we wrote above may be reduced if we use:
259             my $viewport = $module->track("PageInfo");
261             my $page_nx = $viewport->data("page_nx");
262             my $page_ny = $viewport->data("page_ny");
264         Note that the tracker continues to work and maintain the up-to-date
265         information about the current page and desk (or up-to-date windows
266         or colorsets depending on the tracker type) at any given moment.
268         Internally, trackers listen to appropriate events using the same
269         event handler mechanism, so there is no speed advantage. However
270         it is a good idea to reuse the existing verified code and reduce
271         the number of events needed to be trapped manually. There is
272         usually no problem if the developer and the tracker define
273         handlers for the same events (besides the handler order maybe).
275         :head2 On the module masks
277         In our example above we explicitly defined Mask in constructor.
278         This is not really needed. If not specified, the event mask is
279         managed automatically (it is updated every time a new event handler
280         is added).
282         Note, there are actually two event masks, called "mask" and
283         "xmask" (extended mask). If you are interested in the details,
284         refer to the fvwm documentation or perllib sources.
286         When trackers are added or removed, the module mask (and xmask)
287         are automatically tweaked underhand. In short, there is often no
288         reason to worry about the module masks. However, in rare cases
289         you may want to define SyncMask (or SyncXMask), so that fvwm is
290         synchronized with the module on certain events.
292         :head2 Creating a more functional module
294         Let's extend our new-page-flash example above and add a way to stop
295         our module and to define another string format. This would be
296         possible using the following I<fvwm> commands:
298             SendToModule /path/to/module-flash stop
299             SendToModule /path/to/module-flash format '[%d %d]'
301         To handle such commands, we should define I<M_STRING> event handler.
303             use General::Parse;
304             my $format = "(%d, %d)";  # the default format
306             $module->mask($module->mask | M_STRING);
307             $module->add_handler(M_STRING, sub {
308                 my ($module, $event) = @_;
309                 my $line = $event->_text;
310                 my ($action, @args) = get_tokens($line);
312                 if ($action eq "stop") {
313                     $module->terminate;
314                 } elsif ($action eq "format") {
315                     $format = $args[0];
316                 }
317             });
319         Now, let's convert our module to be GTK+ based and not to be
320         dependent on I<xmessage>.
322             use lib `fvwm-perllib dir`;
323             use FVWM::Module::Gtk;
324             use POSIX qw(SIGALRM);
325             Gtk->init;
327             my $format = "(%d, %d)";
328             my $module = new FVWM::Module::Gtk;
329             my $pageinfo = $module->track("PageInfo");
331             $pageinfo->observe("desk/page changed", sub {
332                 my ($module, $tracker, $data) = @_;
334                 my $page_nx = $data->{page_nx};
335                 my $page_ny = $data->{page_ny};
336                 my $width  = $data->{vp_width};
337                 my $height = $data->{vp_height};
339                 # actually show the flash
340                 my ($w, $h) = (80, 50);
341                 my $string = sprintf($format, $page_nx, $page_ny);
343                 my $window = new Gtk::Window('toplevel');
344                 $window->set_title("FlashWindow");
345                 $window->set_border_width(5);
346                 $window->set_usize($w, $h);
347                 $window->set_uposition(($width - $w) / 2, ($height - $h) / 2);
349                 my $frame = new Gtk::Frame();
350                 $window->add($frame);
351                 $frame->set_shadow_type('etched_out');
353                 my $label = new Gtk::Label($string);
354                 $frame->add($label);
355                 $window->show_all;
357                 POSIX::sigaction(SIGALRM, POSIX::SigAction->new(
358                     sub { $window->destroy(); }
359                 ));
360                 alarm(1);
361             });
363             $module->event_loop;
365         The full module source that we just wrote is available at
366         ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/module-gtkflash .
368         :head1 EXAMPLES
370         Currently see I<ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/>
371         for examples.
373         Learning the sources of B<FvwmPerl>, B<FvwmDebug>, B<FvwmGtkDebug>
374         modules may help too.
376         :head1 SEE ALSO
378         See L<FVWM::Module> and L<FVWM::Module::Gtk> for the module API.
380         :head1 AUTHOR
382         Mikhael Goikhman <migo@homemail.com>.
385 $internal_pods->{events} = q{
386         :head1 NAME
388         events - list of all fvwm events with arguments
390         :head1 DESCRIPTION
392         This list is automatically generated from L<FVWM::EventNames> package.
394         Given L<FVWM::Event> object of certain event type, say I<M_STRING>,
395         here is the syntax to get value of its I<win_id> argument:
397             $win_id = $event->_win_id;
399         There are several more ways to access arguments, like:
401             $win_id = $event->arg_values->[0];
402             $text = $event->args->{text};
404         :head1 EVENTS WITH ARGUMENTS
406         {{EVENT_NAMES}}
408         :head1 ARGUMENT TYPE LEGEND
410         Here is a mapping of fvwm argument types to perl native types:
412             number - integer
413             bool   - boolean, true or false
414             window - X window id in decimal, use sprintf("0x%07x", $wid)
415             pixel  - "rgb:" . join('/', sprintf("%06lx", $val) =~ /(..)(..)(..)/)
416             string - string (scalar)
417             wflags - window flags in binary string
418             looped - loop of zero or more fixed argument bunches
420         Run L<FvwmDebug> or L<FvwmGtkDebug> to browse events.
422         :head1 SEE ALSO
424         See "tutorial", L<FVWM::Event>, L<FVWM::EventNames> and L<FvwmDebug>.
426         :head1 AUTHOR
428         Mikhael Goikhman <migo@homemail.com>.
431 my $topic = $ARGV[0] || "index";
432 my $file = "-";
433 my $text = "";
434 if (exists $internal_pods->{$topic}) {
435         $text = $internal_pods->{$topic};
436         $text =~ s/^\t//mg;
437         $text =~ s/^:/=/mg;
439         if ($topic eq 'index') {
440                 my @class_names = sort @{list_filenames($perllibdir, 1)};
441                 @class_names = map { s!\.pm$!!; s!/!::!g; $_ } @class_names;
442                 $text =~ s/{{CLASS_NAMES}}/join("\n    ", @class_names)/seg;
443         }
445         if ($topic eq 'events') {
446                 my $content = `cat '$perllibdir/FVWM/EventNames.pm'`;
447                 my $result = "";
448                 foreach ($content =~ /\t&([^\s]+.*?\t\tfields[^\n]+(?:\n\t\t\t[^\n]+)*)/sg) {
449                         my ($name, $rest) = /^([^\s]+).*?((?:\n\t\t\t[^\n]+)*)$/s;  # ]
450                         $result .= "    $name\n";
451                         $rest =~ s/([^\s]+)\s*=>\s*([\w]+)/
452                                 $result .= sprintf("        %-16s\t%s\n", $1, $2)
453                         /eg;
454                         $result .= "\n";
455                 }
456                 $text =~ s!{{EVENT_NAMES}}!$result!se;
457         }
459 } else {
460         $file = "$perllibdir/$topic.pm";
461         $file =~ s!::!/!g;
462         die "No $file found.\n" unless -f $file;
465 my $man_converter = $do_man ? " | nroff -man | $pager" : "";
466 open(MANPIPE, $do_cat ? "| pod2text '$file' | $pager" :
467         "| pod2man --section 3 --release 'fvwm $version$version_info'" .
468                 " --center 'Fvwm Perl library' '$file'" .
469                 " | sed 's/<STANDARD INPUT>/perllib/ig'$man_converter")
470         or die "Can't open pipe to pod/man viewer\n";
471 print MANPIPE $text
472         or die "Can't write to pod/man viewer\n";
473 close MANPIPE;
475 # ---------------------------------------------------------------------------
477 sub show_help {
478         print "Shows documentation of the supplied FVWM Perl library.\n\n";
479         print "Usage: fvwm-perllib man|cat\n";
480         print "\tAn introduction to the FVWM Perl library\n\n";
481         print "Usage: fvwm-perllib man|cat|raw <Perl::Class>\n";
482         print "\tManual page for <Perl::Class>, try: man FVWM::Module\n";
483         print "\t\$PAGER is used for a pager, the default is '$pager'\n\n";
484         print "Usage: fvwm-perllib dir\n";
485         print "\tFor use in fvwm modules written in Perl\n\n";
486         print "Usage: fvwm-perllib [OPTIONS]\n";
487         print "Options:\n";
488         print "\t--help           show this help and exit\n";
489         print "\t--version        show the version and exit\n";
490         exit 0;
493 sub show_version {
494         print "$version\n";
495         exit 0;
498 sub wrong_usage {
499         print STDERR "Try '$0 --help' for more information.\n";
500         exit -1;
503 __END__
505 # ---------------------------------------------------------------------------
507 =head1 NAME
509 fvwm-perllib - shows the documentation of the Fvwm Perl library
511 =head1 SYNOPSIS
513 B<fvwm-perllib>
514 [ B<--help>|B<-h>|B<-?> ]
515 [ B<--version>|B<-v>|B<-V> ]
516 [ B<man> [ I<Perl::Class> ] ]
517 [ B<cat> [ I<Perl::Class> ] ]
518 [ B<raw> [ I<Perl::Class> ] ]
519 [ B<dir> ]
521 =head1 DESCRIPTION
523 Starting from fvwm-2.5.x versions there is a built-in support for creating
524 fvwm modules in Perl. This B<fvwm-perllib> utility provides help services
525 for the Fvwm Perl library.
527 =head1 OPTIONS
529 B<--help>
530     show the help and exit
532 B<--version>
533     show the version and exit
535 B<--man> or B<man> [ I<Perl::Class> ]
536     show manual page just like man(1)
538 B<--cat> or B<cat> [ I<Perl::Class> ]
539     show manual page in plain text
541 B<--raw> or B<raw> [ I<Perl::Class> ]
542     generate output in man format (not human readable)
544 B<--dir> or B<dir>
545     print perllib directory without a trailing end of line
547 =head1 USAGE
549 Use this in the fvwm modules written in Perl:
551     use lib `fvwm-perllib dir`;
553 Introduction to the Fvwm Perl library:
555     % fvwm-perllib man
557 Manual page for the C<FVWM::Module> class:
559     % fvwm-perllib man FVWM::Module
561 Standard options:
563     % fvwm-perllib --help
564     % fvwm-perllib --version
566 =head1 AUTHORS
568 Mikhael Goikhman <migo@homemail.com>.
570 =head1 COPYING
572 The script is distributed by the same terms as fvwm itself.
573 See GNU General Public License for details.
575 =head1 BUGS
577 No known bugs.
579 Report bugs to fvwm-bug@fvwm.org.
581 =cut
583 # ***************************************************************************