File Coverage

blib/lib/Rofi/Script.pm
Criterion Covered Total %
statement 86 127 67.7
branch 12 24 50.0
condition 3 5 60.0
subroutine 22 34 64.7
pod 20 20 100.0
total 143 210 68.1


line stmt bran cond sub pod time code
1             package Rofi::Script;
2 1     1   193538 use strict;
  1         2  
  1         24  
3 1     1   5 use warnings;
  1         2  
  1         18  
4 1     1   10 use v5.10;
  1         3  
5              
6 1     1   4 use Carp qw( croak );
  1         1  
  1         47  
7 1     1   452 use Data::Printer;
  1         24762  
  1         6  
8 1         4 use Env qw(
9             $ROFI_RETV
10             $ROFI_INFO
11             $ROFI_SCRIPT_DEBUG
12 1     1   1190 );
  1         2134  
13              
14 1     1   175 use base 'Exporter';
  1         2  
  1         87  
15             our @EXPORT = qw(
16             rofi
17             );
18              
19 1     1   422 use namespace::autoclean;
  1         13794  
  1         3  
20              
21             =head1 NAME
22              
23             Rofi::Script - perl interface to the rofi menu
24              
25             =head1 DESCRIPTION
26              
27             rofi is a lightweight, extensible, scriptable, menu interface for Linux. It has
28             a scripting API documented in C. This module is a perl
29             interface to that API.
30              
31             =head1 SYNOPSIS
32              
33             use Rofi::Script;
34              
35             if (rofi->is_initial_call) {
36             rofi->set_prompt("Please select one")
37             ->add_option("Show markup example");
38             }
39              
40             SWITCH: for (rofi->shift_arg) {
41             next unless $_;
42              
43             /markup/ && rofi
44             ->set_prompt("markup")
45             ->enable_markup_rows
46             ->add_option(qq{You can use pango for markup});
47             }
48              
49             =cut
50              
51             my $DELIM = "\n";
52              
53             =head1 EXPORTED FUNCTIONS
54              
55             =head2 rofi
56              
57             rofi
58             ->set_prompt('Please choose')
59             ->add_option("Foo")
60             ->add_option("Bar");
61             if rofi->is_initial_call;
62              
63             if (my $cmd = rofi->shift_arg) {
64             $cmd =~ /foo/ && rofi
65             ->set_prompt("foo")
66             ->add_option("bar");
67             }
68              
69             ...etc
70              
71             This is a god object that's the primary interface to the entire script
72             interface. This object uses a fluid configuration style where you can chain
73             various methods and configuration options.
74              
75             With the standard initialization, the object returned by C will:
76              
77             =over
78              
79             =item
80              
81             Parse args from C<@ARGV>
82              
83             =item
84              
85             Print output to STDOUT (this is how the rofi app actually displays things)
86              
87             =back
88              
89             =cut
90              
91              
92             our $rofi;
93             sub rofi () {
94 25 100   25 1 7382 return $rofi if $rofi;
95              
96 6         22 my $init_state = {
97             args => \@ARGV,
98             output_rows => [],
99             mode_options => {},
100             show_handle => undef,
101             };
102              
103 6         16 $rofi = bless($init_state, __PACKAGE__);
104              
105 6         23 return $rofi;
106             }
107              
108             =head1 METHODS
109              
110             =head2 get_args
111              
112             Gets the arguments L is aware of.
113              
114             =cut
115              
116             sub get_args {
117 4     4 1 6 my ($self) = @_;
118 4         10 return $self->{args};
119             }
120              
121             =head2 set_args
122              
123             Setter for the args L cares about.
124              
125             =cut
126              
127             sub set_args {
128 3     3 1 6 my ($self, @args) = @_;
129              
130 3         5 $self->{args} = \@args;
131              
132 3         4 return $self;
133             }
134              
135             =head2 shift_arg
136              
137             my $cmd = rofi->shift_arg
138              
139             Shift the leading arg from the args queue. This is how you would navigate your
140             way through the rofi's "call stack"
141              
142             =cut
143              
144             sub shift_arg {
145 3     3 1 5 my ($self) = @_;
146              
147 3         4 my @args = @{$self->get_args};
  3         6  
148 3         4 my $arg = shift @args;
149              
150 3         7 $self->set_args(@args);
151              
152 3         10 return $arg;
153             }
154              
155             =head2 add_option
156              
157             rofi->add_option("Choice #1");
158              
159             Add a row to rofi's output. If you select a row, it will cause your script to
160             be re-called, with the selected row pushed onto the args stack.
161              
162             =cut
163              
164             sub add_option {
165 8     8 1 23 my ($self, $option, %mode_options) = @_;
166 8         8 my $what;
167 8         12 for (qw(urgent active)) {
168 16 50       27 $mode_options{$_} = 'true' if $mode_options{$_};
169             }
170              
171 8 100       14 if (%mode_options) {
172 1         3 $what = [
173             $option,
174             \%mode_options,
175             ];
176             } else {
177 7         7 $what = $option;
178             }
179 8         8 push @{$self->{output_rows}}, $what;
  8         17  
180              
181 8         13 return $self;
182             }
183              
184             =head2 show
185              
186             rofi->show;
187              
188             Renders the script output to whatever handle was set by L. By
189             default, this goes to STDOUT.
190              
191             =cut
192              
193             sub show {
194 3     3 1 4 my ($self) = @_;
195              
196 3         7 $self->_print_global_mode_options;
197              
198 3         4 my @output_rows = @{$self->{output_rows}};
  3         6  
199 3         5 for my $output_row (@output_rows) {
200 6         11 $self->_print_row($output_row);
201             }
202             }
203              
204             =head2 set_show_handle
205              
206             my $str = '';
207             open my $h, '>', $str;
208             rofi->set_show_handle($h);
209              
210             Set the handle that is printed to by L.
211              
212             =cut
213              
214             sub set_show_handle {
215 3     3 1 7 my ($self, $handle) = @_;
216              
217 3         4 $self->{show_handle} = $handle;
218              
219 3         4 return $self;
220             }
221              
222             =head2 get_show_handle
223              
224             my $h = rofi->get_show_handle;
225             close $h;
226              
227             Return the output handle used by L, set by L.
228              
229             =cut
230              
231             sub get_show_handle {
232 0     0 1 0 my ($self) = @_;
233              
234 0         0 return $self->{show_handle};
235             }
236              
237             =head2 is_initial_call
238              
239             True if this is the first time the script is being called
240              
241             =cut
242              
243 0     0 1 0 sub is_initial_call { $ROFI_RETV == 0 }
244              
245             =head2 provided_entry_selected
246              
247             The user selected a value from the list of provided entries
248              
249             =cut
250              
251 0     0 1 0 sub provided_entry_selected { $ROFI_RETV == 1 }
252              
253             =head2 custom_entry_selected
254              
255             User manually entered a value on the previous run
256              
257             =cut
258              
259 0     0 1 0 sub custom_entry_selected { $ROFI_RETV == 2 }
260              
261             =head2 set_prompt
262              
263             rofi->set_prompt("Please select a value");
264              
265             Set the prompt on the rofi popup
266              
267             =cut
268              
269             sub set_prompt {
270 2     2 1 5 my ($self, $prompt) = @_;
271 2 100       172 croak "Need prompt" unless $prompt;
272 1         5 $self->_set_mode_option(prompt => $prompt);
273              
274 1         2 return $self;
275             }
276              
277             =head2 set_message
278              
279             Set a message in the rofi box
280              
281             =cut
282              
283             sub set_message {
284 0     0 1 0 my ($self, $message) = @_;
285 0 0       0 croak "Need message" unless $message;
286 0         0 $self->_set_mode_option(message => $message);
287              
288 0         0 return $self;
289             }
290              
291             =head2 enable_markup_rows
292              
293             Turn on pango markup for rows
294              
295             =cut
296              
297             sub enable_markup_rows {
298 0     0 1 0 my ($self) = @_;
299              
300 0         0 $self->_set_mode_option(markup_rows => "true");
301              
302 0         0 return $self;
303             }
304              
305             =head2 disable_markup_rows
306              
307             Turn off pango markup for rows
308              
309             =cut
310              
311              
312             sub disable_markup_rows {
313 0     0 1 0 my ($self) = @_;
314              
315 0         0 $self->_set_mode_option(markup_rows => "false");
316              
317 0         0 return $self;
318             }
319              
320             =head2 markup_rows_enabled
321              
322             Query whether or not markup rows are enabled
323              
324             =cut
325              
326             sub markup_rows_enabled {
327 0     0 1 0 my ($self) = @_;
328 0         0 $self->_get_mode_option('markup_rows') eq 'true';
329             }
330              
331             =head2 set_delim
332              
333             Change the delimiter used to indicate new lines. This is C<\n> by default.
334             There's not really a need to mess with this. I'm not even sure it's implemented
335             100% correctly.
336              
337             =cut
338              
339             sub set_delim {
340 1     1 1 2 my ($self, $delim) = @_;
341              
342 1 50       3 croak "Need delim" unless $delim;
343 1         2 $DELIM = $delim;
344 1         3 $self->_set_mode_option(delim => $delim);
345              
346 1         2 return $self;
347             }
348              
349             =head2 set_no_custom
350              
351             Call this to ignore any custom entries from the user
352              
353             =cut
354              
355             sub set_no_custom {
356 0     0 1 0 my ($self, $set) = @_;
357              
358             croak "must be 'true' or 'false' (not $set)"
359 0 0       0 unless grep { $set eq $_ } qw( true false );
  0         0  
360              
361 0         0 $self->_set_mode_option(no_custom => $set);
362              
363 0         0 return $self;
364             }
365              
366             =head2 use_hot_keys
367              
368             Something to do with custom keybinds from the user. This isn't implemented.
369             I haven't needed it yet.
370              
371             =cut
372              
373             sub use_hot_keys {
374 0     0 1 0 my ($self, $set) = @_;
375 0         0 croak "use_hot_keys not yet implemented";
376             }
377              
378             sub _set_mode_option {
379 2     2   4 my ($self, $option, $value) = @_;
380              
381 2         5 $option =~ s/_/-/g;
382              
383 2         4 $self->{mode_options}->{$option} = $value;
384             }
385              
386             sub _get_mode_option {
387 0     0   0 my ($self, $option) = @_;
388              
389 0         0 $option =~ s/_/-/g;
390              
391 0         0 return $self->{mode_options}->{$option};
392             }
393              
394             sub _print {
395 8     8   10 my ($self, $whatever) = @_;
396 8   33     14 my $show_handle = $self->{show_handle} || *STDOUT;
397 8   100     16 my $delim = $self->{mode_options}->{delim} || "\n";
398 8         22 print $show_handle $whatever.$delim;
399             }
400              
401             sub _print_global_mode_options {
402 3     3   3 my ($self) = @_;
403              
404 3         5 $DB::single = 1;
405              
406 3         4 my %global_mode_options = %{$self->{mode_options}};
  3         9  
407              
408 3 100       7 return unless %global_mode_options;
409              
410 2         13 for my $opt (keys %global_mode_options) {
411 2         4 my $val = $global_mode_options{$opt};
412 2         4 $self->_print(
413             _render_option($opt => $val)
414             );
415             }
416             }
417              
418             sub _print_row {
419 6     6   7 my ($self, $row) = @_;
420              
421 6 50       13 if (ref $row eq 'ARRAY') {
    50          
422 0         0 my $content = $row->[0];
423 0         0 my %mode_options = %{$row->[1]};
  0         0  
424              
425 0         0 my @collected_mode_options;
426 0         0 for (my ($opt, $val) = each %mode_options) {
427 0         0 push @collected_mode_options, _render_option($opt => $val);
428             }
429 0         0 my $rendered_mode_options = join "\x1f", @collected_mode_options;
430              
431 0         0 $self->_print($content);
432 0         0 $self->_print($rendered_mode_options);
433             }
434              
435             elsif (not ref $row) {
436 6         10 $self->_print($row);
437             }
438              
439             else {
440 0         0 croak "unsupported output row type: " . ref($row);
441             }
442             }
443              
444             sub _render_option {
445 2     2   4 my ($option, $value) = @_;
446 2         7 return "\x00$option\x1f$value";
447             }
448              
449             =head2 debug
450              
451             rofi->debug
452              
453             Dump the contents of the L object to STDERR
454              
455             =cut
456              
457             sub debug {
458 0 0   0 1   return unless $ROFI_SCRIPT_DEBUG;
459              
460 0 0         if (ref($_[0]) =~ /Rofi::Script/) {
461 0           p $_[0];
462             } else {
463 0           p @_;
464             }
465             }
466              
467             1;