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   52354 use 5.006; # qr, autovivified filehandles
  3         9  
17              
18             # Plethora of xterm control sequences:
19             # http://rtfm.etla.org/xterm/ctlseq.html
20              
21 3     3   11 use strict;
  3         4  
  3         63  
22 3     3   16 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  3         3  
  3         267  
23              
24             $VERSION = '0.11';
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   1954 use Getopt::Long 2.24; # OO interface
  3         26977  
  3         68  
32              
33 3     3   409 use constant BEL => "";
  3         6  
  3         176  
34 3     3   12 use constant ESC => "";
  3         5  
  3         118  
35              
36 3     3   11 use constant IND => ESC . "D"; # Index
  3         3  
  3         112  
37 3     3   11 use constant IND_8 => chr 0x84;
  3         4  
  3         137  
38 3     3   11 use constant NEL => ESC . "E"; # Next Line
  3         4  
  3         109  
39 3     3   27 use constant NEL_8 => chr 0x85;
  3         3  
  3         147  
40 3     3   13 use constant HTS => ESC . "H"; # Tab Set
  3         4  
  3         135  
41 3     3   11 use constant HTS_8 => chr 0x88;
  3         4  
  3         114  
42 3     3   9 use constant RI => ESC . "M"; # Reverse Index
  3         3  
  3         100  
43 3     3   9 use constant RI_8 => chr 0x8d;
  3         3  
  3         99  
44 3     3   10 use constant SS2 => ESC . "N"; # Single Shift Select of G2 Character Set: affects next character only
  3         3  
  3         95  
45 3     3   10 use constant SS2_8 => chr 0x8e;
  3         2  
  3         96  
46 3     3   8 use constant SS3 => ESC . "O"; # Single Shift Select of G3 Character Set: affects next character only
  3         4  
  3         91  
47 3     3   9 use constant SS3_8 => chr 0x8f;
  3         9  
  3         112  
48 3     3   9 use constant DCS => ESC . "P"; # Device Control String
  3         2  
  3         113  
49 3     3   9 use constant DCS_8 => chr 0x90;
  3         3  
  3         113  
50 3     3   10 use constant SPA => ESC . "V"; # Start of Guarded Area
  3         2  
  3         1200  
51 3     3   11 use constant SPA_8 => chr 0x96;
  3         6  
  3         129  
52 3     3   12 use constant EPA => ESC . "W"; # End of Guarded Area
  3         3  
  3         117  
53 3     3   10 use constant EPA_8 => chr 0x97;
  3         14  
  3         113  
54 3     3   9 use constant SOS => ESC . "X"; # Start of String
  3         5  
  3         94  
55 3     3   10 use constant SOS_8 => chr 0x98;
  3         2  
  3         101  
56 3     3   11 use constant DECID => ESC . "Z"; # Return Terminal ID Obsolete form of CSI c (DA).
  3         1  
  3         114  
57 3     3   10 use constant DECID_8 => chr 0x9a;
  3         3  
  3         124  
58 3     3   12 use constant CSI => ESC . "["; # Control Sequence Introducer
  3         4  
  3         106  
59 3     3   9 use constant CSI_8 => chr 0x9b;
  3         3  
  3         109  
60 3     3   9 use constant ST => ESC . "\\"; # String Terminator
  3         25  
  3         132  
61 3     3   12 use constant ST_8 => chr 0x9c;
  3         2  
  3         117  
62 3     3   9 use constant OSC => ESC . "]";
  3         3  
  3         134  
63 3     3   13 use constant OSC_8 => chr 0x9d;
  3         4  
  3         136  
64 3     3   9 use constant PM => ESC . "^"; # Privacy Message
  3         4  
  3         157  
65 3     3   10 use constant PM_8 => chr 0x9e;
  3         3  
  3         112  
66 3     3   20 use constant APC => ESC . "_"; # Application Program Command
  3         3  
  3         104  
67 3     3   10 use constant APC_8 => chr 0x9f;
  3         1  
  3         2490  
