File Coverage

blib/lib/Wily/RPC.pm
Criterion Covered Total %
statement 24 142 16.9
branch 0 28 0.0
condition n/a
subroutine 8 28 28.5
pod 2 19 10.5
total 34 217 15.6


line stmt bran cond sub pod time code
1             package Wily::RPC;
2              
3             =head1 NAME
4              
5             Wily::RPC - Perl extension for the Wily RPC interface
6              
7             =head1 SYNOPSIS
8              
9             use Wily::RPC;
10             use Wily::Message;
11              
12             # opens a file in wily and exits when the window is destroyed
13              
14             my $wily = Wily::RPC->new();
15             my $win = $wily->win('/tmp/file_to_edit', 1);
16             $wily->attach($win, Wily::Message::WEdestroy);
17             while (my $event = $wily->event()) {
18             if ($event->{type} == Wily::Message::WEdestroy and
19             $event->{window_id} == $win) {
20             last;
21             }
22             }
23              
24             =head1 DESCRIPTION
25              
26             Provides an interface to the Wily, using the lower level Wily::Message
27             and Wily::Connect packages (which can also be used without this wrapper).
28              
29             Most of the methods of the Wily::RPC may block for a short time, they write
30             a message to wily over a unix domain socket and then wait for wily to
31             write a response message. Wily responds quickly, but if such things matter
32             you will have to use the lower level packages instead.
33              
34             =cut
35              
36 1     1   10 use v5.8;
  1         3  
  1         38  
37 1     1   4 use strict;
  1         2  
  1         27  
38 1     1   6 use warnings;
  1         1  
  1         19  
39 1     1   4 use Carp;
  1         1  
  1         91  
40              
41 1     1   472 use Wily::Message;
  1         4  
  1         49  
42 1     1   670 use Wily::Connect;
  1         5  
  1         34  
43 1     1   6 use File::Temp qw/ :POSIX /;
  1         2  
  1         136  
44 1     1   5 use IO::Socket;
  1         3  
  1         6  
