source: perl/lib/BarnOwl.pm @ 782fd39

Last change on this file since 782fd39 was 978a164, checked in by Jason Gross <jgross@mit.edu>, 7 years ago
Moved log file name generation to perl I don't think that the class/personal distinction is the best for general protocols, but I don't know what should replace it. I've made class-logging default to only zephyr (a slight change from previous behavior, where jabber MUC's would be logged to $classlogpath, as well as all non-private non-login messages from other protocols), but made logging path generation overridable. TODO: Decide whether or not to filter out more 'bad' characters. Perhaps we should remove '!' because it indicates history in some shells and makes things obnoxious, or '~' becase it indicates homedirs in many shells. * '/' is for separating directories, and we don't want to accidentally make subdirectories We first NFKC for zephyrs, and then apply lc. The zephyr servers apply case-folded NFKC (so says http://zephyr.1ts.org/browser/zephyr/server/zstring.c). We should probably use Unicode::CaseFold instead of lc. I'm also not sure what the order case-adjustment and normalization should be. We first NFKC, then apply lc, to jabbers, as per http://xmpp.org/internet-drafts/attic/draft-ietf-xmpp-nodeprep-03.html (though I can't actually find anything that specifies the case-folding algorithm, nor the ordering). We now use lc instead of g_utf8_strdown to normalize AIM screennames.
  • Property mode set to 100644
