1a5ae21e4Smrg#!/usr/bin/env perl 25104ee6eSmrg# $XTermId: titlestack.pl,v 1.35 2024/11/29 01:09:46 tom Exp $ 3a5ae21e4Smrg# ----------------------------------------------------------------------------- 4a5ae21e4Smrg# this file is part of xterm 5a5ae21e4Smrg# 65104ee6eSmrg# Copyright 2019,2024 by Thomas E. Dickey 7a5ae21e4Smrg# 8a5ae21e4Smrg# All Rights Reserved 9a5ae21e4Smrg# 10a5ae21e4Smrg# Permission is hereby granted, free of charge, to any person obtaining a 11a5ae21e4Smrg# copy of this software and associated documentation files (the 12a5ae21e4Smrg# "Software"), to deal in the Software without restriction, including 13a5ae21e4Smrg# without limitation the rights to use, copy, modify, merge, publish, 14a5ae21e4Smrg# distribute, sublicense, and/or sell copies of the Software, and to 15a5ae21e4Smrg# permit persons to whom the Software is furnished to do so, subject to 16a5ae21e4Smrg# the following conditions: 17a5ae21e4Smrg# 18a5ae21e4Smrg# The above copyright notice and this permission notice shall be included 19a5ae21e4Smrg# in all copies or substantial portions of the Software. 20a5ae21e4Smrg# 21a5ae21e4Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22a5ae21e4Smrg# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23a5ae21e4Smrg# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24a5ae21e4Smrg# IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 25a5ae21e4Smrg# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26a5ae21e4Smrg# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27a5ae21e4Smrg# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28a5ae21e4Smrg# 29a5ae21e4Smrg# Except as contained in this notice, the name(s) of the above copyright 30a5ae21e4Smrg# holders shall not be used in advertising or otherwise to promote the 31a5ae21e4Smrg# sale, use or other dealings in this Software without prior written 32a5ae21e4Smrg# authorization. 33a5ae21e4Smrg# ----------------------------------------------------------------------------- 34a5ae21e4Smrg# Test the title-stack and title-mode options of xterm. 35a5ae21e4Smrg 36a5ae21e4Smrg# TODO: add test for arbitrary x property 37a5ae21e4Smrg# TODO: allow -g and -v options to toggle interactively 38a5ae21e4Smrg 39a5ae21e4Smrguse strict; 40a5ae21e4Smrguse warnings; 41a5ae21e4Smrg 42a5ae21e4Smrguse Getopt::Std; 43a5ae21e4Smrguse Encode qw(decode encode); 44a5ae21e4Smrguse Term::ReadKey; 45a5ae21e4Smrguse I18N::Langinfo qw(langinfo CODESET); 46a5ae21e4Smrg 47a5ae21e4Smrgour $target = ""; 48a5ae21e4Smrg 49a5ae21e4Smrgour $encoding = lc( langinfo( CODESET() ) ); 50a5ae21e4Smrgour $wm_name; 515104ee6eSmrgour ( $opt_b, $opt_c, $opt_g, $opt_l, $opt_s, $opt_v, $opt_8 ); 52a5ae21e4Smrg 53a5ae21e4Smrgour @titlestack; # stack of title-strings, using current encoding 54a5ae21e4Smrgour @item_stack; # selector used when doing a push 55a5ae21e4Smrgour @mode_stack; # titleModes in effect when titlestack was loaded 56a5ae21e4Smrgour $SP; # stack-pointer 575104ee6eSmrgour $SQ = 10; # stack-limit 58a5ae21e4Smrgour $TM; # current titleModes, in various combinations 595104ee6eSmrgour @cmd_buffer; # command-input 605104ee6eSmrgour $cmd_index; # current index in $cmd_buffer[] 615104ee6eSmrgour $log_fp; # logging-output 62a5ae21e4Smrg 63a5ae21e4Smrgour $utf8_sample = 0; 64a5ae21e4Smrg 65a5ae21e4Smrgour $CSI = "\x1b["; 665104ee6eSmrgour $DCS = "\x1bP"; 67a5ae21e4Smrgour $OSC = "\x1b]"; 68a5ae21e4Smrgour $ST = "\x1b\\"; 69a5ae21e4Smrg 70a5ae21e4Smrgsub SendHEX() { return ( $TM & 1 ) ? 1 : 0; } 71a5ae21e4Smrgsub ReadHEX() { return ( $TM & 2 ) ? 1 : 0; } 72a5ae21e4Smrgsub SendUTF8() { return ( $TM & 4 ) ? 1 : 0; } 73a5ae21e4Smrgsub ReadUTF8() { return ( $TM & 8 ) ? 1 : 0; } 74a5ae21e4Smrg 75a5ae21e4Smrgsub to_hex($) { 76a5ae21e4Smrg my $value = shift; 77a5ae21e4Smrg my $result = ""; 78a5ae21e4Smrg my $n; 79a5ae21e4Smrg 80a5ae21e4Smrg for ( $n = 0 ; $n < length($value) ; ++$n ) { 81a5ae21e4Smrg $result .= sprintf( "%02X", ord substr( $value, $n, 1 ) ); 82a5ae21e4Smrg } 83a5ae21e4Smrg return $result; 84a5ae21e4Smrg} 85a5ae21e4Smrg 86a5ae21e4Smrgsub from_hex($) { 87a5ae21e4Smrg my $value = shift; 88a5ae21e4Smrg my $result = ""; 89a5ae21e4Smrg if ( $value =~ /^[[:xdigit:]]+$/ and ( length($value) % 2 ) == 0 ) { 90a5ae21e4Smrg my $octets = ""; 91a5ae21e4Smrg for ( my $n = 0 ; $n < length($value) ; $n += 2 ) { 92a5ae21e4Smrg my $pair = substr( $value, $n, 2 ); 93a5ae21e4Smrg my $data = hex $pair; 94a5ae21e4Smrg $octets .= chr($data); 95a5ae21e4Smrg } 96a5ae21e4Smrg $result = decode( &ReadUTF8 ? "utf-8" : "iso-8859-1", $octets ); 97a5ae21e4Smrg } 98a5ae21e4Smrg else { 99a5ae21e4Smrg $result = $value; 100a5ae21e4Smrg } 101a5ae21e4Smrg return $result; 102a5ae21e4Smrg} 103a5ae21e4Smrg 104a5ae21e4Smrgsub show_string($) { 105a5ae21e4Smrg my $value = shift; 106a5ae21e4Smrg my $n; 107a5ae21e4Smrg my $octets = 108a5ae21e4Smrg encode( ( ( $encoding eq "utf-8" ) ? "utf-8" : "iso-8859-1" ), $value ); 109a5ae21e4Smrg 110a5ae21e4Smrg my $result = ""; 111a5ae21e4Smrg for ( $n = 0 ; $n < length($octets) ; $n += 1 ) { 112a5ae21e4Smrg my $c = ord substr( $octets, $n, 1 ); 113a5ae21e4Smrg if ( $c == ord '\\' ) { 114a5ae21e4Smrg $result .= "\\\\"; 115a5ae21e4Smrg } 116a5ae21e4Smrg elsif ( $c == 0x1b ) { 117a5ae21e4Smrg $result .= "\\E"; 118a5ae21e4Smrg } 119a5ae21e4Smrg elsif ( $c == 0x7f ) { 120a5ae21e4Smrg $result .= "^?"; 121a5ae21e4Smrg } 122a5ae21e4Smrg elsif ( $c == 32 ) { 123a5ae21e4Smrg $result .= "\\s"; 124a5ae21e4Smrg } 125a5ae21e4Smrg elsif ( $c < 32 ) { 126a5ae21e4Smrg $result .= sprintf( "^%c", $c + 64 ); 127a5ae21e4Smrg } 128a5ae21e4Smrg elsif ( $c > 128 ) { 129a5ae21e4Smrg $result .= sprintf( "\\%03o", $c ); 130a5ae21e4Smrg } 131a5ae21e4Smrg else { 132a5ae21e4Smrg $result .= chr($c); 133a5ae21e4Smrg } 134a5ae21e4Smrg } 135a5ae21e4Smrg 136a5ae21e4Smrg printf "%s\r\n", $result; 137a5ae21e4Smrg} 138a5ae21e4Smrg 139a5ae21e4Smrgsub send_command($) { 140a5ae21e4Smrg my $command = shift; 141a5ae21e4Smrg if ($opt_v) { 142a5ae21e4Smrg printf "send: "; 143a5ae21e4Smrg &show_string($command); 144a5ae21e4Smrg } 145a5ae21e4Smrg print STDERR encode( &SendUTF8 ? "utf-8" : "iso-8859-1", $command ); 146a5ae21e4Smrg} 147a5ae21e4Smrg 148a5ae21e4Smrgsub get_reply($) { 149a5ae21e4Smrg my $command = shift; 150a5ae21e4Smrg my $reply = ""; 151a5ae21e4Smrg 152a5ae21e4Smrg &send_command($command); 153a5ae21e4Smrg my $start = time; 154a5ae21e4Smrg while (1) { 155a5ae21e4Smrg my $test = ReadKey 1; 156a5ae21e4Smrg last if not defined $test; 157a5ae21e4Smrg last if ( time > ( $start + 1 ) ); 158a5ae21e4Smrg 159a5ae21e4Smrg $reply .= $test; 160a5ae21e4Smrg } 161a5ae21e4Smrg if ($opt_v) { 162a5ae21e4Smrg printf "read: "; 163a5ae21e4Smrg &show_string($reply); 164a5ae21e4Smrg } 165a5ae21e4Smrg return $reply; 166a5ae21e4Smrg} 167a5ae21e4Smrg 1685104ee6eSmrgsub get_level() { 1695104ee6eSmrg my $reply = &get_reply( sprintf( "%s#S", $CSI ) ); 1705104ee6eSmrg if ( index( $reply, $CSI ) == 0 ) { 1715104ee6eSmrg $reply = substr( $reply, length($CSI) ); 1725104ee6eSmrg if ( $reply =~ /^\d+;\d+#S$/ ) { 1735104ee6eSmrg $reply =~ s/#S//; 1745104ee6eSmrg my @params = split /;/, $reply; 1755104ee6eSmrg $SP = $params[0]; 1765104ee6eSmrg $SQ = $params[1]; 1775104ee6eSmrg } 1785104ee6eSmrg } 1795104ee6eSmrg} 1805104ee6eSmrg 1815104ee6eSmrgsub get_titlemodes() { 1825104ee6eSmrg my $reply = &get_reply( sprintf( "%s\$q>t%s", $DCS, $ST ) ); 1835104ee6eSmrg my $prefix = "${DCS}1\$r"; 1845104ee6eSmrg my $p = index( $reply, $prefix ); 1855104ee6eSmrg my $q = index( $reply, $ST ); 1865104ee6eSmrg my $r = length($reply) - length($ST); 1875104ee6eSmrg if ( $p == 0 and $q == $r ) { 1885104ee6eSmrg $reply = substr( $reply, 0, $q ); 1895104ee6eSmrg $reply = substr( $reply, length($prefix) ); 1905104ee6eSmrg if ( $reply =~ /^>(\d;)*\dt$/ ) { 1915104ee6eSmrg $reply =~ s/^.(.+).$/$1/; 1925104ee6eSmrg my @modes = split /;/, $reply; 1935104ee6eSmrg $TM = 0; 1945104ee6eSmrg for my $n ( 0 .. $#modes ) { 1955104ee6eSmrg $TM += ( 1 << $n ) if ( $modes[$n] != 0 ); 1965104ee6eSmrg } 1975104ee6eSmrg } 1985104ee6eSmrg } 1995104ee6eSmrg} 2005104ee6eSmrg 201a5ae21e4Smrgsub get_title($) { 202a5ae21e4Smrg my $icon = shift; 203a5ae21e4Smrg my $reply = &get_reply( sprintf( "%s%dt", $CSI, $icon ? 20 : 21 ) ); 204a5ae21e4Smrg my $prefix = $icon ? "L" : "l"; 205a5ae21e4Smrg 206a5ae21e4Smrg if ( $opt_8 and ( $reply =~ /^$CSI/ ) ) { 207a5ae21e4Smrg $reply =~ s/^${CSI}//; 208a5ae21e4Smrg $reply =~ s/${ST}$//; 209a5ae21e4Smrg } 210a5ae21e4Smrg else { 211a5ae21e4Smrg $reply =~ s/^\x1b//; 212a5ae21e4Smrg $reply =~ s/^[\[\]]//; 213a5ae21e4Smrg if ( index( $reply, $ST ) >= 0 ) { 214a5ae21e4Smrg $reply =~ s/\x1b\\$//; 215a5ae21e4Smrg } 216a5ae21e4Smrg else { 217a5ae21e4Smrg $reply =~ s/\007$//; 218a5ae21e4Smrg } 219a5ae21e4Smrg } 220a5ae21e4Smrg if ( $reply =~ /^$prefix/ ) { 221a5ae21e4Smrg $reply =~ s/^$prefix//; 222a5ae21e4Smrg if (&ReadHEX) { 223a5ae21e4Smrg $reply = &from_hex($reply); 224a5ae21e4Smrg } 225a5ae21e4Smrg } 226a5ae21e4Smrg else { 227a5ae21e4Smrg $reply = "?" . $reply; 228a5ae21e4Smrg } 229a5ae21e4Smrg return $reply; 230a5ae21e4Smrg} 231a5ae21e4Smrg 232a5ae21e4Smrgsub raw() { 233a5ae21e4Smrg ReadMode 'ultra-raw', 'STDIN'; # allow single-character inputs 234a5ae21e4Smrg} 235a5ae21e4Smrg 236a5ae21e4Smrgsub cooked() { 237a5ae21e4Smrg ReadMode 'normal'; 238a5ae21e4Smrg} 239a5ae21e4Smrg 2405104ee6eSmrgsub get_cmd() { 2415104ee6eSmrg my $result; 2425104ee6eSmrg select( undef, undef, undef, $opt_s ); 2435104ee6eSmrg if ( $cmd_index <= $#cmd_buffer ) { 2445104ee6eSmrg $result = $cmd_buffer[ $cmd_index++ ]; 2455104ee6eSmrg } 2465104ee6eSmrg else { 2475104ee6eSmrg $result = "q"; 2485104ee6eSmrg } 2495104ee6eSmrg return $result; 2505104ee6eSmrg} 2515104ee6eSmrg 2525104ee6eSmrgsub get_char() { 2535104ee6eSmrg my $result; 2545104ee6eSmrg if ($opt_c) { 2555104ee6eSmrg $result = &get_cmd(); 2565104ee6eSmrg if ( index( $result, "char:" ) == 0 ) { 2575104ee6eSmrg $result = substr( $result, 5 ); 2585104ee6eSmrg } 2595104ee6eSmrg else { 2605104ee6eSmrg $result = "q"; 2615104ee6eSmrg } 2625104ee6eSmrg } 2635104ee6eSmrg else { 2645104ee6eSmrg $result = ReadKey 0; 2655104ee6eSmrg } 2665104ee6eSmrg printf $log_fp "char:%s\n", $result if ($opt_l); 2675104ee6eSmrg return $result; 2685104ee6eSmrg} 2695104ee6eSmrg 2705104ee6eSmrgsub get_line() { 2715104ee6eSmrg my $result; 2725104ee6eSmrg if ($opt_c) { 2735104ee6eSmrg $result = &get_cmd(); 2745104ee6eSmrg if ( index( $result, "line:" ) == 0 ) { 2755104ee6eSmrg $result = substr( $result, 5 ); 2765104ee6eSmrg } 2775104ee6eSmrg else { 2785104ee6eSmrg $result = ""; 2795104ee6eSmrg } 2805104ee6eSmrg } 2815104ee6eSmrg else { 2825104ee6eSmrg &cooked; 2835104ee6eSmrg $result = ReadLine 0; 2845104ee6eSmrg chomp $result; 2855104ee6eSmrg &raw; 2865104ee6eSmrg } 2875104ee6eSmrg printf $log_fp "line:%s\n", $result if ($opt_l); 2885104ee6eSmrg return $result; 2895104ee6eSmrg} 2905104ee6eSmrg 291a5ae21e4Smrgsub read_cmd($) { 292a5ae21e4Smrg my $command = shift; 293a5ae21e4Smrg my @result; 294a5ae21e4Smrg if ( open( my $fp, "$command |" ) ) { 295a5ae21e4Smrg binmode( $fp, ":utf8" ) if ( $encoding eq "utf-8" ); 296a5ae21e4Smrg @result = <$fp>; 297a5ae21e4Smrg close($fp); 298a5ae21e4Smrg chomp @result; 299a5ae21e4Smrg } 300a5ae21e4Smrg return @result; 301a5ae21e4Smrg} 302a5ae21e4Smrg 303a5ae21e4Smrgsub which_modes($) { 304a5ae21e4Smrg my $modes = shift; 305a5ae21e4Smrg my $result = ""; 306a5ae21e4Smrg if ( $modes & 3 ) { 307a5ae21e4Smrg $result .= "put" if ( ( $modes & 3 ) == 1 ); 308a5ae21e4Smrg $result .= "get" if ( ( $modes & 3 ) == 2 ); 309a5ae21e4Smrg $result .= "p/q" if ( ( $modes & 3 ) == 3 ); 310a5ae21e4Smrg $result .= " hex"; 311a5ae21e4Smrg } 312a5ae21e4Smrg if ( $modes & 12 ) { 313a5ae21e4Smrg $modes /= 4; 314a5ae21e4Smrg $result .= "," unless ( $result eq "" ); 315a5ae21e4Smrg $result .= "put" if ( ( $modes & 3 ) == 1 ); 316a5ae21e4Smrg $result .= "get" if ( ( $modes & 3 ) == 2 ); 317a5ae21e4Smrg $result .= "p/q" if ( ( $modes & 3 ) == 3 ); 318a5ae21e4Smrg $result .= " utf"; 319a5ae21e4Smrg } 320a5ae21e4Smrg $result = "default" if ( $result eq "" ); 321a5ae21e4Smrg return $result; 322a5ae21e4Smrg} 323a5ae21e4Smrg 324a5ae21e4Smrgsub which_tmode($$) { 325a5ae21e4Smrg my $set = shift; 326a5ae21e4Smrg my $mode = shift; 327a5ae21e4Smrg my $result = ""; 328a5ae21e4Smrg $result = "set window/icon labels using hexadecimal" if ( $mode == 0 ); 329a5ae21e4Smrg $result = "query window/icon labels using hexadecimal" if ( $mode == 1 ); 330a5ae21e4Smrg $result = "set window/icon labels using UTF-8" if ( $mode == 2 ); 331a5ae21e4Smrg $result = "query window/icon labels using UTF-8" if ( $mode == 3 ); 332a5ae21e4Smrg $result = "do not " . $result if ( $set == 0 and $result ne "" ); 333a5ae21e4Smrg return $result; 334a5ae21e4Smrg} 335a5ae21e4Smrg 336a5ae21e4Smrgsub get_tmode($) { 337a5ae21e4Smrg my $set = shift; 338a5ae21e4Smrg my $help = 0; 339a5ae21e4Smrg my $result = "?"; 340a5ae21e4Smrg while ( $result !~ /^[0123]$/ ) { 3415104ee6eSmrg $result = &get_char; 342a5ae21e4Smrg if ( $result eq "q" ) { 343a5ae21e4Smrg $result = -1; 344a5ae21e4Smrg last; 345a5ae21e4Smrg } 346a5ae21e4Smrg elsif ( $result eq "?" and not $help ) { 347a5ae21e4Smrg for my $n ( 0 .. 3 ) { 348a5ae21e4Smrg printf "\r\n\t%s = %s", $n, &which_tmode( $set, $n ); 349a5ae21e4Smrg } 350a5ae21e4Smrg printf "\r\n\t:"; 351a5ae21e4Smrg $help = 1; 352a5ae21e4Smrg } 353a5ae21e4Smrg } 354a5ae21e4Smrg if ( $result >= 0 ) { 355a5ae21e4Smrg printf "[%s]\r\n\t:", &which_tmode( $set, $result ); 356a5ae21e4Smrg } 357a5ae21e4Smrg return $result; 358a5ae21e4Smrg} 359a5ae21e4Smrg 360a5ae21e4Smrgsub which_item($) { 361a5ae21e4Smrg my $code = shift; 362a5ae21e4Smrg my $result = ""; 363a5ae21e4Smrg $result = "both" if ( $code == 0 ); 364a5ae21e4Smrg $result = "icon" if ( $code == 1 ); 365a5ae21e4Smrg $result = "name" if ( $code == 2 ); 366a5ae21e4Smrg return $result; 367a5ae21e4Smrg} 368a5ae21e4Smrg 369a5ae21e4Smrgsub which_selector($) { 370a5ae21e4Smrg my $code = shift; 371a5ae21e4Smrg my $result = ""; 372a5ae21e4Smrg $result = "both titles" if ( $code == 0 ); 373a5ae21e4Smrg $result = "icon title" if ( $code == 1 ); 374a5ae21e4Smrg $result = "window title" if ( $code == 2 ); 375a5ae21e4Smrg return $result; 376a5ae21e4Smrg} 377a5ae21e4Smrg 378a5ae21e4Smrgsub get_selector() { 379a5ae21e4Smrg my $result = "?"; 380a5ae21e4Smrg my $help = 0; 381a5ae21e4Smrg printf "\t:"; 382a5ae21e4Smrg while ( $result !~ /^[012]$/ ) { 3835104ee6eSmrg $result = &get_char; 384a5ae21e4Smrg if ( $result eq "q" ) { 385a5ae21e4Smrg $result = -1; 386a5ae21e4Smrg last; 387a5ae21e4Smrg } 388a5ae21e4Smrg elsif ( $result eq "l" ) { 389a5ae21e4Smrg $result = 2; 390a5ae21e4Smrg } 391a5ae21e4Smrg elsif ( $result eq "L" ) { 392a5ae21e4Smrg $result = 1; 393a5ae21e4Smrg } 394a5ae21e4Smrg elsif ( $result eq "?" and not $help ) { 395a5ae21e4Smrg for my $n ( 0 .. 2 ) { 396a5ae21e4Smrg printf "\r\n\t%d = %s", $n, &which_selector($n); 397a5ae21e4Smrg } 398a5ae21e4Smrg printf "\r\n\t:"; 399a5ae21e4Smrg $help = 1; 400a5ae21e4Smrg } 401a5ae21e4Smrg } 402a5ae21e4Smrg if ( $result >= 0 ) { 403a5ae21e4Smrg printf "[%s]\r\n\t:", &which_selector($result); 404a5ae21e4Smrg } 405a5ae21e4Smrg return $result; 406a5ae21e4Smrg} 407a5ae21e4Smrg 408a5ae21e4Smrgsub display_info() { 409a5ae21e4Smrg 410a5ae21e4Smrg # use xprop to get properties 411a5ae21e4Smrg my $command = "xprop"; 412a5ae21e4Smrg if ( $ENV{WINDOWID} ) { 413a5ae21e4Smrg my $windowid = $ENV{WINDOWID}; 414a5ae21e4Smrg $command .= " -id " . $windowid if ( $windowid ne "" ); 415a5ae21e4Smrg } 416a5ae21e4Smrg else { 417a5ae21e4Smrg printf "...xprop\r\n"; 418a5ae21e4Smrg } 419a5ae21e4Smrg my @props = &read_cmd($command); 420a5ae21e4Smrg for my $n ( 0 .. $#props ) { 421a5ae21e4Smrg printf "\t%s\r\n", $props[$n] 422a5ae21e4Smrg if ( index( $props[$n], "WM_NAME(" ) >= 0 423a5ae21e4Smrg or index( $props[$n], "WM_ICON_NAME(" ) >= 0 ); 424a5ae21e4Smrg } 425a5ae21e4Smrg 426a5ae21e4Smrg # use escape sequences to get corresponding information 427a5ae21e4Smrg printf "... Icon title:%s\r\n", &get_title(1); 428a5ae21e4Smrg printf "... Window title:%s\r\n", &get_title(0); 429a5ae21e4Smrg 430a5ae21e4Smrg # show title-stack (and modes used for each level) 431a5ae21e4Smrg printf "... Modes[%s]\r\n", &which_modes($TM); 432a5ae21e4Smrg printf "... Stack(%d):\r\n", $SP; 433a5ae21e4Smrg for my $n ( 0 .. $SP ) { 434a5ae21e4Smrg printf "\t%d [%s:%s]%s\r\n", $n, &which_item( $item_stack[$n] ), 435a5ae21e4Smrg &which_modes( $mode_stack[$n] ), $titlestack[$n]; 436a5ae21e4Smrg } 437a5ae21e4Smrg} 438a5ae21e4Smrg 439a5ae21e4Smrgsub set_titlemode($) { 440a5ae21e4Smrg my $set = shift; 441a5ae21e4Smrg my $opts = ""; 442a5ae21e4Smrg my $opt; 443a5ae21e4Smrg printf "\t:"; 444a5ae21e4Smrg while ( ( $opt = &get_tmode($set) ) >= 0 ) { 445a5ae21e4Smrg $TM |= ( 1 << $opt ) if ($set); 446a5ae21e4Smrg $TM &= ~( 1 << $opt ) unless ($set); 447a5ae21e4Smrg $opts .= ";" unless ( $opts eq "" ); 448a5ae21e4Smrg $opts .= $opt; 449a5ae21e4Smrg } 450a5ae21e4Smrg if ( $opts ne "" ) { 451a5ae21e4Smrg &send_command( sprintf( "%s>%s%s", $CSI, $opts, $set ? "t" : "T" ) ); 452a5ae21e4Smrg } 4535104ee6eSmrg 4545104ee6eSmrg if ($opt_l) { 4555104ee6eSmrg my $save = $TM; 4565104ee6eSmrg &get_titlemodes; 4575104ee6eSmrg 4585104ee6eSmrg if ( $TM != $save ) { 4595104ee6eSmrg printf $log_fp "note: expected title-modes $save, got $TM\n"; 4605104ee6eSmrg } 4615104ee6eSmrg } 462a5ae21e4Smrg} 463a5ae21e4Smrg 464a5ae21e4Smrgsub utf8_sample($) { 465a5ae21e4Smrg my $item = shift; 466a5ae21e4Smrg my $last = 4; 467a5ae21e4Smrg my $text; 468a5ae21e4Smrg if ( ( $item % $last ) == 0 ) { 469a5ae21e4Smrg my $chars = "THE QUICK BROWN FOX\nJUMPED OVER THE LAZY DOG"; 470a5ae21e4Smrg $text = ""; 471a5ae21e4Smrg for my $n ( 0 .. length($chars) ) { 472a5ae21e4Smrg my $chr = substr( $chars, $n, 1 ); 473a5ae21e4Smrg if ( $chr eq " " ) { 474a5ae21e4Smrg $chr = " "; 475a5ae21e4Smrg } 476a5ae21e4Smrg elsif ( ord($chr) < 32 ) { 477a5ae21e4Smrg 478a5ae21e4Smrg # leave control characters as-is 479a5ae21e4Smrg } 480a5ae21e4Smrg else { 481a5ae21e4Smrg $chr = chr( 0xff00 + ord($chr) - 32 ); 482a5ae21e4Smrg } 483a5ae21e4Smrg $text .= $chr; 484a5ae21e4Smrg } 485a5ae21e4Smrg } 486a5ae21e4Smrg elsif ( ( $item % $last ) == 1 ) { 487a5ae21e4Smrg $text = chr(0x442) . chr(0x435) . chr(0x441) . chr(0x442); 488a5ae21e4Smrg } 489a5ae21e4Smrg elsif ( ( $item % $last ) == 2 ) { 490a5ae21e4Smrg for my $chr ( 0x391 .. 0x3a9 ) { 491a5ae21e4Smrg $text .= chr($chr); 492a5ae21e4Smrg } 493a5ae21e4Smrg } 494a5ae21e4Smrg elsif ( ( $item % $last ) == 3 ) { 495a5ae21e4Smrg for my $chr ( 0x3b1 .. 0x3c9 ) { 496a5ae21e4Smrg $text .= chr($chr); 497a5ae21e4Smrg } 498a5ae21e4Smrg } 499a5ae21e4Smrg return $text; 500a5ae21e4Smrg} 501a5ae21e4Smrg 502a5ae21e4Smrgsub set_titletext() { 503a5ae21e4Smrg my $opt = &get_selector; 504a5ae21e4Smrg if ( $opt >= 0 ) { 505a5ae21e4Smrg my $text; 506a5ae21e4Smrg if ($opt_g) { 507a5ae21e4Smrg 508a5ae21e4Smrg if (&SendUTF8) { 509a5ae21e4Smrg $text = &utf8_sample( $utf8_sample++ ); 510a5ae21e4Smrg } 511a5ae21e4Smrg else { 512a5ae21e4Smrg # ugly code, but mapping the a/e/i/o/u uppercase accented 513a5ae21e4Smrg # characters that repeat. 514a5ae21e4Smrg my $a_chars = chr(192) . chr(193) . chr(194) . chr(196); 515a5ae21e4Smrg my $e_chars = ""; 516a5ae21e4Smrg my $i_chars = " "; 517a5ae21e4Smrg my $o_chars = chr(210) . chr(211) . chr(212) . chr(214); 518a5ae21e4Smrg my $u_chars = ""; 519a5ae21e4Smrg my $gap = " " . chr(215) . " "; 520a5ae21e4Smrg for my $chr ( 0 .. 3 ) { 521a5ae21e4Smrg $e_chars .= chr( $chr + 200 ); 522a5ae21e4Smrg $i_chars .= chr( $chr + 204 ) . " "; 523a5ae21e4Smrg $u_chars .= chr( $chr + 217 ); 524a5ae21e4Smrg } 525a5ae21e4Smrg $text = 526a5ae21e4Smrg $a_chars 527a5ae21e4Smrg . $gap 528a5ae21e4Smrg . $e_chars 529a5ae21e4Smrg . $gap 530a5ae21e4Smrg . $i_chars 531a5ae21e4Smrg . $gap 532a5ae21e4Smrg . $o_chars 533a5ae21e4Smrg . $gap 534a5ae21e4Smrg . $u_chars; 535a5ae21e4Smrg } 5365104ee6eSmrg &cooked; 537a5ae21e4Smrg printf "%s\r\n", $text; 5385104ee6eSmrg &raw; 539a5ae21e4Smrg } 540a5ae21e4Smrg else { 5415104ee6eSmrg $text = &get_line; 542a5ae21e4Smrg } 543a5ae21e4Smrg $titlestack[$SP] = $text; 544a5ae21e4Smrg $item_stack[$SP] = $opt; 545a5ae21e4Smrg $mode_stack[$SP] = $TM; 546a5ae21e4Smrg if (&SendHEX) { 547a5ae21e4Smrg my $octets = 548a5ae21e4Smrg encode( ( &SendUTF8 ? "utf-8" : "iso-8859-1" ), $text ); 549a5ae21e4Smrg $text = &to_hex($octets); 550a5ae21e4Smrg } 551a5ae21e4Smrg &send_command( sprintf( "%s%s;%s%s", $OSC, $opt, $text, $ST ) ); 552a5ae21e4Smrg } 553a5ae21e4Smrg} 554a5ae21e4Smrg 555a5ae21e4Smrgsub save_title() { 556a5ae21e4Smrg my $opt = &get_selector; 557a5ae21e4Smrg if ( $opt >= 0 ) { 558a5ae21e4Smrg &send_command( sprintf( "%s22;%st", $CSI, $opt ) ); 559a5ae21e4Smrg ++$SP; 560a5ae21e4Smrg $titlestack[$SP] = $titlestack[ $SP - 1 ]; 561a5ae21e4Smrg $item_stack[$SP] = $opt; 562a5ae21e4Smrg $mode_stack[$SP] = $mode_stack[ $SP - 1 ]; 563a5ae21e4Smrg } 564a5ae21e4Smrg} 565a5ae21e4Smrg 566a5ae21e4Smrgsub restore_title($) { 567a5ae21e4Smrg my $set = shift; 568a5ae21e4Smrg my $opt = &get_selector unless ($set); 569a5ae21e4Smrg if ( $opt >= 0 and $SP > 0 ) { 570a5ae21e4Smrg $opt = $item_stack[$SP] if ($set); 571a5ae21e4Smrg &send_command( sprintf( "%s23;%st", $CSI, $opt ) ); 572a5ae21e4Smrg $SP--; 573a5ae21e4Smrg } 574a5ae21e4Smrg} 575a5ae21e4Smrg 576a5ae21e4Smrgsub get_xprop($$) { 577a5ae21e4Smrg my $id = shift; 578a5ae21e4Smrg my $name = shift; 579a5ae21e4Smrg my @data = &read_cmd("xprop -id $id"); 580a5ae21e4Smrg my $prop = ""; 581a5ae21e4Smrg for my $n ( 0 .. $#data ) { 582a5ae21e4Smrg if ( $data[$n] =~ /$name\([^)]+\) =/ ) { 583a5ae21e4Smrg $prop = $data[$n]; 584a5ae21e4Smrg $prop =~ s/^[^=]*=\s*//; 585a5ae21e4Smrg $prop =~ s/"//g; 586a5ae21e4Smrg last; 587a5ae21e4Smrg } 588a5ae21e4Smrg } 589a5ae21e4Smrg return $prop; 590a5ae21e4Smrg} 591a5ae21e4Smrg 592a5ae21e4Smrgsub get_WM_NAME() { 593a5ae21e4Smrg $wm_name = "missing WM_NAME"; 594a5ae21e4Smrg my $supwin = `xprop -root '_NET_SUPPORTING_WM_CHECK'`; 595a5ae21e4Smrg if ( $supwin ne "" ) { 596a5ae21e4Smrg $supwin =~ s/^.*(0x[[:xdigit:]]+).*/$1/; 597a5ae21e4Smrg $wm_name = &get_xprop( $supwin, "_NET_WM_NAME" ); 598a5ae21e4Smrg $wm_name = "unknown" if ( $wm_name eq "" ); 599a5ae21e4Smrg printf "** using \"$wm_name\" window manager\n"; 600a5ae21e4Smrg } 601a5ae21e4Smrg} 602a5ae21e4Smrg 603a5ae21e4Smrgsub main::HELP_MESSAGE() { 604a5ae21e4Smrg printf STDERR <<EOF 605a5ae21e4SmrgUsage: $0 [options] 606a5ae21e4SmrgOptions: 607a5ae21e4Smrg -8 use 8-bit controls 608a5ae21e4Smrg -b use BEL rather than ST for terminating strings 6095104ee6eSmrg -c FILE read commands from this file. 610a5ae21e4Smrg -g generate title-strings rather than prompting 6115104ee6eSmrg -l FILE log commands to this file. 6125104ee6eSmrg -s SECS sleep this long each time a command is read from file 613a5ae21e4Smrg -v verbose 614a5ae21e4SmrgEOF 615a5ae21e4Smrg ; 616a5ae21e4Smrg exit 1; 617a5ae21e4Smrg} 618a5ae21e4Smrg 619a5ae21e4Smrg$Getopt::Std::STANDARD_HELP_VERSION = 1; 6205104ee6eSmrg&getopts('bc:gl:s:v8') || &main::HELP_MESSAGE; 6215104ee6eSmrg 6225104ee6eSmrgif ($opt_c) { 6235104ee6eSmrg open( my $cmd_fp, "<", $opt_c ) || &main::HELP_MESSAGE; 6245104ee6eSmrg @cmd_buffer = <$cmd_fp>; 6255104ee6eSmrg close $cmd_fp; 6265104ee6eSmrg chomp @cmd_buffer; 6275104ee6eSmrg $cmd_index = 0; 6285104ee6eSmrg} 6295104ee6eSmrg 6305104ee6eSmrgif ($opt_l) { 6315104ee6eSmrg open( $log_fp, ">", $opt_l ) || &main::HELP_MESSAGE; 6325104ee6eSmrg} 6335104ee6eSmrg 6345104ee6eSmrg$opt_s = "1" unless ( defined($opt_s) and ( $opt_s =~ /^(\d*\.)?\d+$/ ) ); 635a5ae21e4Smrg 636a5ae21e4Smrg$ST = "\007" if ($opt_b); 637a5ae21e4Smrg 6385104ee6eSmrg$SP = 0; 6395104ee6eSmrg$titlestack[$SP] = "unknown"; 640a5ae21e4Smrg$item_stack[$SP] = 0; 641a5ae21e4Smrg$mode_stack[$SP] = $TM = 0; 642a5ae21e4Smrg 643a5ae21e4Smrgbinmode( STDOUT, ":utf8" ) if ( $encoding eq "utf-8" ); 644a5ae21e4Smrgif ($opt_8) { 645a5ae21e4Smrg if ( $encoding eq "utf-8" ) { 646a5ae21e4Smrg undef $opt_8; 647a5ae21e4Smrg printf "...ignoring -8 option since locale uses %s\n", $encoding; 648a5ae21e4Smrg } 649a5ae21e4Smrg else { 650a5ae21e4Smrg printf STDERR "\x1b G"; 651a5ae21e4Smrg $CSI = "\x9b"; 6525104ee6eSmrg $DCS = "\x90"; 653a5ae21e4Smrg $OSC = "\x9d"; 654a5ae21e4Smrg $ST = "\x9c"; 655a5ae21e4Smrg } 656a5ae21e4Smrg} 657a5ae21e4Smrg 658a5ae21e4Smrg&get_WM_NAME; 659a5ae21e4Smrg 660a5ae21e4Smrg&raw; 6615104ee6eSmrg&get_titlemodes; 6625104ee6eSmrg&get_level; 663a5ae21e4Smrg&raw; 664a5ae21e4Smrgwhile (1) { 665a5ae21e4Smrg my $cmd; 666a5ae21e4Smrg 6675104ee6eSmrg printf "\r\n[$SP:$SQ] Command (? for help):"; 6685104ee6eSmrg $cmd = &get_char; 669a5ae21e4Smrg if ( not $cmd ) { 670a5ae21e4Smrg sleep 1; 671a5ae21e4Smrg } 672a5ae21e4Smrg elsif ( $cmd eq "?" ) { 673a5ae21e4Smrg printf "\r\n? help," 674a5ae21e4Smrg . " d=display," 675a5ae21e4Smrg . " m/M=set/reset mode," 676a5ae21e4Smrg . " p=set title," 677a5ae21e4Smrg . " q=quit," 678a5ae21e4Smrg . " r=restore," 679a5ae21e4Smrg . " s=save\r\n"; 680a5ae21e4Smrg } 681a5ae21e4Smrg elsif ( $cmd eq "#" ) { 682a5ae21e4Smrg printf " ...comment\r\n\t#"; 6835104ee6eSmrg &get_line; 684a5ae21e4Smrg } 685a5ae21e4Smrg elsif ( $cmd eq "!" ) { 686a5ae21e4Smrg printf " ...shell\r\n"; 687a5ae21e4Smrg &cooked; 688a5ae21e4Smrg system( $ENV{SHELL} ); 689a5ae21e4Smrg &raw; 690a5ae21e4Smrg } 691a5ae21e4Smrg elsif ( $cmd eq "d" ) { 692a5ae21e4Smrg printf " ...display\r\n"; 693a5ae21e4Smrg &display_info; 694a5ae21e4Smrg } 695a5ae21e4Smrg elsif ( $cmd eq "p" ) { 696a5ae21e4Smrg printf " ...set text\r\n"; 697a5ae21e4Smrg &set_titletext; 698a5ae21e4Smrg } 699a5ae21e4Smrg elsif ( $cmd eq "q" ) { 700a5ae21e4Smrg printf " ...quit\r\n"; 701a5ae21e4Smrg last; 702a5ae21e4Smrg } 703a5ae21e4Smrg elsif ( $cmd eq "s" ) { 704a5ae21e4Smrg printf " ...save title\r\n"; 705a5ae21e4Smrg &save_title; 706a5ae21e4Smrg } 707a5ae21e4Smrg elsif ( $cmd eq "r" ) { 708a5ae21e4Smrg printf " ...restore title\r\n"; 709a5ae21e4Smrg &restore_title(0); 710a5ae21e4Smrg } 711a5ae21e4Smrg elsif ( $cmd eq "m" ) { 712a5ae21e4Smrg printf " ...set title mode\r\n"; 713a5ae21e4Smrg &set_titlemode(1); 714a5ae21e4Smrg } 715a5ae21e4Smrg elsif ( $cmd eq "M" ) { 716a5ae21e4Smrg printf " ...reset title mode\r\n"; 717a5ae21e4Smrg &set_titlemode(0); 718a5ae21e4Smrg } 719a5ae21e4Smrg} 720a5ae21e4Smrg 721a5ae21e4Smrg# when unstacking here, just use the selector used for the push 722a5ae21e4Smrgwhile ( $SP > 0 ) { 723a5ae21e4Smrg &restore_title(1); 724a5ae21e4Smrg} 725a5ae21e4Smrg 726a5ae21e4Smrg&send_command( sprintf( "%s>T", $CSI ) ); # reset title-modes to default 727a5ae21e4Smrg 728a5ae21e4Smrg&cooked; 729a5ae21e4Smrg 730a5ae21e4Smrgprintf "\x1b F" if ($opt_8); 731