File Coverage

blib/lib/XTerm/Conf.pm
Criterion Covered Total %
statement 169 232 72.8
branch 41 106 38.6
condition 0 6 0.0
subroutine 43 52 82.6
pod 3 3 100.0
total 256 399 64.1


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 2006,2008,2009,2012,2014,2015,2017 Slaven Rezic. All rights reserved.
7             # This program is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: srezic@cpan.org
11             # WWW: http://www.rezic.de/eserte/
12             #
13              
14             package XTerm::Conf;
15              
16 3     3   210756 use 5.006; # qr, autovivified filehandles
  3         25  
17              
18             # Plethora of xterm control sequences:
19             # http://rtfm.etla.org/xterm/ctlseq.html
20              
21 3     3   12 use strict;
  3         5  
  3         60  
22 3     3   10 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  3         4  
  3         444  
23              
24             $VERSION = '0.11_50';
25              
26             require Exporter;
27             @ISA = qw(Exporter);
28             @EXPORT = qw(xterm_conf);
29             @EXPORT_OK = qw(xterm_conf_string terminal_is_supported);
30              
31 3     3   1543 use Getopt::Long 2.24; # OO interface
  3         40916  
  3         61  
32              
33 3     3   445 use constant BEL => "";
  3         5  
  3         151  
34 3     3   196 use constant ESC => "";
  3         4  
  3         124  
35              
36 3     3   13 use constant IND => ESC . "D"; # Index
  3         3  
  3         633  
37 3     3   17 use constant IND_8 => chr 0x84;
  3         3  
  3         264  
38 3     3   14 use constant NEL => ESC . "E"; # Next Line
  3         4  
  3         108  
39 3     3   12 use constant NEL_8 => chr 0x85;
  3         4  
  3         97  
40 3     3   13 use constant HTS => ESC . "H"; # Tab Set
  3         6  
  3         94  
41 3     3   12 use constant HTS_8 => chr 0x88;
  3         4  
  3         97  
42 3     3   12 use constant RI => ESC . "M"; # Reverse Index
  3         3  
  3         110  
43 3     3   17 use constant RI_8 => chr 0x8d;
  3         6  
  3         145  
44 3     3   14 use constant SS2 => ESC . "N"; # Single Shift Select of G2 Character Set: affects next character only
  3         3  
  3         180  
45 3     3   13 use constant SS2_8 => chr 0x8e;
  3         22  
  3         104  
46 3     3   13 use constant SS3 => ESC . "O"; # Single Shift Select of G3 Character Set: affects next character only
  3         4  
  3         104  
47 3     3   12 use constant SS3_8 => chr 0x8f;
  3         6  
  3         96  
48 3     3   12 use constant DCS => ESC . "P"; # Device Control String
  3         4  
  3         105  
49 3     3   13 use constant DCS_8 => chr 0x90;
  3         5  
  3         102  
50 3     3   12 use constant SPA => ESC . "V"; # Start of Guarded Area
  3         10  
  3         101  
51 3     3   20 use constant SPA_8 => chr 0x96;
  3         4  
  3         152  
52 3     3   15 use constant EPA => ESC . "W"; # End of Guarded Area
  3         5  
  3         111  
53 3     3   13 use constant EPA_8 => chr 0x97;
  3         4  
  3         134  
54 3     3   13 use constant SOS => ESC . "X"; # Start of String
  3         4  
  3         134  
55 3     3   14 use constant SOS_8 => chr 0x98;
  3         4  
  3         103  
56 3     3   12 use constant DECID => ESC . "Z"; # Return Terminal ID Obsolete form of CSI c (DA).
  3         3  
  3         122  
57 3     3   14 use constant DECID_8 => chr 0x9a;
  3         4  
  3         118  
58 3     3   11 use constant CSI => ESC . "["; # Control Sequence Introducer
  3         5  
  3         108  
59 3     3   18 use constant CSI_8 => chr 0x9b;
  3         6  
  3         129  
60 3     3   14 use constant ST => ESC . "\\"; # String Terminator
  3         4  
  3         254  
61 3     3   14 use constant ST_8 => chr 0x9c;
  3         4  
  3         111  
62 3     3   80 use constant OSC => ESC . "]";
  3         5  
  3         120  