File size: 23.8 KB
Line 
1use strict;
2use warnings;
3
4package BarnOwl;
5
6use base qw(Exporter);
7our @EXPORT_OK = qw(command getcurmsg getnumcols getnumlines getidletime
8                    register_idle_watcher unregister_idle_watcher
9                    zephyr_getsender zephyr_getrealm zephyr_zwrite
10                    zephyr_stylestrip zephyr_smartstrip_user zephyr_getsubs
11                    queue_message admin_message
12                    start_edit
13                    start_question start_password start_edit_win
14                    get_data_dir get_config_dir popless_text popless_ztext
15                    error debug
16                    create_style getnumcolors wordwrap
17                    message_matches_filter
18                    add_dispatch remove_dispatch
19                    add_io_dispatch remove_io_dispatch
20                    new_command
21                    new_variable_int new_variable_bool new_variable_string
22                    new_variable_enum
23                    quote redisplay);
24our %EXPORT_TAGS = (all => \@EXPORT_OK);
25
26BEGIN {
27# bootstrap in C bindings and glue
28    *owl:: = \*BarnOwl::;
29    bootstrap BarnOwl 1.2;
30};
31
32use lib(get_data_dir() . "/lib");
33use lib(get_config_dir() . "/lib");
34
35use Glib;
36use AnyEvent;
37
38use BarnOwl::Hook;
39use BarnOwl::Hooks;
40use BarnOwl::Logging;
41use BarnOwl::Message;
42use BarnOwl::Style;
43use BarnOwl::Zephyr;
44use BarnOwl::Timer;
45use BarnOwl::Editwin;
46use BarnOwl::Completion;
47use BarnOwl::Help;
48use BarnOwl::DeferredLogging;
49
50use List::Util qw(max);
51use Tie::RefHash;
52
53=head1 NAME
54
55BarnOwl
56
57=head1 DESCRIPTION
58
59The BarnOwl module contains the core of BarnOwl's perl
60bindings. Source in this module is also run at startup to bootstrap
61BarnOwl by defining things like the default style.
62
63=for NOTE
64These following functions are defined in perlglue.xs. Keep the
65documentation here in sync with the user-visible commands defined
66there!
67
68=head2 command STRING
69
70Executes a BarnOwl command in the same manner as if the user had
71executed it at the BarnOwl command prompt. If the command returns a
72value, return it as a string, otherwise return undef.
73
74=head2 getcurmsg
75
76Returns the current message as a C<BarnOwl::Message> subclass, or
77undef if there is no message selected
78=head2 getnumcols
79
80Returns the width of the display window BarnOwl is currently using
81
82=head2 getidletime
83
84Returns the length of time since the user has pressed a key, in
85seconds.
86
87=head2 zephyr_getrealm
88
89Returns the zephyr realm BarnOwl is running in
90
91=head2 zephyr_getsender
92
93Returns the fully-qualified name of the zephyr sender BarnOwl is
94running as, e.g. C<nelhage@ATHENA.MIT.EDU>
95
96=head2 zephyr_zwrite COMMAND MESSAGE
97
98Sends a zephyr programmatically. C<COMMAND> should be a C<zwrite>
99command line, and C<MESSAGE> is the zephyr body to send.
100
101=cut
102
103sub zephyr_zwrite {
104    my ($command, $message) = @_;
105    my $ret = BarnOwl::Internal::zephyr_zwrite($command, $message);
106    die "Error sending zephyr" unless $ret == 0;
107}
108
109=head2 ztext_stylestrip STRING
110
111Strips zephyr formatting from a string and returns the result
112
113=head2 zephyr_getsubs
114
115Returns the list of subscription triples <class,instance,recipient>,
116separated by newlines.
117
118=head2 queue_message MESSAGE
119
120Enqueue a message in the BarnOwl message list, logging it and
121processing it appropriately. C<MESSAGE> should be an instance of
122BarnOwl::Message or a subclass.
123
124=head2 admin_message HEADER BODY
125
126Display a BarnOwl B<Admin> message, with the given header and body.
127
128=head2 start_edit %ARGS
129
130Displays a prompt on the screen and lets the user enter text,
131and calls a callback when the editwin is closed.
132
133C<%ARGS> must contain the following keys:
134
135=over 4
136
137=item prompt
138
139The line to display on the screen
140
141=item type
142
143One of:
144
145=over 4
146
147=item edit_win
148
149Displays the prompt on a line of its own and opens the edit_win.
150
151=item question
152
153Displays prompt on the screen and lets the user enter a line of
154text.
155
156=item password
157
158Like question, but echoes the user's input as C<*>s when they
159input.
160
161=back
162
163=item callback
164
165A Perl subroutine that is called when the user closes the edit_win.
166C<CALLBACK> gets called with two parameters: the text the user entered,
167and a C<SUCCESS> boolean parameter which is false if the user canceled
168the edit_win and true otherwise.
169
170=back
171
172=head2 start_question PROMPT CALLBACK
173
174=head2 start_password PROMPT CALLBACK
175
176=head2 start_edit_win PROMPT CALLBACK
177
178Roughly equivalent to C<start_edit> called with the appropriate parameters.
179C<CALLBACK> is only called on success, for compatibility.
180
181These are deprecated wrappers around L<BarnOwl::start_edit>, and should not
182be uesd in new code.
183
184=cut
185
186sub start_edit {
187    my %args = (@_);
188    BarnOwl::Internal::start_edit($args{type}, $args{prompt}, $args{callback});
189}
190
191sub start_question {
192    my ($prompt, $callback) = @_;
193    BarnOwl::start_edit(type => 'question', prompt => $prompt, callback => sub {
194            my ($text, $success) = @_;
195            $callback->($text) if $success;
196        });
197}
198
199sub start_password {
200    my ($prompt, $callback) = @_;
201    BarnOwl::start_edit(type => 'password', prompt => $prompt, callback => sub {
202            my ($text, $success) = @_;
203            $callback->($text) if $success;
204        });
205}
206
207sub start_edit_win {
208    my ($prompt, $callback) = @_;
209    BarnOwl::start_edit(type => 'edit_win', prompt => $prompt, callback => sub {
210            my ($text, $success) = @_;
211            $callback->($text) if $success;
212        });
213}
214
215=head2 get_data_dir
216
217Returns the BarnOwl system data directory, where system libraries and
218modules are stored
219
220=head2 get_config_dir
221
222Returns the BarnOwl user configuration directory, where user modules
223and configuration are stored (by default, C<$HOME/.owl>)
224
225=head2 popless_text TEXT
226
227Show a popup window containing the given C<TEXT>
228
229=head2 popless_ztext TEXT
230
231Show a popup window containing the provided zephyr-formatted C<TEXT>
232
233=head2 error STRING
234
235Reports an error and log it in `show errors'. Note that in any
236callback or hook called in perl code from BarnOwl, a C<die> will be
237caught and passed to C<error>.
238
239=head2 debug STRING
240
241Logs a debugging message to BarnOwl's debug log
242
243=head2 getnumcolors
244
245Returns the number of colors this BarnOwl is capable of displaying
246
247=head2 message_matches_filter MESSAGE FILTER_NAME [QUIET = 0]
248
249Returns 1 if C<FILTER_NAME> is the name of a valid filter, and
250C<MESSAGE> matches that filter.  Returns 0 otherwise.  If
251C<QUIET> is false, this method displays an error message if
252if C<FILTER_NAME> does not name a valid filter.
253
254=head2 add_dispatch FD CALLBACK
255
256Adds a file descriptor to C<BarnOwl>'s internal C<select()>
257loop. C<CALLBACK> will be invoked whenever data is available to be
258read from C<FD>.
259
260C<add_dispatch> has been deprecated in favor of C<AnyEvent>, and is
261now a wrapper for C<add_io_dispatch> called with C<mode> set to
262C<'r'>.
263
264=cut
265
266sub add_dispatch {
267    my $fd = shift;
268    my $cb = shift;
269    add_io_dispatch($fd, 'r', $cb);
270}
271
272=head2 remove_dispatch FD
273
274Remove a file descriptor previously registered via C<add_dispatch>
275
276C<remove_dispatch> has been deprecated in favor of C<AnyEvent>.
277
278=cut
279
280*remove_dispatch = \&remove_io_dispatch;
281
282=head2 add_io_dispatch FD MODE CB
283
284Adds a file descriptor to C<BarnOwl>'s internal C<select()>
285loop. <MODE> can be 'r', 'w', or 'rw'. C<CALLBACK> will be invoked
286whenever C<FD> becomes ready, as specified by <MODE>.
287
288Only one callback can be registered per FD. If a new callback is
289registered, the old one is removed.
290
291C<add_io_dispatch> has been deprecated in favor of C<AnyEvent>.
292
293=cut
294
295our %_io_dispatches;
296
297sub add_io_dispatch {
298    my $fd = shift;
299    my $modeStr = shift;
300    my $cb = shift;
301    my @modes;
302
303    push @modes, 'r' if $modeStr =~ /r/i; # Read
304    push @modes, 'w' if $modeStr =~ /w/i; # Write
305    if (@modes) {
306        BarnOwl::remove_io_dispatch($fd);
307        for my $mode (@modes) {
308            push @{$_io_dispatches{$fd}}, AnyEvent->io(fh => $fd,
309                                                       poll => $mode,
310                                                       cb => $cb);
311        }
312    } else {
313        die("Invalid I/O Dispatch mode: $modeStr");
314    }
315}
316
317=head2 remove_io_dispatch FD
318
319Remove a file descriptor previously registered via C<add_io_dispatch>
320
321C<remove_io_dispatch> has been deprecated in favor of C<AnyEvent>.
322
323=cut
324
325sub remove_io_dispatch {
326    my $fd = shift;
327    undef $_ foreach @{$_io_dispatches{$fd}};
328    delete $_io_dispatches{$fd};
329}
330
331=head2 create_style NAME OBJECT
332
333Creates a new BarnOwl style with the given NAME defined by the given
334object. The object must have a C<description> method which returns a
335string description of the style, and a and C<format_message> method
336which accepts a C<BarnOwl::Message> object and returns a string that
337is the result of formatting the message for display.
338
339=head2 redisplay
340
341Redraw all of the messages on screen. This is useful if you've just
342changed how a style renders messages.
343
344=cut
345
346# perlconfig.c will set this to the value of the -c command-line
347# switch, if present.
348our $configfile;
349
350our @all_commands;
351
352if(!$configfile) {
353    if (-f get_config_dir() . "/init.pl") {
354        $configfile = get_config_dir() . "/init.pl";
355    } elsif (-f $ENV{HOME} . "/.barnowlconf") {
356        $configfile = $ENV{HOME} . "/.barnowlconf";
357    } else {
358        $configfile = $ENV{HOME}."/.owlconf";
359    }
360}
361
362# populate global variable space for legacy owlconf files
363sub _receive_msg_legacy_wrap {
364    my ($m) = @_;
365    $m->legacy_populate_global();
366    return &BarnOwl::Hooks::_receive_msg($m);
367}
368
369=head2 new_command NAME FUNC [{ARGS}]
370
371Add a new owl command. When owl executes the command NAME, FUNC will
372be called with the arguments passed to the command, with NAME as the
373first argument.
374
375ARGS should be a hashref containing any or all of C<summary>,
376C<usage>, or C<description> keys:
377
378=over 4
379
380=item summary
381
382A one-line summary of the purpose of the command
383
384=item usage
385
386A one-line usage synopsis, showing available options and syntax
387
388=item description
389
390A longer description of the syntax and semantics of the command,
391explaining usage and options
392
393=back
394
395=cut
396
397sub new_command {
398    my $name = shift;
399    my $func = shift;
400    my $args = shift || {};
401    my %args = (
402        summary     => "",
403        usage       => "",
404        description => "",
405        %{$args}
406    );
407
408    BarnOwl::Internal::new_command($name, $func, $args{summary}, $args{usage}, $args{description});
409}
410
411=head2 new_variable_int NAME [{ARGS}]
412
413=head2 new_variable_bool NAME [{ARGS}]
414
415=head2 new_variable_string NAME [{ARGS}]
416
417=head2 new_variable_enum NAME [{ARGS}]
418
419Add a new owl variable, either an int, a bool, a string, or an enum with the
420specified name.
421
422For new_variable_enum, ARGS is required to contain a validsettings key pointing
423to an array reference. For all four, it can optionally contain the following
424keys:
425
426=over 4
427
428=item default
429
430The default and initial value for the variable
431
432=item summary
433
434A one-line summary of the variable's purpose
435
436=item description
437
438A longer description of the function of the variable
439
440=back
441
442In addition, new_variable_string optionally accepts a string validsettings
443parameter, in case people want to set it to "<path>".
444
445=cut
446
447sub new_variable_int {
448    my ($name, $args) = @_;
449    my $storage = defined($args->{default}) ? $args->{default} : 0;
450    BarnOwl::new_variable_full($name, {
451            %{$args},
452            get_tostring => sub { "$storage" },
453            set_fromstring => sub {
454                die "Expected integer" unless $_[0] =~ /^-?[0-9]+$/;
455                $storage = 0 + $_[0];
456            },
457            validsettings => "<int>",
458            takes_on_off => 0,
459        });
460}
461
462sub new_variable_bool {
463    my ($name, $args) = @_;
464    my $storage = defined($args->{default}) ? $args->{default} : 0;
465    BarnOwl::new_variable_full($name, {
466            %{$args},
467            get_tostring => sub { $storage ? "on" : "off" },
468            set_fromstring => sub {
469                die "Valid settings are on/off" unless $_[0] eq "on" || $_[0] eq "off";
470                $storage = $_[0] eq "on";
471            },
472            validsettings => "on,off",
473            takes_on_off => 1,
474        });
475}
476
477sub new_variable_string {
478    my ($name, $args) = @_;
479    my $storage = defined($args->{default}) ? $args->{default} : "";
480    BarnOwl::new_variable_full($name, {
481            # Allow people to override this one if they /reaaally/ want to for
482            # some reason. Though we still reserve the right to interpret this
483            # value in interesting ways for tab-completion purposes.
484            validsettings => "<string>",
485            %{$args},
486            get_tostring => sub { $storage },
487            set_fromstring => sub { $storage = $_[0]; },
488            takes_on_off => 0,
489        });
490}
491
492sub new_variable_enum {
493    my ($name, $args) = @_;
494
495    # Gather the valid settings.
496    die "validsettings is required" unless defined($args->{validsettings});
497    my %valid;
498    map { $valid{$_} = 1 } @{$args->{validsettings}};
499
500    my $storage = (defined($args->{default}) ?
501                   $args->{default} :
502                   $args->{validsettings}->[0]);
503    BarnOwl::new_variable_full($name, {
504            %{$args},
505            get_tostring => sub { $storage },
506            set_fromstring => sub {
507                die "Invalid input" unless $valid{$_[0]};
508                $storage = $_[0];
509            },
510            validsettings => join(",", @{$args->{validsettings}})
511        });
512}
513
514=head2 new_variable_full NAME {ARGS}
515
516Create a variable, in full generality. The keyword arguments have types below:
517
518 get_tostring : ()  -> string
519 set_fromstring : string -> int
520 -- optional --
521 summary : string
522 description : string
523 validsettings : string
524 takes_on_off : int
525
526The get/set functions are required. Note that the caller manages storage for the
527variable. get_tostring/set_fromstring both convert AND store the value.
528set_fromstring dies on failure.
529
530If the variable takes parameters 'on' and 'off' (i.e. is boolean-looking), set
531takes_on_off to 1. This makes :set VAR and :unset VAR work. set_fromstring will
532be called with those arguments.
533
534=cut
535
536sub new_variable_full {
537    my $name = shift;
538    my $args = shift || {};
539    my %args = (
540        summary => "",
541        description => "",
542        takes_on_off => 0,
543        validsettings => "<string>",
544        %{$args});
545
546    die "get_tostring required" unless $args{get_tostring};
547    die "set_fromstring required" unless $args{set_fromstring};
548
549    # Strip off the bogus dummy argument. Aargh perl-Glib.
550    my $get_tostring_fn = sub { $args{get_tostring}->() };
551    my $set_fromstring_fn = sub {
552      my ($dummy, $val) = @_;
553      # Translate from user-supplied die-on-failure callback to expected
554      # non-zero on error. Less of a nuisance than interacting with ERRSV.
555      eval { $args{set_fromstring}->($val) };
556      # TODO: Consider changing B::I::new_variable to expect string|NULL with
557      # string as the error message. That can then be translated to a GError in
558      # owl_variable_set_fromstring. For now the string is ignored.
559      return ($@ ? -1 : 0);
560    };
561
562    BarnOwl::Internal::new_variable($name, $args{summary}, $args{description}, $args{validsettings},
563                                    $args{takes_on_off}, $get_tostring_fn, $set_fromstring_fn, undef);
564}
565
566=head2 quote LIST
567
568Quotes each of the strings in LIST and returns a string that will be
569correctly decoded to LIST by the BarnOwl command parser.  For example:
570
571    quote('zwrite', 'andersk', '-m', 'Hello, world!')
572    # returns "zwrite andersk -m 'Hello, world!'"
573
574=cut
575
576sub quote {
577    my @quoted;
578    for my $str (@_) {
579        if ($str eq '') {
580            push @quoted, "''";
581        } elsif ($str !~ /['" \n\t]/) {
582            push @quoted, "$str";
583        } elsif ($str !~ /'/) {
584            push @quoted, "'$str'";
585        } else {
586            (my $qstr = $str) =~ s/"/"'"'"/g;
587            push @quoted, '"' . $qstr . '"';
588        }
589    }
590    return join(' ', @quoted);
591}
592
593=head2 Modify filters by appending text
594
595=cut
596
597sub register_builtin_commands {
598    # Filter modification
599    BarnOwl::new_command("filterappend",
600                         sub { filter_append_helper('appending', '', @_); },
601                       {
602                           summary => "append '<text>' to filter",
603                           usage => "filterappend <filter> <text>",
604                       });
605
606    BarnOwl::new_command("filterand",
607                         sub { filter_append_helper('and-ing', 'and', @_); },
608                       {
609                           summary => "append 'and <text>' to filter",
610                           usage => "filterand <filter> <text>",
611                       });
612
613    BarnOwl::new_command("filteror",
614                         sub { filter_append_helper('or-ing', 'or', @_); },
615                       {
616                           summary => "append 'or <text>' to filter",
617                           usage => "filteror <filter> <text>",
618                       });
619
620    # Date formatting
621    BarnOwl::new_command("showdate",
622                         sub { BarnOwl::time_format('showdate', '%Y-%m-%d %H:%M'); },
623                       {
624                           summary => "Show date in timestamps for supporting styles.",
625                           usage => "showdate",
626                       });
627
628    BarnOwl::new_command("hidedate",
629                         sub { BarnOwl::time_format('hidedate', '%H:%M'); },
630                       {
631                           summary => "Don't show date in timestamps for supporting styles.",
632                           usage => "hidedate",
633                       });
634
635    BarnOwl::new_command("timeformat",
636                         \&BarnOwl::time_format,
637                       {
638                           summary => "Set the format for timestamps and re-display messages",
639                           usage => "timeformat <format>",
640                       });
641
642    # Receive window scrolling
643    BarnOwl::new_command("recv:shiftleft",
644                        \&BarnOwl::recv_shift_left,
645                        {
646                            summary => "scrolls receive window to the left",
647                            usage => "recv:shiftleft [<amount>]",
648                            description => <<END_DESCR
649By default, scroll left by 10 columns. Passing no arguments or 0 activates this default behavior.
650Otherwise, scroll by the number of columns specified as the argument.
651END_DESCR
652                        });
653
654    BarnOwl::new_command("recv:shiftright",
655                        \&BarnOwl::recv_shift_right,
656                        {
657                            summary => "scrolls receive window to the right",
658                            usage => "recv:shiftright [<amount>]",
659                            description => <<END_DESCR
660By default, scroll right by 10 columns. Passing no arguments or 0 activates this default behavior.
661Otherwise, scroll by the number of columns specified as the argument.
662END_DESCR
663                        });
664
665}
666
667$BarnOwl::Hooks::startup->add("BarnOwl::register_builtin_commands");
668
669=head3 filter_append_helper ACTION SEP FUNC FILTER APPEND_TEXT
670
671Helper to append to filters.
672
673=cut
674
675sub filter_append_helper
676{
677    my $action = shift;
678    my $sep = shift;
679    my $func = shift;
680    my $filter = shift;
681    my @append = @_;
682    my $oldfilter = BarnOwl::getfilter($filter);
683    chomp $oldfilter;
684    my $newfilter = "$oldfilter $sep " . quote(@append);
685    my $msgtext = "To filter " . quote($filter) . " $action\n" . quote(@append) . "\nto get\n$newfilter";
686    if (BarnOwl::getvar('showfilterchange') eq 'on') {
687        BarnOwl::admin_message("Filter", $msgtext);
688    }
689    set_filter($filter, $newfilter);
690    return;
691}
692BarnOwl::new_variable_bool("showfilterchange",
693                           { default => 1,
694                             summary => 'Show modifications to filters by filterappend and friends.'});
695
696sub set_filter
697{
698    my $filtername = shift;
699    my $filtertext = shift;
700    my $cmd = 'filter ' . BarnOwl::quote($filtername) . ' ' . $filtertext;
701    BarnOwl::command($cmd);
702}
703
704=head3 time_format FORMAT
705
706Set the format for displaying times (variable timeformat) and redisplay
707messages.
708
709=cut
710
711my $timeformat = '%H:%M';
712
713sub time_format
714{
715    my $function = shift;
716    my $format = shift;
717    if(!$format)
718    {
719        return $timeformat;
720    }
721    if(shift)
722    {
723        return "Wrong number of arguments for command";
724    }
725    $timeformat = $format;
726    redisplay();
727}
728
729=head3 Receive window scrolling
730
731Permit scrolling the receive window left or right by arbitrary
732amounts (with a default of 10 characters).
733
734=cut
735
736sub recv_shift_left
737{
738    my $func = shift;
739    my $delta = shift;
740    $delta = 10 unless defined($delta) && int($delta) > 0;
741    my $shift = BarnOwl::recv_getshift();
742    if($shift > 0) {
743        BarnOwl::recv_setshift(max(0, $shift-$delta));
744    } else {
745        return "Already full left";
746    }
747}
748
749sub recv_shift_right
750{
751    my $func = shift;
752    my $delta = shift;
753    $delta = 10 unless defined($delta) && int($delta) > 0;
754    my $shift = BarnOwl::recv_getshift();
755    BarnOwl::recv_setshift($shift+$delta);
756}
757
758=head3 default_zephyr_signature
759
760Compute the default zephyr signature.
761
762=cut
763
764sub default_zephyr_signature
765{
766  my $zsig = getvar('zsig');
767  if (!defined($zsig) || $zsig eq '') {
768      my $zsigproc = getvar('zsigproc');
769      if (defined($zsigproc) && $zsigproc ne '') {
770          $zsig = `$zsigproc`;
771      } elsif (!defined($zsig = get_zephyr_variable('zwrite-signature'))) {
772          $zsig = ((getpwuid($<))[6]);
773          $zsig =~ s/,.*//;
774      }
775  }
776  chomp($zsig);
777  return $zsig;
778}
779
780=head3 random_zephyr_signature
781
782Retrieve a random line from ~/.zsigs (except those beginning with '#')
783and use it as the zephyr signature.
784
785=cut
786
787sub random_zephyr_signature
788{
789    my $zsigfile = "$ENV{'HOME'}/.zsigs";
790    open my $file, '<', $zsigfile or die "Error opening file $zsigfile: $!";
791    my @lines = grep !(/^#/ || /^\s*$/), <$file>;
792    close $file;
793    return '' if !@lines;
794    my $zsig = "$lines[int(rand(scalar @lines))]";
795    chomp $zsig;
796    return $zsig;
797}
798
799=head3 register_idle_watcher %ARGS
800
801Call a callback whenever the amount of time the user becomes idle or comes
802back from being idle.
803
804You must include the following parameters:
805
806=over 4
807
808=item name
809
810The name given to the idle watcher
811
812=item after
813
814How long the user must be idle, in seconds, before the callback is called.
815If the value is too small, you may have spurious or inaccurate calls.
816(The current lower limit is about 1 second.)
817
818=item callback
819
820The Perl subroutine that gets called when the user has been idle for C<AFTER>
821seconds, or comes back from being idle.  The subroutine is passed one parameter,
822which is true if the user is currently idle, and false otherwise.
823
824=back
825
826This method returns a unique identifier which may be passed to
827L<BarnOwl::unregister_idle_watcher>.
828
829=cut
830
831=head3 unregister_idle_watcher UNIQUE_ID [...]
832
833Removed and returns the idle watcher specified by C<UNIQUE_ID>.
834You may specify multiple unique ids.
835
836=cut
837
838my %idle_watchers;
839tie %idle_watchers, 'Tie::RefHash';
840
841$BarnOwl::Hooks::wakeup->add(sub {
842        foreach my $idle_watcher (values %idle_watchers) {
843            _wakeup_idle_watcher($idle_watcher);
844        }
845    });
846
847sub _wakeup_idle_watcher {
848    my ($idle_watcher, $offset) = @_;
849    $offset = 0 unless defined $offset;
850    # go unidle
851    $idle_watcher->{idle_timer}->stop if $idle_watcher->{idle_timer};
852    undef $idle_watcher->{idle_timer};
853    $idle_watcher->{callback}->(0) if $idle_watcher->{is_idle};
854    $idle_watcher->{is_idle} = 0;
855
856    # queue going idle
857    $idle_watcher->{idle_timer} = BarnOwl::Timer->new({
858        name  => $idle_watcher->{name},
859        after => $idle_watcher->{after} - $offset,
860        cb    => sub {
861            $idle_watcher->{is_idle} = 1;
862            $idle_watcher->{callback}->(1);
863        }
864    });
865}
866
867sub register_idle_watcher {
868    my %args = (@_);
869    $idle_watchers{\%args} = \%args;
870    _wakeup_idle_watcher(\%args, BarnOwl::getidletime); # make sure to queue up the idle/unidle events from this idle watcher
871    return \%args;
872}
873
874sub unregister_idle_watcher {
875    my ($id) = @_;
876    $idle_watchers{$id}->{idle_timer}->stop if $idle_watchers{$id}->{idle_timer};
877    return delete $idle_watchers{$id};
878}
879
880# Stub for owl::startup / BarnOwl::startup, so it isn't bound to the
881# startup command. This may be redefined in a user's configfile.
882sub startup
883{
884}
885
8861;
Note: See TracBrowser for help on using the repository browser.