68              
69             my %o;
70             my $need_reset_terminal;
71              
72             sub xterm_conf_string {
73 7     7 1 1054 local @ARGV = @_;
74              
75 7         14 %o = ();
76              
77 7         40 my $p = Getopt::Long::Parser->new;
78 7         117 $p->configure('no_ignore_case');
79 7 100       377 $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       11045 die _usage() if (@ARGV);
128              
129 6         9 my $rv = "";
130              
131 6 50       18 $rv .= BEL if $o{bell};
132              
133             CS_SWITCH: {
134 6 50       5 if (defined $o{cs}) {
  6         16  
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       12 $rv .= ESC . "c" if $o{fullreset};
142              
143             {
144 6         6 my %DECSET = qw(smoothscroll 4
  6         51  
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         22 while(my($optname, $Pm) = each %DECSET) {
161 90 50       222 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       14 $rv .= CSI . '!p' if $o{softreset};
169              
170 6 50       11 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       17 $rv .= CSI . "1t" if $o{deiconify};
183 6 50       13 $rv .= CSI . "2t" if $o{iconify};
184              
185 6 50       11 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       12 $rv .= CSI . "5t" if $o{raise};
196 6 50       11 $rv .= CSI . "6t" if $o{lower};
197 6 50       12 $rv .= CSI . "7t" if $o{refresh};
198 6 50       12 $rv .= CSI . "9;0t" if $o{unmaximize}; # does not work?
199 6 50       16 $rv .= CSI . "9;1t" if $o{maximize}; # does not work?
200 6 50       17 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       12 $rv .= OSC . "1;$o{iconname}" . BEL if defined $o{iconname};
207 6 100       24 $rv .= OSC . "2;$o{title}" . BEL if defined $o{title};
208 6 50       12 $rv .= OSC . "3;$o{xproperty}" . BEL if defined $o{xproperty};
209 6 50       11 $rv .= OSC . "10;$o{fg}" . BEL if defined $o{fg};
210 6 50       10 $rv .= OSC . "11;$o{bg}" . BEL if defined $o{bg};
211 6 50       12 $rv .= OSC . "12;$o{textcursor}" . BEL if defined $o{textcursor};
212 6 50       10 $rv .= OSC . "13;$o{mousefg}" . BEL if defined $o{mousefg};
213 6 50       11 $rv .= OSC . "14;$o{mousebg}" . BEL if defined $o{mousebg};
214 6 50       13 $rv .= OSC . "15;$o{tekfg}" . BEL if defined $o{tekfg};
215 6 50       10 $rv .= OSC . "16;$o{tekbg}" . BEL if defined $o{tekbg};
216 6 50       20 $rv .= OSC . "17;$o{highlightcolor}" . BEL if defined $o{highlightcolor};
217 6 50       12 $rv .= OSC . "50;#$o{font}" . BEL if defined $o{font};
218 6 50       11 $rv .= OSC . "50;#-" . BEL if $o{prevfont};
219 6 50       13 $rv .= OSC . "50;#+" . BEL if $o{nextfont};
220              
221 6 50       11 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   18 no strict 'refs';
  3         4  
  3         2589  
228 0         0 my(@args) = &$sub;
229 0         0 $rv .= join(" ", @args) . "\n";
230             }
231             }
232              
233 6         45 $rv;
234             }
235              
236             sub xterm_conf {
237             # always call xterm_conf_string(), so option validation is done
238 4     4 1 3285 my $rv = xterm_conf_string(@_);
239 4 100       7 if (terminal_is_supported()) {
240 2         7 local $| = 1;
241 2         50 print $rv;
242             }
243             }
244              
245             sub terminal_is_supported {
246 4     4 1 4 my($term) = @_;
247 4 50       17 $term = $ENV{TERM} if !defined $term;
248 4 100       19 if (!$ENV{TERM}) {
    100          
249 1         4 0;
250             } elsif ($ENV{TERM} !~ m{^(xterm|rxvt)}) {
251 1         4 0;
252             } else {
253 2         3 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   2652 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__