63 3     3   15 use constant OSC_8 => chr 0x9d;
  3         4  
  3         111  
64 3     3   13 use constant PM => ESC . "^"; # Privacy Message
  3         4  
  3         105  
65 3     3   12 use constant PM_8 => chr 0x9e;
  3         5  
  3         105  
66 3     3   13 use constant APC => ESC . "_"; # Application Program Command
  3         4  
  3         113  
67 3     3   14 use constant APC_8 => chr 0x9f;
  3         9  
  3         2701  
68              
69             my %o;
70             my $need_reset_terminal;
71              
72             sub xterm_conf_string {
73 7     7 1 743 local @ARGV = @_;
74              
75 7         12 %o = ();
76              
77 7         36 my $p = Getopt::Long::Parser->new;
78 7         164 $p->configure('no_ignore_case');
79 7 100       373 $p->getoptions(\%o,
80             "iconname|n=s",
81             "title|T=s",
82             "fg|foreground=s",
83             "bg|background=s",
84             "textcursor|cr=s",
85             "mousefg|mouseforeground|ms=s",
86             "mousebg|mousebackground=s",
87             "tekfg|tekforeground=s",
88             "tekbg|tekbackground=s",
89             "highlightcolor|hc=s",
90             "bell",
91             "cs=s",
92             "fullreset",
93             "softreset",
94             "smoothscroll!", # no visual effect
95             "reverse|reversevideo!",
96             "origin!",
97             "wraparound!",
98             "autorepeat!",
99             "formfeed!",
100             "showcursor!",
101             "showscrollbar!", # rxvt
102             "tektronix!",
103             "marginbell!",
104             "reversewraparound!",
105             "backsendsdelete!",
106             "bottomscrolltty!", # rxvt
107             "bottomscrollkey!", # rxvt
108             "metasendsesc|metasendsescape!",
109             "scrollregion=s",
110             "deiconify",
111             "iconify",
112             "geometry=s",
113             "raise",
114             "lower",
115             "refresh|x11refresh",
116             "maximize",
117             "unmaximize",
118             "xproperty|x11property=s",
119             "font=s",
120             "nextfont",
121             "prevfont",
122             "report=s",
123             "debugreport",
124             "resize=i",
125             )
126             or _usage();
127 6 50       14184 die _usage() if (@ARGV);
128              
129 6         12 my $rv = "";
130              
131 6 50       14 $rv .= BEL if $o{bell};
132              
133             CS_SWITCH: {
134 6 50       8 if (defined $o{cs}) {
  6         13  
135 0 0       0 $rv .= (ESC . '%G'), last if $o{cs} =~ m{^utf-?8$}i;
136 0 0       0 $rv .= (ESC . '%@'), last if $o{cs} =~ m{^(latin-?1|iso-?8859-?1)$}i;
137 0         0 warn "Unhandled -cs parameter $o{cs}\n";
138             }
139             }
140              
141 6 50       14 $rv .= ESC . "c" if $o{fullreset};
142              
143             {
144 6         6 my %DECSET = qw(smoothscroll 4
  6         41  
145             reverse 5
146             origin 6
147             wraparound 7
148             autorepeat 8
149             formfeed 18
150             showcursor 25
151             showscrollbar 30
152             tektronix 38
153             marginbell 44
154             reversewraparound 45
155             backsendsdelete 67
156             bottomscrolltty 1010
157             bottomscrollkey 1011
158             metasendsesc 1036
159             );
160 6         23 while(my($optname, $Pm) = each %DECSET) {
161 90 50       236 if (defined $o{$optname}) {
162 0 0       0 my $onoff = $o{$optname} ? 'h' : 'l';
163 0         0 $rv .= CSI . '?' . $Pm . $onoff;
164             }
165             }
166             }
167              
168 6 50       12 $rv .= CSI . '!p' if $o{softreset};
169              
170 6 50       13 if (defined $o{scrollregion}) {
171 0 0 0     0 if ($o{scrollregion} eq '' || $o{scrollregion} eq 'default') {
172 0         0 $rv .= CSI . 'r';
173             } else {
174 0         0 my($top,$bottom) = split /,/, $o{scrollregion};
175 0         0 for ($top, $bottom) {
176 0 0       0 die "Not a number: $_\n" if !/^\d*$/;
177             }
178 0         0 $rv .= CSI . $top . ";" . $bottom . "r";
179             }
180             }
181              
182 6 50       11 $rv .= CSI . "1t" if $o{deiconify};
183 6 50       11 $rv .= CSI . "2t" if $o{iconify};
184              
185 6 50       10 if (defined $o{geometry}) {
186 0 0       0 if (my($w,$h,$wc,$hc,$x,$y) = $o{geometry} =~ m{^(?:(\d+)x(\d+)|(\d+)cx(\d+)c)?(?:\+(\d+)\+(\d+))?$}) {
187 0 0       0 $rv .= CSI."3;".$x.";".$y."t" if defined $x;
188 0 0       0 $rv .= CSI."4;".$h.";".$w."t" if defined $h; # does not work?
189 0 0       0 $rv .= CSI."8;".$hc.";".$wc."t" if defined $hc; # does not work?
190             } else {
191 0         0 die "Cannot parse geometry string, must be width x height+x+y\n";
192             }
193             }
194              
195 6 50       11 $rv .= CSI . "5t" if $o{raise};
196 6 50       10 $rv .= CSI . "6t" if $o{lower};
197 6 50       9 $rv .= CSI . "7t" if $o{refresh};
198 6 50       9 $rv .= CSI . "9;0t" if $o{unmaximize}; # does not work?
199 6 50       8 $rv .= CSI . "9;1t" if $o{maximize}; # does not work?
200 6 50       11 if ($o{resize}) {
201             die "-resize parameter must be at least 24\n"
202 0 0 0     0 if $o{resize} < 24 || $o{resize} !~ /^\d+$/;
203 0         0 $rv .= CSI . $o{resize} . 't';
204             }
205              
206 6 50       10 $rv .= OSC . "1;$o{iconname}" . BEL if defined $o{iconname};
207 6 100       18 $rv .= OSC . "2;$o{title}" . BEL if defined $o{title};
208 6 50       10 $rv .= OSC . "3;$o{xproperty}" . BEL if defined $o{xproperty};
209 6 50       13 $rv .= OSC . "10;$o{fg}" . BEL if defined $o{fg};
210 6 50       9 $rv .= OSC . "11;$o{bg}" . BEL if defined $o{bg};
211 6 50       11 $rv .= OSC . "12;$o{textcursor}" . BEL if defined $o{textcursor};
212 6 50       12 $rv .= OSC . "13;$o{mousefg}" . BEL if defined $o{mousefg};
213 6 50       10 $rv .= OSC . "14;$o{mousebg}" . BEL if defined $o{mousebg};
214 6 50       11 $rv .= OSC . "15;$o{tekfg}" . BEL if defined $o{tekfg};
215 6 50       9 $rv .= OSC . "16;$o{tekbg}" . BEL if defined $o{tekbg};
216 6 50       10 $rv .= OSC . "17;$o{highlightcolor}" . BEL if defined $o{highlightcolor};
217 6 50       9 $rv .= OSC . "50;#$o{font}" . BEL if defined $o{font};
218 6 50       28 $rv .= OSC . "50;#-" . BEL if $o{prevfont};
219 6 50       10 $rv .= OSC . "50;#+" . BEL if $o{nextfont};
220              
221 6 50       43 if ($o{report}) {
222 0 0       0 if ($o{report} eq 'cgeometry') {
223 0         0 my($h,$w) = _report_cgeometry();
224 0         0 $rv .= $w."x".$h."\n";
225             } else {
226 0         0 my $sub = "_report_" . $o{report};
227 3     3   20 no strict 'refs';
  3         5  
  3         2421  
228 0         0 my(@args) = &$sub;
229 0         0 $rv .= join(" ", @args) . "\n";
230             }
231             }
232              
233 6         53 $rv;
234             }
235              
236             sub xterm_conf {
237             # always call xterm_conf_string(), so option validation is done
238 4     4 1 6407 my $rv = xterm_conf_string(@_);
239 4 100       8 if (terminal_is_supported()) {
240 2         8 local $| = 1;
241 2         53 print $rv;
242             }
243             }
244              
245             sub terminal_is_supported {
246 4     4 1 6 my($term) = @_;
247 4 50       9 $term = $ENV{TERM} if !defined $term;
248 4 100       17 if (!$ENV{TERM}) {
    100          
249 1         3 0;
250             } elsif ($ENV{TERM} !~ m{^(xterm|rxvt)}) {
251 1         3 0;
252             } else {
253 2         4 1;
254             }
255             }
256              
257             sub _report ($$) {
258 0     0   0 my($cmd, $rx) = @_;
259              
260 0         0 require Term::ReadKey;
261 0         0 Term::ReadKey::ReadMode(5);
262              
263 0         0 my @args;
264              
265 0         0 eval {
266 0         0 require IO::Select;
267              
268 0         0 my $debug = $o{debugreport};
269              
270 0 0       0 open my $TTY, "+< /dev/tty" or die "Cannot open terminal /dev/tty: $!";
271 0         0 syswrite $TTY, $cmd;
272              
273 0         0 my $sel = IO::Select->new;
274 0         0 $sel->add($TTY);
275              
276 0         0 my $res = "";
277 0         0 while() {
278 0         0 my(@ready) = $sel->can_read(5);
279 0 0       0 if (!@ready) {
280 0         0 die "Cannot report, maybe allowWindowOps is set to false?";
281 0         0 last;
282             }
283 0 0       0 sysread $TTY, my $ch, 1 or die "Cannot sysread: $!";
284 0 0       0 print STDERR ord($ch)." " if $debug;
285 0         0 $res .= $ch;
286 0 0       0 last if (@args = $res =~ $rx);
287             }
288              
289 0         0 1;
290             };
291 0         0 my $err = $@;
292              
293 0         0 Term::ReadKey::ReadMode(0);
294              
295 0 0       0 if ($err) {
296 0         0 die "$err\n";
297             }
298 0         0 @args;
299             }
300              
301 0     0   0 sub _report_status { _report CSI.'5n', qr{0n} }
302 0     0   0 sub _report_cursorpos { _report CSI.'6n', qr{(\d+);(\d+)R} }
303 0     0   0 sub _report_windowpos { _report CSI.'13t', qr{;(\d+);(\d+)t} }
304 0     0   0 sub _report_geometry { _report CSI.'14t', qr{;(\d+);(\d+)t} }
305 0     0   0 sub _report_cgeometry { _report CSI.'18t', qr{;(\d+);(\d+)t} }
306 0     0   0 sub _report_cscreengeom { _report CSI.'19t', qr{;(\d+);(\d+)t} }
307 0     0   0 sub _report_iconname { _report CSI.'20t', qr{L(.*?)(?:\Q@{[ST]}\E|\Q@{[ST_8]}\E)} }
  0         0  
  0         0  
