File Coverage

blib/lib/IO/Async/Loop/Glib.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2007-2013 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Loop::Glib;
7              
8 2     2   3431 use strict;
  2         6  
  2         254  
9 2     2   13 use warnings;
  2         5  
  2         128  
10              
11             our $VERSION = '0.21';
12 2     2   22 use constant API_VERSION => '0.49';
  2         4  
  2         163  
13              
14 2     2   11 use base qw( IO::Async::Loop );
  2         4  
  2         3786  
15             IO::Async::Loop->VERSION( '0.49' );
16              
17 2     2   124077 use Carp;
  2         5  
  2         193  
18              
19 2     2   1007 use Glib;
  0            
  0            
20              
21             =head1 NAME
22              
23             C - use C with F or F
24              
25             =head1 SYNOPSIS
26              
27             use IO::Async::Loop::Glib;
28              
29             my $loop = IO::Async::Loop::Glib->new();
30              
31             $loop->add( ... );
32              
33             ...
34             # Rest of GLib/Gtk program that uses GLib
35              
36             Glib::MainLoop->new->run();
37              
38             Or
39              
40             $loop->loop_forever();
41              
42             Or
43              
44             while(1) {
45             $loop->loop_once();
46             }
47              
48             =head1 DESCRIPTION
49              
50             This subclass of C uses the C to perform
51             read-ready and write-ready tests.
52              
53             The appropriate C sources are added or removed from the
54             C when notifiers are added or removed from the set, or when
55             they change their C status. The callbacks are called
56             automatically by Glib itself; no special methods on this loop object are
57             required.
58              
59             =cut
60              
61             =head1 CONSTRUCTOR
62              
63             =cut
64              
65             =head2 $loop = IO::Async::Loop::Glib->new()
66              
67             This function returns a new instance of a C object. It
68             takes no special arguments.
69              
70             =cut
71              
72             sub new
73             {
74             my $class = shift;
75             my ( %args ) = @_;
76              
77             my $self = $class->__new( %args );
78              
79             $self->{sourceid} = {}; # {$fd} -> [ $readid, $writeid, $hangupid ]
80              
81             return $self;
82             }
83              
84             sub __new_feature
85             {
86             my $self = shift;
87             my ( $classname ) = @_;
88              
89             # veto IO::Async::TimeQueue since we implement its methods locally
90             die __PACKAGE__." implements $classname internally"
91             if grep { $_ eq $classname } qw( IO::Async::TimeQueue );
92              
93             return $self->SUPER::__new_feature( $classname );
94             }
95              
96             =head1 METHODS
97              
98             There are no special methods in this subclass, other than those provided by
99             the C base class.
100              
101             =cut
102              
103             sub watch_io
104             {
105             my $self = shift;
106             my %params = @_;
107              
108             my $handle = $params{handle} or croak "Expected 'handle'";
109             my $fd = $handle->fileno;
110              
111             # TODO: Investigate if the following can be made more efficient by
112             # installing just one source on all the masks, and detecting the particular
113             # event bits within the callback
114              
115             my $sourceids = ( $self->{sourceid}->{$fd} ||= [] );
116              
117             if( my $on_read_ready = $params{on_read_ready} ) {
118             Glib::Source->remove( $sourceids->[0] ) if defined $sourceids->[0];
119              
120             $sourceids->[0] = Glib::IO->add_watch( $fd,
121             ['in', 'hup', 'err'],
122             sub {
123             $on_read_ready->();
124             # Must yield true value or else GLib will remove this IO source
125             return 1;
126             }
127             );
128             }
129              
130             if( my $on_write_ready = $params{on_write_ready} ) {
131             Glib::Source->remove( $sourceids->[1] ) if defined $sourceids->[1];
132              
133             $sourceids->[1] = Glib::IO->add_watch( $fd,
134             ['out', 'hup', 'err'],
135             sub {
136             $on_write_ready->();
137             # Must yield true value or else GLib will remove this IO source
138             return 1;
139             }
140             );
141             }
142              
143             if( my $on_hangup = $params{on_hangup} ) {
144             $self->_CAN_ON_HANGUP or croak "Cannot watch_io for 'on_hangup' in ".ref($self);
145              
146             Glib::Source->remove( $sourceids->[2] ) if defined $sourceids->[2];
147              
148             $sourceids->[2] = Glib::IO->add_watch( $fd,
149             ['hup'],
150             sub {
151             $on_hangup->();
152             # Must yield true value or else GLib will remove this IO source
153             return 1;
154             }
155             );
156             }
157             }
158              
159             sub unwatch_io
160             {
161             my $self = shift;
162             my %params = @_;
163              
164             my $handle = $params{handle} or croak "Expected 'handle'";
165             my $fd = $handle->fileno;
166              
167             my $sourceids = $self->{sourceid}->{$fd} or return;
168              
169             if( $params{on_read_ready} ) {
170             Glib::Source->remove( $sourceids->[0] ) if defined $sourceids->[0];
171             undef $sourceids->[0];
172             }
173              
174             if( $params{on_write_ready} ) {
175             Glib::Source->remove( $sourceids->[1] ) if defined $sourceids->[1];
176             undef $sourceids->[1];
177             }
178              
179             if( $params{on_hangup} ) {
180             $self->_CAN_ON_HANGUP or croak "Cannot watch_io for 'on_hangup' in ".ref($self);
181              
182             Glib::Source->remove( $sourceids->[2] ) if defined $sourceids->[2];
183             undef $sourceids->[2];
184             }
185              
186             delete $self->{sourceids}->{$fd} if not $sourceids->[0] and not $sourceids->[1] and not $sourceids->[2];
187             }
188              
189             sub watch_time
190             {
191             my $self = shift;
192             my ( %params ) = @_;
193              
194             # Just let GLib handle all these timer events
195             my $delay;
196             if( exists $params{at} ) {
197             my $now = exists $params{now} ? $params{now} : $self->time;
198              
199             $delay = delete($params{at}) - $now;
200             }
201             elsif( exists $params{after} ) {
202             $delay = delete $params{after};
203             }
204             else {
205             croak "Expected either 'at' or 'after' keys";
206             }
207              
208             my $interval = $delay * 1000; # miliseconds
209             $interval = 0 if $interval < 0; # clamp or Glib gets upset
210              
211             my $code = delete $params{code};
212             ref $code eq "CODE" or croak "Expected 'code' to be a CODE reference";
213              
214             my $id;
215              
216             my $callback = sub {
217             $code->();
218             return 0;
219             };
220              
221             return Glib::Timeout->add( $interval, $callback );
222             }
223              
224             sub unwatch_time
225             {
226             my $self = shift;
227             my ( $id ) = @_;
228              
229             Glib::Source->remove( $id );
230              
231             return;
232             }
233              
234             sub watch_child
235             {
236             my $self = shift;
237             my ( $pid, $code ) = @_;
238              
239             if( $pid == 0 ) {
240             return $self->SUPER::watch_child( @_ );
241             }
242              
243             my $childwatches = $self->{childwatches};
244              
245             $childwatches->{$pid} = Glib::Child->watch_add( $pid,
246             sub {
247             $code->( $_[0], $_[1] );
248             delete $childwatches->{$pid};
249             return 0;
250             },
251             );
252             }
253              
254             sub unwatch_child
255             {
256             my $self = shift;
257             my ( $pid ) = @_;
258              
259             if( $pid == 0 ) {
260             return $self->SUPER::unwatch_child( @_ );
261             }
262              
263             Glib::Source->remove( delete $self->{childwatches}{$pid} );
264             }
265              
266             sub watch_idle
267             {
268             my $self = shift;
269             my %params = @_;
270              
271             my $code = delete $params{code};
272             ref $code eq "CODE" or croak "Expected 'code' to be a CODE reference";
273              
274             my $when = delete $params{when} or croak "Expected 'when'";
275             $when eq "later" or croak "Expected 'when' to be 'later'";
276              
277             return Glib::Idle->add( sub { $code->(); return 0 } );
278             }
279              
280             sub unwatch_idle
281             {
282             my $self = shift;
283             my ( $id ) = @_;
284              
285             Glib::Source->remove( $id );
286             }
287              
288             =head2 $loop->loop_once( $timeout )
289              
290             This method calls the C method on the underlying
291             C. If a timeout value is supplied, then a Glib timeout
292             will be installed, to interrupt the loop at that time. If Glib indicates that
293             any callbacks were fired, then this method will return 1 (however, it does not
294             mean that any C callbacks were invoked, as there may be other parts
295             of code sharing the Glib main context. Otherwise, it will return 0.
296              
297             =cut
298              
299             sub loop_once
300             {
301             my $self = shift;
302             my ( $timeout ) = @_;
303              
304             $self->_adjust_timeout( \$timeout, no_sigwait => 1 );
305              
306             my $timed_out = 0;
307              
308             my $timerid;
309             if( defined $timeout ) {
310             my $interval = $timeout * 1000; # miliseconds
311             $timerid = Glib::Timeout->add( $interval, sub { $timed_out = 1; return 0; } );
312             }
313              
314             my $context = Glib::MainContext->default;
315             1 until $context->iteration( 1 );
316              
317             if( defined $timerid ) {
318             Glib::Source->remove( $timerid ) unless $timed_out;
319             }
320             }
321              
322             sub loop_forever
323             {
324             my $self = shift;
325              
326             my $mainloop = $self->{mainloop} = Glib::MainLoop->new();
327             $mainloop->run;
328              
329             undef $self->{mainloop};
330             }
331              
332             sub loop_stop
333             {
334             my $self = shift;
335            
336             $self->{mainloop}->quit;
337             }
338              
339             =head1 SEE ALSO
340              
341             =over 4
342              
343             =item *
344              
345             L - Perl wrappers for the GLib utility and Object libraries
346              
347             =item *
348              
349             L - Perl interface to the 2.x series of the Gimp Toolkit library
350              
351             =back
352              
353             =head1 AUTHOR
354              
355             Paul Evans
356              
357             =cut
358              
359             0x55AA;