45              
46             our $VERSION = '0.01';
47              
48             =head2 Connecting To Wily
49              
50             $wily = Wily::RPC->new();
51              
52             Connects to wily and returns a Wily::RPC object reference.
53              
54             =cut
55             sub new {
56 0     0 0   my $package = shift;
57 0           my $self = {};
58 0           $self->{s} = Wily::Connect::connect();
59 0           $self->{buffer} = '';
60 0           $self->{events} = [];
61 0           $self->{replies} = {};
62 0           return bless $self, $package;
63             }
64              
65             =head2 Checking for events
66              
67             unless ($wily->would_block()) {
68             # retrieve event...
69             }
70              
71             The would_block() method returns true if a call to the event() method
72             will block or false if there is an event waiting to be retrieved.
73              
74             =cut
75             sub would_block {
76 0     0 0   my $self = shift;
77 0           return scalar @{$self->{events}}
  0            
78             }
79             =head2 Retrieving an event
80              
81             $event = $wily->event();
82              
83             The event() method returns a Wily::Message object representing
84             an event wily has sent. This method will block until an event
85             arrives (and if no events have been attached to, will thus block
86             for a very long time...).
87              
88             Returns undef if the connection to Wily drops or errors.
89              
90             =cut
91             sub event {
92 0     0 1   my $self = shift;
93 0           while (not @{$self->{events}}) {
  0            
94 0 0         $self->read_socket() or return;
95             }
96 0           return pop @{$self->{events}};
  0            
97             }
98              
99             =head2 Bouncing an event
100              
101             $wily->bounce($event);
102              
103             The bounce() method sends an event back to wily for the standard
104             wily handling. This is commonly used with exec events, for example,
105             when your program only cares about the commands it understands and
106             bounces back the exec events it doesn't understand for wily to handle.
107              
108             =cut
109             sub bounce {
110 0     0 0   my $self = shift;
111 0           my $msg = shift;
112 0 0         if ($self->{type} > Wily::Message::WEfencepost) {
113 0           return;
114             }
115 0           my $flat = $msg->flatten();
116 0 0         if ($self->{s}->syswrite($flat) != length($flat)) {
117 0           $self->{s}->close();
118 0           croak "Write to wily failed";
119             }
120 0           return 1;
121             }
122              
123             =head2 Listing existing windows
124              
125             $window_list = $wily->list();
126              
127             The list() method returns a string that contains a line for each window
128             open in wily. Each line consists of the window name followed by whitespace
129             and the window ID number (which is used to specify a window in many other
130             methods).
131              
132             On failure (I'm not sure how a list message could fail) undef is returned
133             and the error message placed in $wily->{error}.
134              
135             =cut
136             sub list {
137 0     0 0   my $self = shift;
138 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMlist));
139 0 0         if ($msg->{type} == Wily::Message::WRerror) {
140 0           $self->{error} = $msg;
141 0           return;
142             }
143 0           return $msg->{s};
144             }
145              
146             =head2 Getting the supported wily features
147              
148             $features = $wily->features();
149              
150             The features() method returns a string containing a whitespace seperated list of
151             features that the wily instance supports. Note, that not all wily instances support
152             this, and will result in an error (in which case only the standard wily messages
153             are supported).
154              
155             On failure undef is returned and the error message placed in $wily->{error}.
156              
157             =cut
158             sub features {
159 0     0 1   my $self = shift;
160 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMgetfeatures));
161 0 0         if ($msg->{type} == Wily::Message::WRerror) {
162 0           $self->{error} = $msg;
163 0           return;
164             }
165 0           return $msg->{s};
166             }
167              
168             =head2 Creating a window
169              
170             $win_id = $wily->win($name $backup)
171              
172             The win() method causes wily to open a window with the pathname set to $name and returns the ID
173             number of that window. If $backup is 1 then
174             wily will keep backups for the window and enable the dirty indicator. If a window with the same
175             name already exists then the value of $backup is ignored and the ID number of the existing
176             window is returned.
177              
178             On failure undef is returned and the error message placed in $wily->{error}.
179              
180             =cut
181             sub win {
182 0     0 0   my $self = shift;
183 0           my ($name, $backup) = @_;
184 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMnew, 0, 0, 0, $backup, $name));
185 0 0         if ($msg->{type} == Wily::Message::WRerror) {
186 0           $self->{error} = $msg;
187 0           return;
188             }
189 0           return $msg->{window_id};
190             }
191              
192             =head2 Reading window text
193              
194             $text = $wily->read($win_id, $p0, $p1);
195              
196             The read() method returns the text in the character range [$p0, $p1) of the specified
197             window. Note, that the text includes the character at $p0 but does not include the
198             character at $p1.
199              
200             On failure undef is returned and the error message placed in $wily->{error}.
201              
202             =cut
203             sub read {
204 0     0 0   my $self = shift;
205 0           my ($window, $p0, $p1) = @_;
206 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMread, $window, $p0, $p1));
207 0 0         if ($msg->{type} == Wily::Message::WRerror) {
208 0           $self->{error} = $msg;
209 0           return;
210             }
211 0           return $msg->{s};
212             }
213              
214             =head2 Searching
215              
216             ($win_id, $r0, $r1) = $wily->goto($window_id, $p0, $p1, $search, $set_dot);
217              
218             The goto() method causes wily to act as if the user had selected the text $search with
219             B3 in the window with ID number $window_id. If this results in a search then the search starts
220             from the position indicated by the range [$p0, $p1) - if $p0 > $p1 then the search starts
221             from the current selection. If $set_dot is 1 then wily will select the resulting selection and
222             warp the mouse cursor to it. Returns the window ID number and the range in that window found
223             by the search. This may be a different window (if the search text was a file name, for example).
224              
225             $search can be plain text to search for or an address that wily understands, or a wily
226             regular expression search - anything which works when B3ed.
227              
228             On failure () is returned and the error message placed in $wily->{error}.
229              
230             =cut
231             sub goto {
232 0     0 0   my $self = shift;
233 0           my ($window, $p0, $p1, $s, $set_dot) = @_;
234 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMgoto, $window, $p0, $p1, $set_dot, $s));
235 0 0         if ($msg->{type} == Wily::Message::WRerror) {
236 0           $self->{error} = $msg;
237 0           return;
238             }
239 0           return ($msg->{window_id}, $msg->{p0}, $msg->{p1});
240             }
241              
242             =head2 Getting the window name
243              
244             $name = $wily->get_name($win_id);
245              
246             The get_name() method returns the name of the window with the specified ID number.
247              
248             On failure undef is returned and the error message placed in $wily->{error}.
249              
250             =cut
251             sub get_name {
252 0     0 0   my $self = shift;
253 0           my ($window) = @_;
254 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMgetname, $window));
255 0 0         if ($msg->{type} == Wily::Message::WRerror) {
256 0           $self->{error} = $msg;
257 0           return;
258             }
259 0           return $msg->{s};
260             }
261              
262             =head2 Getting the window tools
263              
264             $tools = $wily->get_tools($win_id);
265              
266             The get_tools() method returns the text of the tools in the tag of the specified window.
267              
268             On faiure undef is returned and the error message placed in $wily->{error}.
269              
270             =cut
271             sub get_tools {
272 0     0 0   my $self = shift;
273 0           my ($window) = @_;
274 0           my $msg = $self->send(Wily::Message->new(Wily::Message::WMgettools, $window));
275 0 0         if ($msg->{type} == Wily::Message::WRerror) {
276 0           $self->{error} = $msg;
277 0           return;
278             }
279 0           return $msg->{s};
280             }
281              
282             =head2 Replacing text
283              
284             $wily->replace($win_id, $p0, $p1, $text);
285            
286             The replace() method replaces the text in the range [$p0, $p1) in the window with ID number
287             $win_id with $text. A true value is returned upon success. If $p0==$p1 the text is inserted
288             at position $p0.
289              
290             On failure undef is returned and the error message placed in $wily->{error}.
291              
292             =cut
293             sub replace {
294 0     0 0   my $self = shift;
295 0           my ($window, $p0, $p1, $s) = @_;
296 0           return $self->_simple_send(Wily::Message::WMreplace, $window, $p0, $p1, 0, $s);
297             }
298              
299             =head2 Executing commands
300              
301             $wily->execute($win_id, $cmd);
302              
303             The execute() method causes wily to act as if $cmd was selected with B2 in the window with
304             ID number $win_id. Returns 1 upon success.
305              
306             On failure undef is returned and the error message placed in $wily->{error}.
307              
308             =cut
309             sub execute {
310 0     0 0   my $self = shift;
311 0           my ($window, $s) = @_;
312 0           return $self->_simple_send(Wily::Message::WMexec, $window, 0, 0, 0, $s);
313             }
314              
315             =head2 Attaching
316              
317             $wily->attach($win_id, $mask);
318              
319             The attach() method causes wily to send the requested events for the specified window
320             to the program. $mask should be a bitwise or (or just a sum) of the WE* constants
321             in the Wily::Message package. If 'detach' is in the list of features returned by
322             the features() method then attach() can be called multiple times to recieve additional
323             event types.
324              
325             WEexec and WEgoto events are sent to the client vefore they are processed by wily, if
326             you want wily to process them they need to be bounce()d back to wily.
327              
328             The events will be made available via the event() method.
329              
330             Returns 1 on success, on failure undef is returned and the error message is placed in
331             $wily->{error}.
332              
333             =cut
334             sub attach {
335 0     0 0   my $self = shift;
336 0           my ($window, $mask) = @_;
337 0           return $self->_simple_send(Wily::Message::WMattach, $window, 0, 0, $mask);
338             }
339              
340             =head2 Detaching
341              
342             $wily->detach($win_id, $mask);
343              
344             The detach() method causes wily to stop sending the specified events for the specified window
345             to the program. $mask should be a bitwise or (or just a sum) of the WE* constants
346             in the Wily::Message package.
347              
348             Note, that this is not part of the "standard" wily message set and hence you should
349             make sure to handle a failure when dealing with wily instances that don't support
350             this function. Wily instances that do support this function will include 'detach' in
351             the feature list returned by the features() method.
352              
353             Returns 1 on success, on failure undef is returned and the error message is placed in
354             $wily->{error}.
355              
356             =cut
357             sub detach {
358 0     0 0   my $self = shift;
359 0           my ($window, $mask) = @_;
360 0           return $self->_simple_send(Wily::Message::WMdetach, $window, 0, 0, $mask);
361             }
362              
363             =head2 Setting the window name
364              
365             $wily->set_name($win_id, $name);
366              
367             The set_name() method sets the name of the specified window to $name.
368              
369             Returns 1 on success, on failure undef is returned and the error message is placed in
370             $wily->{error}.
371              
372             =cut
373             sub set_name {
374 0     0 0   my $self = shift;
375 0           my ($window, $name) = @_;
376 0           return $self->_simple_send(Wily::Message::WMsetname, $window, 0, 0, 0, $name);
377             }
378              
379             =head2 Setting the window tools
380              
381             $wily->set_tools($win_id, $tools);
382              
383             The set_tools() method sets the tools in the tag of the specified window to $tools.
384              
385             Returns 1 on success, on failure undef is returned and the error message is placed in
386             $wily->{error}.
387              
388             =cut
389             sub set_tools {
390 0     0 0   my $self = shift;
391 0           my ($window, $tools) = @_;
392 0           return $self->_simple_send(Wily::Message::WMsettools, $window, 0, 0, 0, $tools);
393             }
394              
395             sub _simple_send {
396 0     0     my $self = shift;
397 0           my $msg = $self->send(Wily::Message->new(@_));
398 0 0         if ($msg->{type} == Wily::Message::WRerror) {
399 0           $self->{error} = $msg;
400 0           return;
401             }
402 0           return 1;
403             }
404              
405             =head2 Sending a message
406              
407             $result = $wily->send($msg);
408              
409             The send() method is passed a Wily::Message object which is sent to wily. The response
410             message that will be sent by wily is then returned.
411              
412             This method is not usually used, but could be useful if you wish to send a message
413             that has been added to wily but is not available through the other methods.
414              
415             =cut
416             sub send {
417 0     0 0   my $self = shift;
418 0           my $msg = shift;
419              
420 0           $msg->{message_id} = $self->{message_id}++;
421 0           my $flat = $msg->flatten();
422 0 0         if ($self->{s}->syswrite($flat) != length($flat)) {
423 0           $self->{s}->close();
424 0           croak "Write to wily failed";
425             }
426 0           while (not exists $self->{replies}{$msg->{message_id}}) {
427 0 0         $self->read_socket() or croak "Read from wily failed";
428             }
429 0           $msg = $self->{replies}{$msg->{message_id}};
430 0           delete $self->{replies}{$msg->{message_id}};
431 0           return $msg;
432             }
433              
434             =head2 Reading from wily
435              
436             $wily->read_socket()
437              
438             The read_socket() method will read from the connection to wily. This is necessary in
439             order to retrieve events that have been attach()ed for. This method is needed if you
440             wish to avoid extended blocking which can result when calling event(). The
441             socket can be accessed via $wily->{s} and then the perl select function (or some other
442             mechanism) used to determine that a read won't block, at which point read_socket() can
443             safely be called and the would_block() used to determine if a complete event was read.
444              
445             Note: this method will block if the socket does not have data ready (so check first
446             if that is an issue).
447              
448             =cut
449             sub read_socket {
450 0     0 0   my $self = shift;
451 0           my $res = $self->{s}->sysread($self->{buffer}, 128, length($self->{buffer}));
452 0           while (Wily::Message::complete_message($self->{buffer})) {
453 0           my $msg = Wily::Message->new(0);
454 0           $self->{buffer} = $msg->from_string($self->{buffer});
455 0 0         if ($msg->{type} < Wily::Message::WEfencepost) {
456 0           push @{$self->{events}}, $msg;
  0            
457             } else {
458 0           $self->{replies}{$msg->{message_id}} = $msg;
459             }
460             }
461 0           return $res;
462             }
463              
464             1;
465             __END__