1package UI::Dialog::Backend::XOSD;
2###############################################################################
3#  Copyright (C) 2004-2016  Kevin C. Krinke <kevin@krinke.ca>
4#
5#  This library is free software; you can redistribute it and/or
6#  modify it under the terms of the GNU Lesser General Public
7#  License as published by the Free Software Foundation; either
8#  version 2.1 of the License, or (at your option) any later version.
9#
10#  This library is distributed in the hope that it will be useful,
11#  but WITHOUT ANY WARRANTY; without even the implied warranty of
12#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13#  Lesser General Public License for more details.
14#
15#  You should have received a copy of the GNU Lesser General Public
16#  License along with this library; if not, write to the Free Software
17#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18###############################################################################
19use 5.006;
20use strict;
21use warnings;
22use Carp;
23use UI::Dialog::Backend;
24use FileHandle;
25
26#: Ideas:
27# - implement debugging code...
28# - what about tail("/file")?
29# - and my $fh = tail_pipe() ?
30#   (or pipe_start(), pipe_print() and pipe_close())
31# - now here's a kicker, what about a "valid fonts" list and a
32# suitable mechanism to use (and cache) `xlsfonts` to determine
33# the font to use. Once the decision is made the decision should
34# simply be enforced rather than revalidated again and again.
35
36BEGIN {
37    use vars qw( $VERSION @ISA );
38    @ISA = qw( UI::Dialog::Backend );
39    $VERSION = '1.21';
40}
41
42sub new {
43    my $proto = shift();
44    my $class = ref($proto) || $proto;
45    my $cfg = ((ref($_[0]) eq "HASH") ? $_[0] : (@_) ? { @_ } : {});
46    my $self = {};
47    bless($self, $class);
48    $self->{'_opts'} = {};
49
50	#: Dynamic path discovery...
51	my $CFG_PATH = $cfg->{'PATH'};
52	if ($CFG_PATH) {
53		if (ref($CFG_PATH) eq "ARRAY") { $self->{'PATHS'} = $CFG_PATH; }
54		elsif ($CFG_PATH =~ m!:!) { $self->{'PATHS'} = [ split(/:/,$CFG_PATH) ]; }
55		elsif (-d $CFG_PATH) { $self->{'PATHS'} = [ $CFG_PATH ]; }
56	} elsif ($ENV{'PATH'}) { $self->{'PATHS'} = [ split(/:/,$ENV{'PATH'}) ]; }
57	else { $self->{'PATHS'} = ''; }
58
59    $self->{'_opts'}->{'bin'} = $self->_find_bin('osd_cat');
60    $self->{'_opts'}->{'tail'} = $self->_find_bin('tail');
61    $self->{'_opts'}->{'kill'} = $self->_find_bin('kill');
62    $self->{'_opts'}->{'pos'} = $cfg->{'pos'} || undef();
63    $self->{'_opts'}->{'offset'} = $cfg->{'offset'} || 0;
64    $self->{'_opts'}->{'align'} = $cfg->{'align'} || undef();
65    $self->{'_opts'}->{'indent'} = $cfg->{'indent'} || 0;
66    $self->{'_opts'}->{'font'} = $cfg->{'font'} || undef();
67    $self->{'_opts'}->{'colour'} = $cfg->{'colour'} || $cfg->{'color'} || undef();
68    $self->{'_opts'}->{'delay'} = $cfg->{'delay'} || 0;
69    $self->{'_opts'}->{'lines'} = $cfg->{'lines'} || 0;
70    $self->{'_opts'}->{'shadow'} = $cfg->{'shadow'} || 0;
71    $self->{'_opts'}->{'age'} = $cfg->{'age'} || 0;
72    $self->{'_opts'}->{'wait'} = ($cfg->{'wait'}) ? 1 : 0;
73    $self->{'_opts'}->{'length'} = $cfg->{'wait'} || 40;
74    $self->{'_opts'}->{'bar'} = $cfg->{'bar'} || "-";
75    $self->{'_opts'}->{'mark'} = $cfg->{'mark'} || "|";
76    unless (-x $self->{'_opts'}->{'bin'}) {
77		croak("the osd_cat binary could not be found at: ".$self->{'_opts'}->{'bin'});
78    }
79
80    $self->{'_opts'}->{'trust-input'} =
81      ( exists $cfg->{'trust-input'}
82        && $cfg->{'trust-input'}==1
83      ) ? 1 : 0;
84
85    return($self);
86}
87
88#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
89#: Internal Methods
90#:
91
92my $SIG_CODE = {};
93sub _del_display {
94    my $CODE = $SIG_CODE->{$$};
95    unless (not ref($CODE)) {
96		delete($CODE->{'_DISPLAY'});
97		$SIG_CODE->{$$} = "";
98    }
99}
100sub _gen_opt_str {
101    my $self = shift();
102    my $args = shift();
103    my $string = "";
104    if ($args->{'pos'}) {
105		my $pos = ($args->{'pos'} =~ /^top|middle|bottom$/i) ? lc($args->{'pos'}) : 'top';
106		$string .= " --pos='".$pos."'";
107    }
108    if ($args->{'offset'}) {
109		my $offset = ($args->{'offset'} =~ /^\d+$/) ? $args->{'offset'} : 0;
110		$string .= " --offset='".$offset."'";
111    }
112    if ($args->{'align'}) {
113		my $align = ($args->{'align'} =~ /^left|center|right$/i) ? lc($args->{'align'}) : 'left';
114		$string .= " --align='".$align."'";
115    }
116    if ($args->{'indent'}) {
117		my $indent = ($args->{'indent'} =~ /^\d+$/) ? $args->{'indent'} : 0;
118		$string .= " --indent='".$indent."'";
119    }
120    if ($args->{'font'}) {
121		my $font = $args->{'font'} || "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*";
122		$string .= " --font='".$font."'";
123    }
124    if ($args->{'colour'}) {
125		my $colour = $args->{'colour'} || "green";
126		$string .= " --color='".$colour."'";
127    }
128    if ($args->{'delay'}) {
129		my $delay = ($args->{'delay'} =~ /^\d+$/) ? $args->{'delay'} : 5;
130		$string .= " --delay='".$delay."'";
131    }
132    if ($args->{'lines'}) {
133		my $lines = ($args->{'lines'} =~ /^\d+$/) ? $args->{'lines'} : 5;
134		$string .= " --lines='".$lines."'";
135    }
136    if ($args->{'shadow'}) {
137		my $shadow = ($args->{'shadow'} =~ /^\d+$/) ? $args->{'shadow'} : 0;
138		$string .= " --shadow='".$shadow."'";
139    }
140    if ($args->{'age'}) {
141		my $age = ($args->{'age'} =~ /^\d+$/) ? $args->{'age'} : 0;
142		$string .= " --age='".$age."'";
143    }
144    if ($args->{'wait'}) {
145		$string .= " --wait";
146    }
147    $self->_debug("xosd: ".$string,3);
148    return($string||" ");
149}
150
151#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
152#: Public Methods
153#:
154
155sub line {
156    my $self = shift();
157    my $args = $self->_merge_attrs(@_);
158    my $opts = $self->_gen_opt_str($args);
159    if (open(XOSD,"| ".$self->{'_opts'}->{'bin'}.$opts." -")) {
160		print XOSD ($args->{'text'}||'')."\n";
161		close(XOSD);
162    } else {
163		croak("failed to open osd_cat output pipe!");
164    }
165}
166
167sub file {
168    my $self = shift();
169    my $args = $self->_merge_attrs(@_);
170    my $opts = $self->_gen_opt_str($args);
171    if (-r $args->{'file'}) {
172		if (open(FILE,"<".$args->{'file'})) {
173			local $/;
174			my $text = <FILE>;
175			close(FILE);
176			$text =~ s!\t!    !g;
177			if (open(XOSD,"| ".$self->{'_opts'}->{'bin'}.$opts." -")) {
178				print XOSD ($text||'')."\n";
179				close(XOSD);
180			} else {
181				croak("failed to open osd_cat output pipe!");
182			}
183		}
184    }
185}
186
187sub gauge {
188    my $self = shift();
189    my $args = $self->_merge_attrs(@_);
190    my $opts = $self->_gen_opt_str($args);
191    my $length = $args->{'length'} || 40;
192    my $bar = ($args->{'bar'} || "-") x $length;
193    my $percent = $args->{'percent'} || '0';
194    $percent = (($percent <= 100 && $percent >= 0) ? $percent : 0 );
195    my $perc = int((($length / 100) * $percent));
196    substr($bar,($perc||0),1,($args->{'mark'}||"|"));
197    my $text = $args->{'text'} ? $args->{'text'}."\n" : '';
198    $text .= $percent."% ".$bar."\n";
199    if (open(XOSD,"| ".$self->{'_opts'}->{'bin'}.$opts." -")) {
200		print XOSD $text;
201		close(XOSD);
202    } else {
203		croak("failed to open osd_cat output pipe!");
204    }
205}
206
207sub display_start {
208    my $self = shift();
209    my $args = $self->_merge_attrs(@_);
210    my $opts = $self->_gen_opt_str($args);
211    $self->{'_DISPLAY'} ||= {};
212    $self->{'_DISPLAY'}->{'ARGS'} = $args;
213    return(0) if defined $self->{'_DISPLAY'}->{'FH'};
214    my $command = $self->{'_opts'}->{'bin'}.$opts." -";
215    $self->{'_DISPLAY'}->{'FH'} = new FileHandle;
216    $self->{'_DISPLAY'}->{'FH'}->open("| $command");
217    my $rv = $? >> 8;
218    $self->{'_DISPLAY'}->{'FH'}->autoflush(1);
219    my $this_rv;
220    if ($rv && $rv >= 1) { $this_rv = 0; }
221    else { $this_rv = 1; }
222    return($this_rv);
223}
224sub display_text {
225    my $self = shift();
226    my $mesg;
227    if (@_ > 1) { $mesg = join("\n",@_); }
228    elsif (ref($_[0]) eq "ARRAY") { $mesg = join("\n",@{$_[0]}); }
229    else { $mesg = $_[0] || return(0); }
230    return(0) unless $self->{'_DISPLAY'}->{'FH'};
231    my $fh = $self->{'_DISPLAY'}->{'FH'};
232    $SIG_CODE->{$$} = $self; local $SIG{'PIPE'} = \&_del_gauge;
233    print $fh $mesg."\n";
234    return(((defined $self->{'_DISPLAY'}->{'FH'}) ? 1 : 0));
235}
236sub display_gauge {
237    my $self = $_[0];
238    return(0) unless $self->{'_DISPLAY'}->{'FH'};
239    my $args = $self->_merge_attrs();
240    my $length = $args->{'length'} || 40;
241    my $bar = ($args->{'bar'} || "-") x $length;
242    my $percent = $_[1] || 0;
243    $percent = (($percent <= 100 && $percent >= 0) ? $percent : 0 );
244    my $perc = int((($length / 100) * $percent));
245    substr($bar,($perc||0),1,($args->{'mark'}||"|"));
246    my $text = $_[2] ? $_[2]."\n" : '';
247    $text .= $percent."% ".$bar."\n";
248    my $fh = $self->{'_DISPLAY'}->{'FH'};
249    $SIG_CODE->{$$} = $self; local $SIG{'PIPE'} = \&_del_gauge;
250    print $fh $text;
251    return(((defined $self->{'_DISPLAY'}->{'FH'}) ? 1 : 0));
252}
253sub display_stop {
254    my $self = shift();
255    return(0) unless $self->{'_DISPLAY'}->{'FH'};
256    my $args = $self->{'_DISPLAY'}->{'ARGS'};
257    my $fh = $self->{'_DISPLAY'}->{'FH'};
258    $SIG_CODE->{$$} = $self; local $SIG{'PIPE'} = \&_del_gauge;
259    $self->{'_DISPLAY'}->{'FH'}->close();
260    delete($self->{'_DISPLAY'}->{'FH'});
261    delete($self->{'_DISPLAY'}->{'ARGS'});
262    delete($self->{'_DISPLAY'}->{'PERCENT'});
263    delete($self->{'_DISPLAY'});
264    return(1);
265}
266
267# #$d->tail( file => "/tmp/xosdtail.log", delay => '3' );
268# sub tail {
269#     my $self = shift();
270#     my $args = $self->_merge_attrs(@_);
271#     my $opts = $self->_gen_opt_str($args);
272#     if (-r $args->{'file'}) {
273# 	my $tail_cmnd = $self->{'_opts'}->{'tail'}.' -f '.$args->{'file'};
274# 	system($tail_cmnd." | ".$self->{'_opts'}->{'bin'}.$opts." -");
275#     } else {
276# 	$self->line( @_, text => "couldn't open file: ".($args->{'file'}||'NULL') );
277#     }
278# }
279
280# sub tailbg {
281#     my $self = shift();
282#     my $args = $self->_merge_attrs(@_);
283#     my $opts = $self->_gen_opt_str($args);
284#     if (-r $args->{'file'}) {
285# 	my $tail_cmnd = $self->{'_opts'}->{'tail'}.' -f '.$args->{'file'};
286# 	my $xosd_cmnd = $self->{'_opts'}->{'bin'}.$opts." -";
287# #	system($tail_cmnd." | ".$self->{'_opts'}->{'bin'}.$opts." - &");
288# #	$self->{'forkpid'} = $self->command_forked($tail_cmnd." | ".$self->{'_opts'}->{'bin'}.$opts." -");
289# #	$self->{'forkpid'} = $self->command_forked($self->{'_opts'}->{'tail'},' -f '.$args->{'file'}. " | ".$self->{'_opts'}->{'bin'}.$opts." -");
290# 	$self->{'forkpid'} = $self->tailbg_forked($tail_cmnd,$xosd_cmnd);
291# 	print "pid: ".$self->{'forkpid'}."\n";
292# 	return(1) if $self->{'forkpid'};
293# 	return(0);
294#     } else {
295# 	$self->line( @_, text => "couldn't open file: ".($args->{'file'}||'NULL') );
296#     }
297# }
298
299
300# sub command_forked {
301#     my $self = shift();
302#     if (my $pid = fork()) { return($pid); }
303#     else { exec(@_); }
304# }
305
306# sub tailbg_forked {
307#     my $self = shift();
308#     my $tail = shift();
309#     my $osdc = shift();
310#     if (my $pid = fork()) { return($pid); }
311#     else {
312# 	# here we open the tail and read
313# 	# while reading, print to an open osd_cat
314# 	my $TSIGP = $SIG{'PIPE'};
315# 	$SIG{'PIPE'} = "IGNORE";
316# 	if (open(TAIL,$tail." |")) {
317# 	    unless (open(OSDC,"| ".$osdc)) {
318# 		close(TAIL);
319# 		return();
320# 	    }
321# 	    my $TP = $|;
322# 	    $| = 1;
323# 	    while (my $line = <TAIL>) {
324# 		print OSDC $line;
325# 	    }
326# 	    $| = $TP;
327# 	    close(OSDC);
328# 	    close(TAIL);
329# 	}
330# 	$SIG{'PIPE'} = $TSIGP;
331#     }
332# }
333
334# sub tailbg_end {
335#     my $self = shift();
336#     my $args = $self->_merge_attrs(@_);
337#     if ($self->{'forkpid'}) {
338# 	print "pid: ".$self->{'forkpid'}."\n";
339# 	if (kill(15,$self->{'forkpid'})) {
340# 	    print "killed: ".$self->{'forkpid'}."\n";
341# 	} else {
342# 	    print "maimed: ".$self->{'forkpid'}."\n";
343# 	}
344#     }
345# }
346
3471;
348