File Coverage

blib/lib/Term/Detect/Software.pm
Criterion Covered Total %
statement 11 173 6.3
branch 0 74 0.0
condition 0 28 0.0
subroutine 4 8 50.0
pod 2 2 100.0
total 17 285 5.9


line stmt bran cond sub pod time code
1             package Term::Detect::Software;
2              
3 1     1   419884 use 5.010001;
  1         5  
4 1     1   8 use strict;
  1         2  
  1         32  
5 1     1   11 use warnings;
  1         3  
  1         111  
6              
7 1     1   8 use Exporter qw(import);
  1         1  
  1         2100  
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2024-02-10'; # DATE
11             our $DIST = 'Term-Detect-Software'; # DIST
12             our $VERSION = '0.227'; # VERSION
13              
14             our @EXPORT_OK = qw(detect_terminal detect_terminal_cached);
15              
16             my $dt_cache;
17             sub detect_terminal_cached {
18 0 0   0 1   if (!$dt_cache) {
19 0           $dt_cache = detect_terminal(@_);
20             }
21 0           $dt_cache;
22             }
23              
24             sub _set_engine {
25 0     0     my ($info, $engine) = @_;
26 0 0         if ($engine eq 'konsole') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
27 0           $info->{emulator_engine} = 'konsole';
28 0           $info->{color_depth} = 2**24;
29 0           $info->{default_bgcolor} = '000000';
30 0           $info->{unicode} = 1;
31 0           $info->{box_chars} = 1;
32             } elsif ($engine eq 'xterm') {
33 0           $info->{emulator_engine} = 'xterm';
34 0           $info->{color_depth} = 256;
35 0           $info->{default_bgcolor} = 'ffffff';
36 0           $info->{unicode} = 0;
37 0           $info->{box_chars} = 1;
38             } elsif ($engine eq 'cygwin') {
39 0           $info->{emulator_engine} = 'cygwin';
40 0           $info->{color_depth} = 16;
41 0           $info->{default_bgcolor} = '000000';
42 0           $info->{unicode} = 0; # CONFIRM?
43 0           $info->{box_chars} = 1;
44             } elsif ($engine eq 'linux') {
45             # Linux virtual console
46 0           $info->{emulator_engine} = 'linux';
47 0           $info->{color_depth} = 16;
48 0           $info->{default_bgcolor} = '000000';
49             # actually it can show a few Unicode characters like single borders
50 0           $info->{unicode} = 0;
51 0           $info->{box_chars} = 0;
52             } elsif ($engine eq 'gnome-terminal') {
53 0           $info->{emulator_engine} = 'gnome-terminal';
54 0           $info->{unicode} = 1;
55 0           $info->{box_chars} = 1;
56             # color_depth and default_bgcolor vary
57             } elsif ($engine eq 'windows') {
58 0           $info->{emulator_software} = 'windows';
59 0           $info->{emulator_engine} = 'windows';
60 0           $info->{color_depth} = 16;
61 0           $info->{unicode} = 0;
62 0           $info->{default_bgcolor} = '000000';
63 0           $info->{box_chars} = 0;
64             } elsif ($engine eq 'dumb') {
65             # run under CGI or something like that
66 0           $info->{emulator_software} = 'dumb';
67 0           $info->{emulator_engine} = 'dumb';
68 0           $info->{color_depth} = 0;
69             # XXX how to determine unicode support?
70 0           $info->{default_bgcolor} = '000000';
71 0           $info->{box_chars} = 0;
72             } elsif ($engine eq 'rxvt') {
73 0           $info->{emulator_engine} = 'rxvt';
74 0           $info->{color_depth} = 16;
75 0           $info->{unicode} = 0;
76 0           $info->{default_bgcolor} = 'd6d2d0';
77 0           $info->{box_chars} = 1;
78             } elsif ($engine eq 'st') {
79 0           $info->{emulator_software} = 'st';
80 0           $info->{emulator_engine} = 'st';
81 0           $info->{color_depth} = 256;
82 0           $info->{unicode} = 1;
83 0           $info->{default_bgcolor} = '000000';
84 0           $info->{box_chars} = 1; # some characters are currently flawed though as of 0.6
85             } elsif ($engine eq 'putty') {
86 0           $info->{emulator_engine} = 'putty';
87 0           $info->{color_depth} = 256;
88 0           $info->{unicode} = 0;
89 0           $info->{default_bgcolor} = '000000';
90             } elsif ($engine eq 'xvt') {
91 0           $info->{emulator_engine} = 'xvt';
92 0           $info->{color_depth} = 0; # only support bold
93 0           $info->{unicode} = 0;
94 0           $info->{default_bgcolor} = 'd6d2d0';
95             } else {
96 0           die "Unknown engine '$engine'";
97             }
98             }
99              
100             sub detect_terminal {
101 0     0 1   my @dbg;
102 0           my $info = {_debug_info=>\@dbg};
103              
104             DETECT:
105             {
106 0 0         if (defined $ENV{PERL_TERM_DETECT_SOFTWARE_ENGINE}) {
  0            
107 0           push @dbg, "skip detect, set to '$ENV{PERL_TERM_DETECT_SOFTWARE_ENGINE}' via PERL_TERM_DETECT_SOFTWARE_ENGINE env";
108 0           _set_engine($info, $ENV{PERL_TERM_DETECT_SOFTWARE_ENGINE});
109 0           last DETECT;
110             }
111              
112 0 0         unless (defined $ENV{TERM}) {
113 0           push @dbg, "skip: TERM env undefined";
114 0           $info->{emulator_engine} = '';
115 0           $info->{emulator_software} = '';
116 0           last DETECT;
117             }
118              
119 0 0 0       if ($ENV{KONSOLE_DBUS_SERVICE} || $ENV{KONSOLE_DBUS_SESSION}) {
120 0           push @dbg, "detect: konsole via KONSOLE_DBUS_{SERVICE,SESSION} env";
121 0           _set_engine($info, 'konsole');
122 0           last DETECT;
123             }
124              
125 0 0         if ($ENV{XTERM_VERSION}) {
126 0           push @dbg, "detect: xterm via XTERM_VERSION env";
127 0           _set_engine($info, 'xterm');
128 0           last DETECT;
129             }
130              
131 0 0 0       if ($ENV{TERM} eq 'xterm' && ($ENV{OSTYPE} // '') eq 'cygwin') {
      0        
132 0           push @dbg, "detect: xterm via TERM env (cygwin)";
133 0           _set_engine($info, 'cygwin');
134 0           last DETECT;
135             }
136              
137 0 0         if ($ENV{TERM} eq 'linux') {
138 0           push @dbg, "detect: linux via TERM env";
139 0           _set_engine($info, 'linux');
140 0           last DETECT;
141             }
142              
143 0           my $gnome_terminal_terms = [qw/gnome-terminal guake xfce4-terminal
144             mlterm lxterminal/];
145              
146             my $set_gnome_terminal_term = sub {
147 0     0     _set_engine($info, 'gnome-terminal');
148 0           $info->{emulator_software} = $_[0];
149              
150             # xfce4-terminal only shows 16 color, despite being
151             # gnome-terminal-based?
152 0 0         $info->{color_depth} = $_[0] =~ /xfce4/ ? 16 : 256;
153              
154 0 0         if (grep { $_ eq $_[0] } (qw/mlterm/)) {
  0            
155 0           $info->{default_bgcolor} = 'ffffff';
156             } else {
157 0           $info->{default_bgcolor} = '000000';
158             }
159 0           };
160              
161 0 0 0       if (grep { $_ eq ($ENV{COLORTERM} // '') } @$gnome_terminal_terms) {
  0            
162 0           push @dbg, "detect: gnome-terminal via COLORTERM";
163 0           $set_gnome_terminal_term->($ENV{COLORTERM});
164 0           last DETECT;
165             }
166              
167 0 0 0       if ($ENV{TERM} eq 'dumb' && $ENV{windir}) {
168 0           push @dbg, "detect: windows via TERM & windir env";
169 0           _set_engine($info, 'windows');
170 0           last DETECT;
171             }
172              
173 0 0         if ($ENV{TERM} eq 'dumb') {
174 0           push @dbg, "detect: dumb via TERM env";
175 0           _set_engine($info, 'dumb');
176 0           last DETECT;
177             }
178              
179             DETECT_VIA_PS:
180             {
181             # TODO: check under windows, because Proc::ProcessTable also works
182             # under windows
183 0 0         last if $^O =~ /Win/;
  0            
184              
185 0 0         last unless $ENV{PERL_TERM_DETECT_SOFTWARE_CHECK_PS};
186              
187 0           require Proc::Find::Parents;
188 0           my $ppids = Proc::Find::Parents::get_parent_processes();
189 0 0         unless (defined $ppids) {
190 0           push @dbg, "skip: get_parent_processes returns undef";
191 0           last;
192             }
193              
194             # [0] is shell
195 0 0         my $proc = @$ppids >= 2 ? $ppids->[1]{name} : '';
196             #say "D:proc=$proc";
197 0 0 0       if (grep { $_ eq $proc } @$gnome_terminal_terms) {
  0 0          
    0          
    0          
    0          
198 0           push @dbg, "detect: gnome-terminal via procname ($proc)";
199 0           $set_gnome_terminal_term->($proc);
200 0           last DETECT;
201 0           } elsif (grep { $_ eq $proc } (qw/rxvt mrxvt/)) {
202 0           push @dbg, "detect: rxvt via procname ($proc)";
203 0           $info->{emulator_software} = $proc;
204 0           _set_engine($info, 'rxvt');
205 0           last DETECT;
206             } elsif ($proc eq 'st' && $ENV{TERM} eq 'xterm-256color') {
207 0           push @dbg, "detect: st via procname";
208 0           _set_engine($info, 'st');
209 0           last DETECT;
210 0           } elsif (grep { $_ eq $proc } (qw/pterm/)) {
211 0           push @dbg, "detect: pterm via procname ($proc)";
212 0           $info->{emulator_software} = $proc;
213 0           _set_engine($info, 'putty');
214 0           last DETECT;
215 0           } elsif (grep { $_ eq $proc } (qw/xvt/)) {
216 0           push @dbg, "detect: xvt via procname ($proc)";
217 0           $info->{emulator_software} = $proc;
218 0           _set_engine($info, 'xvt');
219 0           last DETECT;
220             }
221             }
222              
223             DETECT_GENERIC:
224             {
225 0 0         unless (exists $info->{color_depth}) {
  0            
226 0 0         if ($ENV{TERM} =~ /256color/) {
227 0           push @dbg, "detect color_depth: 256 via TERM env";
228 0           $info->{color_depth} = 256;
229             } else {
230 0           require File::Which;
231 0 0         if (File::Which::which("tput")) {
232 0           my $res = `tput colors` + 0;
233 0           push @dbg, "detect color_depth: $res via tput";
234 0 0         $res = 16 if $res == 8; # 8 is basically 16 (8 low-intensity + 8 high-intensity)
235 0           $info->{color_depth} = $res;
236             }
237             }
238             }
239              
240 0   0       $info->{emulator_software} //= '(generic)';
241 0   0       $info->{emulator_engine} //= '(generic)';
242 0   0       $info->{unicode} //= 0;
243 0   0       $info->{color_depth} //= 0;
244 0   0       $info->{box_chars} //= 0;
245 0   0       $info->{default_bgcolor} //= '000000';
246             }
247              
248             } # DETECT
249              
250             # some additional detections
251              
252             # we're running under emacs, it doesn't support box chars
253 0 0         if ($ENV{INSIDE_EMACS}) {
254 0           $info->{inside_emacs} = 1;
255 0           $info->{box_chars} = 0;
256             }
257              
258 0           $info;
259             }
260              
261             1;
262             # ABSTRACT: Detect terminal (emulator) software and its capabilities
263              
264             __END__