File Coverage

lib/Mojo/Reactor/Glib.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Mojo::Reactor::Glib;
2 1     1   17587 use Mojo::Base 'Mojo::Reactor';
  1         7229  
  1         7  
3              
4 1     1   50119 use strict;
  1         1  
  1         23  
5 1     1   4 use warnings;
  1         6  
  1         22  
6              
7 1     1   247 use Glib;
  0            
  0            
8              
9             =head1 NAME
10              
11             Mojo::Reactor::Glib - Glib::MainLoop backend for Mojo
12              
13             =head1 VERSION
14              
15             Version 0.001
16              
17             I hope I need not to emphasise that this is in VERY EARLY STAGES OF DEVELOPMENT.
18              
19             =cut
20              
21             our $VERSION = '0.001';
22              
23             =head1 SYNOPSIS
24              
25             B is a backend for L, build on top of the L main loop,
26             allowing you to use various Mojo(licious) modules within a Glib or Gtk program.
27              
28             BEGIN {
29             $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Glib';
30             }
31              
32             use Gtk2 -init;
33             use Mojo::UserAgent;
34              
35             my $ua = Mojo::UserAgent->new();
36              
37             Glib::Timeout->add(1000, sub {
38             $ua->get('http://example.com/' => sub {
39             my ($ua, $tx) = @_;
40             say $tx->res->body;
41             });
42             return Glib::SOURCE_CONTINUE;
43             });
44              
45             Gtk2->main();
46              
47             =cut
48              
49             my $Glib;
50             $ENV{MOJO_REACTOR} ||= 'Mojo::Reactor::Glib';
51              
52             sub CLONE {
53             die "We don't work with ithreads.\n"; ## To be honest, I don't know, don't care, don't want.
54             }
55              
56             sub DESTROY {
57             undef $Glib;
58             }
59              
60             sub new {
61             my $class = shift;
62              
63             if ($Glib) {
64             return $Glib;
65             } else {
66             my $r = {
67             loop => undef,
68             timers => {},
69             io => {},
70             };
71             bless($r, $class);
72             $Glib = $r;
73             return $r;
74             }
75             }
76              
77             #####
78              
79             =head1 METHODS
80              
81             =head2 $r->again($id) X
82              
83             Runs the timer known by C<$id> again
84              
85             =cut
86             sub again {
87             my $r = shift;
88             my ($id) = @_;
89              
90             if (my $s = $r->{timers}->{$id}) {
91             $r->_timer($s->{recurring}, $s->{after_sec}, $s->{cb});
92             }
93             }
94              
95             =head2 $r->io($handle, $cb) X
96              
97             Assigns a callback function C<$cb> to the IO handle C<$handle>. This is required before you can use L.
98              
99             Returns the reactor C<$r> so you can chain the calls.
100              
101             =cut
102             sub io {
103             my $r = shift;
104             my ($handle, $cb) = @_;
105              
106             my $fd = fileno($handle);
107             $r->{io}->{$fd} //= {};
108             $r->{io}->{$fd}->{handle} = $handle;
109             $r->{io}->{$fd}->{cb} = $cb;
110              
111             return $r;
112             }
113              
114             =head2 $r->is_running() X
115              
116             Returns true if the loop is running, otherwise returns false
117              
118             =cut
119             sub is_running {
120             my $r = shift;
121             return ($r->{loop} ? $r->{loop}->is_running() : 0);
122             }
123              
124             =head2 $r->one_tick() X
125              
126             Does a single L iteration. Returns true if events were dispatched during this iteration
127             (whether or not they had been B events), false if nothing happened.
128              
129             =cut
130             sub one_tick {
131             my $r = shift;
132              
133             if ($r->{loop}) {
134             my $ctx = $r->{loop}->get_context();
135             $ctx && $ctx->iteration(Glib::FALSE);
136             }
137             }
138              
139             =head2 $r->recurring($after_sec, $cb) X
140              
141             Starts a recurring timer that beats every C<$after_sec> seconds (N.B. Glib allows for millisecond granularity),
142             which will result in C<$cb> being fired.
143              
144             Returns the B ID that you can use to L the timer.
145              
146             See also L
147              
148             =cut
149             sub recurring {
150             my $r = shift;
151              
152             $r->_timer(Glib::SOURCE_CONTINUE, @_);
153             }
154              
155             =head2 $r->remove($id) X
156              
157             Removes the timer identified by C<$id>, returning true if this was successful, and a false-ish value otherwise.
158              
159             =cut
160             sub remove {
161             my $r = shift;
162             my ($id) = @_;
163              
164             my $removed;
165             if (exists $r->{timers}->{$id}) {
166             $removed = Glib::Source->remove($id);
167             delete $r->{timers}->{$id};
168             }
169              
170             return $removed;
171             }
172              
173             =head2 $r->reset() X
174              
175             Stops all timers and watches.
176              
177             =cut
178             sub reset {
179             my $r = shift;
180              
181             for my $id (keys %{$r->{timers}}) {
182             Glib::Source->remove($id);
183             delete $r->{timers}->{$id};
184             }
185             for my $fd (keys %{$r->{io}}) {
186             my $handle = $r->{io}->{$fd}->{handle};
187             $r->watch($handle, 0, 0);
188             }
189             }
190              
191             =head2 $r->start() X
192              
193             Starts the loop if it isn't already running.
194              
195             =cut
196             sub start {
197             my $r = shift;
198              
199             if (not $r->{loop}) {
200             $r->{loop} = Glib::MainLoop->new(undef, Glib::FALSE);
201             }
202              
203             if ($r->{loop} and not $r->{loop}->is_running()) {
204             $r->{loop}->run();
205             }
206             }
207              
208             =head2 $r->stop() X
209              
210             Stops the loop.
211              
212             =cut
213             sub stop {
214             my $r = shift;
215              
216             if ($r->{loop}) {
217             $r->{loop}->quit();
218             }
219             }
220              
221             =head2 $r->timer($after_sec, $cb) X
222              
223             Starts a one-shot timer that beats after C<$after_sec> seconds (N.B. Glib allows for millisecond granularity),
224             which will result in C<$cb> being fired.
225              
226             Returns the B ID that you can use to L or L the timer.
227              
228             See also L.
229              
230             =cut
231             sub timer {
232             my $r = shift;
233              
234             $r->_timer(Glib::SOURCE_REMOVE, @_);
235             }
236              
237             =head2 $r->watch($handle, $read, $write) X
238              
239             Adds an IO watch for C<$read> or C<$write> (booleans) on C<$handle>. If both C<$read>
240             and C<$write> are false, it removes the watch.
241              
242             Requires L to be run on C<$handle> first, as that associates the callback function with the handle.
243              
244             See also L.
245              
246             =cut
247             sub watch {
248             my $r = shift;
249             my ($handle, $read, $write) = @_;
250              
251             my $fd = fileno($handle);
252             my $io = $r->{io}->{$fd};
253              
254             if (not defined $io) {
255             return; ## Croak?
256             }
257              
258             if ($io->{id}) {
259             Glib::Source->remove($io->{id});
260             }
261              
262             if ($read or $write) {
263             my $watchlist = [
264             'hup', 'err',
265             ($read ? 'in' : ()),
266             ($write ? 'out' : ()),
267             ];
268             my $id = Glib::IO->add_watch($fd, $watchlist, sub {
269             my ($fd, $iocondition) = @_;
270             $io->{cb}->($r, $iocondition eq 'out' ? 1 : 0);
271              
272             return Glib::SOURCE_CONTINUE;
273             });
274             $io->{id} = $id;
275             }
276              
277             return $r;
278             }
279              
280              
281             sub _timer {
282             my $r = shift;
283             my ($recurring, $after_sec, $cb) = @_;
284              
285             $after_sec = 0 if $after_sec < 0;
286             my $after_ms = int($after_sec * 1000);
287              
288             my $id = Glib::Timeout->add($after_ms, sub {
289             $cb->();
290             return $recurring;
291             });
292              
293             $r->{timers}->{$id} = {
294             recurring => $recurring,
295             after_sec => $after_sec,
296             cb => $cb,
297             };
298              
299             return $id;
300             }
301              
302             =head1 AUTHOR
303              
304             Ralesk C<< >>
305              
306             =head1 BUGS
307              
308             Please report issues at L
309              
310             =head2 KNOWN ISSUES
311              
312             =over
313              
314             =item *
315              
316             Breaks on Win32, Mojo::UserAgent can't do non-blocking calls at least.
317              
318             =item *
319              
320             Can't latch onto an existing Gtk event loop (no API for that), not sure if we actually should be able to or if we're good here in a sub-loop.
321              
322             =back
323              
324             =head1 LICENCE AND COPYRIGHT
325              
326             Copyright (C) 2014 Henrik Pauli
327              
328             This program is free software; you can redistribute it and/or modify it
329             under the terms of the Artistic License (2.0). You may obtain a
330             copy of the full licence at:
331              
332             L
333              
334             =cut
335              
336             1; # End of Mojo::Reactor::Glib