308 0     0   0 sub _report_title { _report CSI.'21t', qr{l(.*?)(?:\Q@{[ST]}\E|\Q@{[ST_8]}\E)} }
  0         0  
  0         0  
309              
310             sub _usage {
311 1     1   2348 die <
312             usage: $0 [-n|iconname string] [-T|title string] [-cr|textcursor color]
313             [-fg|-foreground color] [-bg|-background color color]
314             [-ms|mousefg|-mouseforeground color] [-mousebg|-mousebackground color]
315             [-tekfg|-tekforeground color] [-tekbg|-tekbackground color]
316             [-hc|highlightcolor color] [-bell] [-cs ...] [-fullreset] [-softreset]
317             [-[no]smoothscroll] [-[no]reverse|reversevideo], [-[no]origin]
318             [-[no]wraparound] [-[no]autorepeat] [-[no]formfeed] [-[no]showcursor]
319             [-[no]showscrollbar] [-[no]tektronix] [-[no]marginbell]
320             [-[no]reversewraparound] [-[no]backsendsdelete]
321             [-[no]bottomscrolltty] [-[no]bottomscrollkey]
322             [-[no]metasendsesc|metasendsescape] [-scrollregion ...]
323             [-deiconify] [-iconify] [-geometry x11geom] [-raise] [-lower]
324             [-refresh|x11refresh] [-maximize] [-unmaximize]
325             [-xproperty|x11property ...] [-font ...] [-nextfont] [-prevfont]
326             [-report ...] [-debugreport] [-resize ...]
327              
328             EOF
329             }
330              
331             return 1 if caller;
332              
333             xterm_conf(@ARGV);
334              
335             __END__