titlestack.pl revision a5ae21e4
1a5ae21e4Smrg#!/usr/bin/env perl
2a5ae21e4Smrg# $XTermId: titlestack.pl,v 1.29 2019/09/20 00:50:10 tom Exp $
3a5ae21e4Smrg# -----------------------------------------------------------------------------
4a5ae21e4Smrg# this file is part of xterm
5a5ae21e4Smrg#
6a5ae21e4Smrg# Copyright 2019 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;
51a5ae21e4Smrgour ( $opt_b, $opt_g, $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
57a5ae21e4Smrgour $TM;            # current titleModes, in various combinations
58a5ae21e4Smrg
59a5ae21e4Smrgour $utf8_sample = 0;
60a5ae21e4Smrg
61a5ae21e4Smrgour $CSI = "\x1b[";
62a5ae21e4Smrgour $OSC = "\x1b]";
63a5ae21e4Smrgour $ST  = "\x1b\\";
64a5ae21e4Smrg
65a5ae21e4Smrgsub SendHEX()  { return ( $TM & 1 ) ? 1 : 0; }
66a5ae21e4Smrgsub ReadHEX()  { return ( $TM & 2 ) ? 1 : 0; }
67a5ae21e4Smrgsub SendUTF8() { return ( $TM & 4 ) ? 1 : 0; }
68a5ae21e4Smrgsub ReadUTF8() { return ( $TM & 8 ) ? 1 : 0; }
69a5ae21e4Smrg
70a5ae21e4Smrgsub to_hex($) {
71a5ae21e4Smrg    my $value  = shift;
72a5ae21e4Smrg    my $result = "";
73a5ae21e4Smrg    my $n;
74a5ae21e4Smrg
75a5ae21e4Smrg    for ( $n = 0 ; $n < length($value) ; ++$n ) {
76a5ae21e4Smrg        $result .= sprintf( "%02X", ord substr( $value, $n, 1 ) );
77a5ae21e4Smrg    }
78a5ae21e4Smrg    return $result;
79a5ae21e4Smrg}
80a5ae21e4Smrg
81a5ae21e4Smrgsub from_hex($) {
82a5ae21e4Smrg    my $value  = shift;
83a5ae21e4Smrg    my $result = "";
84a5ae21e4Smrg    if ( $value =~ /^[[:xdigit:]]+$/ and ( length($value) % 2 ) == 0 ) {
85a5ae21e4Smrg        my $octets = "";
86a5ae21e4Smrg        for ( my $n = 0 ; $n < length($value) ; $n += 2 ) {
87a5ae21e4Smrg            my $pair = substr( $value, $n, 2 );
88a5ae21e4Smrg            my $data = hex $pair;
89a5ae21e4Smrg            $octets .= chr($data);
90a5ae21e4Smrg        }
91a5ae21e4Smrg        $result = decode( &ReadUTF8 ? "utf-8" : "iso-8859-1", $octets );
92a5ae21e4Smrg    }
93a5ae21e4Smrg    else {
94a5ae21e4Smrg        $result = $value;
95a5ae21e4Smrg    }
96a5ae21e4Smrg    return $result;
97a5ae21e4Smrg}
98a5ae21e4Smrg
99a5ae21e4Smrgsub show_string($) {
100a5ae21e4Smrg    my $value = shift;
101a5ae21e4Smrg    my $n;
102a5ae21e4Smrg    my $octets =
103a5ae21e4Smrg      encode( ( ( $encoding eq "utf-8" ) ? "utf-8" : "iso-8859-1" ), $value );
104a5ae21e4Smrg
105a5ae21e4Smrg    my $result = "";
106a5ae21e4Smrg    for ( $n = 0 ; $n < length($octets) ; $n += 1 ) {
107a5ae21e4Smrg        my $c = ord substr( $octets, $n, 1 );
108a5ae21e4Smrg        if ( $c == ord '\\' ) {
109a5ae21e4Smrg            $result .= "\\\\";
110a5ae21e4Smrg        }
111a5ae21e4Smrg        elsif ( $c == 0x1b ) {
112a5ae21e4Smrg            $result .= "\\E";
113a5ae21e4Smrg        }
114a5ae21e4Smrg        elsif ( $c == 0x7f ) {
115a5ae21e4Smrg            $result .= "^?";
116a5ae21e4Smrg        }
117a5ae21e4Smrg        elsif ( $c == 32 ) {
118a5ae21e4Smrg            $result .= "\\s";
119a5ae21e4Smrg        }
120a5ae21e4Smrg        elsif ( $c < 32 ) {
121a5ae21e4Smrg            $result .= sprintf( "^%c", $c + 64 );
122a5ae21e4Smrg        }
123a5ae21e4Smrg        elsif ( $c > 128 ) {
124a5ae21e4Smrg            $result .= sprintf( "\\%03o", $c );
125a5ae21e4Smrg        }
126a5ae21e4Smrg        else {
127a5ae21e4Smrg            $result .= chr($c);
128a5ae21e4Smrg        }
129a5ae21e4Smrg    }
130a5ae21e4Smrg
131a5ae21e4Smrg    printf "%s\r\n", $result;
132a5ae21e4Smrg}
133a5ae21e4Smrg
134a5ae21e4Smrgsub send_command($) {
135a5ae21e4Smrg    my $command = shift;
136a5ae21e4Smrg    if ($opt_v) {
137a5ae21e4Smrg        printf "send: ";
138a5ae21e4Smrg        &show_string($command);
139a5ae21e4Smrg    }
140a5ae21e4Smrg    print STDERR encode( &SendUTF8 ? "utf-8" : "iso-8859-1", $command );
141a5ae21e4Smrg}
142a5ae21e4Smrg
143a5ae21e4Smrgsub get_reply($) {
144a5ae21e4Smrg    my $command = shift;
145a5ae21e4Smrg    my $reply   = "";
146a5ae21e4Smrg
147a5ae21e4Smrg    &send_command($command);
148a5ae21e4Smrg    my $start = time;
149a5ae21e4Smrg    while (1) {
150a5ae21e4Smrg        my $test = ReadKey 1;
151a5ae21e4Smrg        last if not defined $test;
152a5ae21e4Smrg        last if ( time > ( $start + 1 ) );
153a5ae21e4Smrg
154a5ae21e4Smrg        $reply .= $test;
155a5ae21e4Smrg    }
156a5ae21e4Smrg    if ($opt_v) {
157a5ae21e4Smrg        printf "read: ";
158a5ae21e4Smrg        &show_string($reply);
159a5ae21e4Smrg    }
160a5ae21e4Smrg    return $reply;
161a5ae21e4Smrg}
162a5ae21e4Smrg
163a5ae21e4Smrgsub get_title($) {
164a5ae21e4Smrg    my $icon   = shift;
165a5ae21e4Smrg    my $reply  = &get_reply( sprintf( "%s%dt", $CSI, $icon ? 20 : 21 ) );
166a5ae21e4Smrg    my $prefix = $icon ? "L" : "l";
167a5ae21e4Smrg
168a5ae21e4Smrg    if ( $opt_8 and ( $reply =~ /^$CSI/ ) ) {
169a5ae21e4Smrg        $reply =~ s/^${CSI}//;
170a5ae21e4Smrg        $reply =~ s/${ST}$//;
171a5ae21e4Smrg    }
172a5ae21e4Smrg    else {
173a5ae21e4Smrg        $reply =~ s/^\x1b//;
174a5ae21e4Smrg        $reply =~ s/^[\[\]]//;
175a5ae21e4Smrg        if ( index( $reply, $ST ) >= 0 ) {
176a5ae21e4Smrg            $reply =~ s/\x1b\\$//;
177a5ae21e4Smrg        }
178a5ae21e4Smrg        else {
179a5ae21e4Smrg            $reply =~ s/\007$//;
180a5ae21e4Smrg        }
181a5ae21e4Smrg    }
182a5ae21e4Smrg    if ( $reply =~ /^$prefix/ ) {
183a5ae21e4Smrg        $reply =~ s/^$prefix//;
184a5ae21e4Smrg        if (&ReadHEX) {
185a5ae21e4Smrg            $reply = &from_hex($reply);
186a5ae21e4Smrg        }
187a5ae21e4Smrg    }
188a5ae21e4Smrg    else {
189a5ae21e4Smrg        $reply = "?" . $reply;
190a5ae21e4Smrg    }
191a5ae21e4Smrg    return $reply;
192a5ae21e4Smrg}
193a5ae21e4Smrg
194a5ae21e4Smrgsub raw() {
195a5ae21e4Smrg    ReadMode 'ultra-raw', 'STDIN';    # allow single-character inputs
196a5ae21e4Smrg}
197a5ae21e4Smrg
198a5ae21e4Smrgsub cooked() {
199a5ae21e4Smrg    ReadMode 'normal';
200a5ae21e4Smrg}
201a5ae21e4Smrg
202a5ae21e4Smrgsub read_cmd($) {
203a5ae21e4Smrg    my $command = shift;
204a5ae21e4Smrg    my @result;
205a5ae21e4Smrg    if ( open( my $fp, "$command |" ) ) {
206a5ae21e4Smrg        binmode( $fp, ":utf8" ) if ( $encoding eq "utf-8" );
207a5ae21e4Smrg        @result = <$fp>;
208a5ae21e4Smrg        close($fp);
209a5ae21e4Smrg        chomp @result;
210a5ae21e4Smrg    }
211a5ae21e4Smrg    return @result;
212a5ae21e4Smrg}
213a5ae21e4Smrg
214a5ae21e4Smrgsub which_modes($) {
215a5ae21e4Smrg    my $modes  = shift;
216a5ae21e4Smrg    my $result = "";
217a5ae21e4Smrg    if ( $modes & 3 ) {
218a5ae21e4Smrg        $result .= "put" if ( ( $modes & 3 ) == 1 );
219a5ae21e4Smrg        $result .= "get" if ( ( $modes & 3 ) == 2 );
220a5ae21e4Smrg        $result .= "p/q" if ( ( $modes & 3 ) == 3 );
221a5ae21e4Smrg        $result .= " hex";
222a5ae21e4Smrg    }
223a5ae21e4Smrg    if ( $modes & 12 ) {
224a5ae21e4Smrg        $modes /= 4;
225a5ae21e4Smrg        $result .= "," unless ( $result eq "" );
226a5ae21e4Smrg        $result .= "put" if ( ( $modes & 3 ) == 1 );
227a5ae21e4Smrg        $result .= "get" if ( ( $modes & 3 ) == 2 );
228a5ae21e4Smrg        $result .= "p/q" if ( ( $modes & 3 ) == 3 );
229a5ae21e4Smrg        $result .= " utf";
230a5ae21e4Smrg    }
231a5ae21e4Smrg    $result = "default" if ( $result eq "" );
232a5ae21e4Smrg    return $result;
233a5ae21e4Smrg}
234a5ae21e4Smrg
235a5ae21e4Smrgsub which_tmode($$) {
236a5ae21e4Smrg    my $set    = shift;
237a5ae21e4Smrg    my $mode   = shift;
238a5ae21e4Smrg    my $result = "";
239a5ae21e4Smrg    $result = "set window/icon labels using hexadecimal"   if ( $mode == 0 );
240a5ae21e4Smrg    $result = "query window/icon labels using hexadecimal" if ( $mode == 1 );
241a5ae21e4Smrg    $result = "set window/icon labels using UTF-8"         if ( $mode == 2 );
242a5ae21e4Smrg    $result = "query window/icon labels using UTF-8"       if ( $mode == 3 );
243a5ae21e4Smrg    $result = "do not " . $result if ( $set == 0 and $result ne "" );
244a5ae21e4Smrg    return $result;
245a5ae21e4Smrg}
246a5ae21e4Smrg
247a5ae21e4Smrgsub get_tmode($) {
248a5ae21e4Smrg    my $set    = shift;
249a5ae21e4Smrg    my $help   = 0;
250a5ae21e4Smrg    my $result = "?";
251a5ae21e4Smrg    while ( $result !~ /^[0123]$/ ) {
252a5ae21e4Smrg        $result = ReadKey 0;
253a5ae21e4Smrg        if ( $result eq "q" ) {
254a5ae21e4Smrg            $result = -1;
255a5ae21e4Smrg            last;
256a5ae21e4Smrg        }
257a5ae21e4Smrg        elsif ( $result eq "?" and not $help ) {
258a5ae21e4Smrg            for my $n ( 0 .. 3 ) {
259a5ae21e4Smrg                printf "\r\n\t%s = %s", $n, &which_tmode( $set, $n );
260a5ae21e4Smrg            }
261a5ae21e4Smrg            printf "\r\n\t:";
262a5ae21e4Smrg            $help = 1;
263a5ae21e4Smrg        }
264a5ae21e4Smrg    }
265a5ae21e4Smrg    if ( $result >= 0 ) {
266a5ae21e4Smrg        printf "[%s]\r\n\t:", &which_tmode( $set, $result );
267a5ae21e4Smrg    }
268a5ae21e4Smrg    return $result;
269a5ae21e4Smrg}
270a5ae21e4Smrg
271a5ae21e4Smrgsub which_item($) {
272a5ae21e4Smrg    my $code   = shift;
273a5ae21e4Smrg    my $result = "";
274a5ae21e4Smrg    $result = "both" if ( $code == 0 );
275a5ae21e4Smrg    $result = "icon" if ( $code == 1 );
276a5ae21e4Smrg    $result = "name" if ( $code == 2 );
277a5ae21e4Smrg    return $result;
278a5ae21e4Smrg}
279a5ae21e4Smrg
280a5ae21e4Smrgsub which_selector($) {
281a5ae21e4Smrg    my $code   = shift;
282a5ae21e4Smrg    my $result = "";
283a5ae21e4Smrg    $result = "both titles"  if ( $code == 0 );
284a5ae21e4Smrg    $result = "icon title"   if ( $code == 1 );
285a5ae21e4Smrg    $result = "window title" if ( $code == 2 );
286a5ae21e4Smrg    return $result;
287a5ae21e4Smrg}
288a5ae21e4Smrg
289a5ae21e4Smrgsub get_selector() {
290a5ae21e4Smrg    my $result = "?";
291a5ae21e4Smrg    my $help   = 0;
292a5ae21e4Smrg    printf "\t:";
293a5ae21e4Smrg    while ( $result !~ /^[012]$/ ) {
294a5ae21e4Smrg        $result = ReadKey 0;
295a5ae21e4Smrg        if ( $result eq "q" ) {
296a5ae21e4Smrg            $result = -1;
297a5ae21e4Smrg            last;
298a5ae21e4Smrg        }
299a5ae21e4Smrg        elsif ( $result eq "l" ) {
300a5ae21e4Smrg            $result = 2;
301a5ae21e4Smrg        }
302a5ae21e4Smrg        elsif ( $result eq "L" ) {
303a5ae21e4Smrg            $result = 1;
304a5ae21e4Smrg        }
305a5ae21e4Smrg        elsif ( $result eq "?" and not $help ) {
306a5ae21e4Smrg            for my $n ( 0 .. 2 ) {
307a5ae21e4Smrg                printf "\r\n\t%d = %s", $n, &which_selector($n);
308a5ae21e4Smrg            }
309a5ae21e4Smrg            printf "\r\n\t:";
310a5ae21e4Smrg            $help = 1;
311a5ae21e4Smrg        }
312a5ae21e4Smrg    }
313a5ae21e4Smrg    if ( $result >= 0 ) {
314a5ae21e4Smrg        printf "[%s]\r\n\t:", &which_selector($result);
315a5ae21e4Smrg    }
316a5ae21e4Smrg    return $result;
317a5ae21e4Smrg}
318a5ae21e4Smrg
319a5ae21e4Smrgsub display_info() {
320a5ae21e4Smrg
321a5ae21e4Smrg    # use xprop to get properties
322a5ae21e4Smrg    my $command = "xprop";
323a5ae21e4Smrg    if ( $ENV{WINDOWID} ) {
324a5ae21e4Smrg        my $windowid = $ENV{WINDOWID};
325a5ae21e4Smrg        $command .= " -id " . $windowid if ( $windowid ne "" );
326a5ae21e4Smrg    }
327a5ae21e4Smrg    else {
328a5ae21e4Smrg        printf "...xprop\r\n";
329a5ae21e4Smrg    }
330a5ae21e4Smrg    my @props = &read_cmd($command);
331a5ae21e4Smrg    for my $n ( 0 .. $#props ) {
332a5ae21e4Smrg        printf "\t%s\r\n", $props[$n]
333a5ae21e4Smrg          if ( index( $props[$n], "WM_NAME(" ) >= 0
334a5ae21e4Smrg            or index( $props[$n], "WM_ICON_NAME(" ) >= 0 );
335a5ae21e4Smrg    }
336a5ae21e4Smrg
337a5ae21e4Smrg    # use escape sequences to get corresponding information
338a5ae21e4Smrg    printf "... Icon title:%s\r\n",   &get_title(1);
339a5ae21e4Smrg    printf "... Window title:%s\r\n", &get_title(0);
340a5ae21e4Smrg
341a5ae21e4Smrg    # show title-stack (and modes used for each level)
342a5ae21e4Smrg    printf "... Modes[%s]\r\n",  &which_modes($TM);
343a5ae21e4Smrg    printf "... Stack(%d):\r\n", $SP;
344a5ae21e4Smrg    for my $n ( 0 .. $SP ) {
345a5ae21e4Smrg        printf "\t%d [%s:%s]%s\r\n", $n, &which_item( $item_stack[$n] ),
346a5ae21e4Smrg          &which_modes( $mode_stack[$n] ), $titlestack[$n];
347a5ae21e4Smrg    }
348a5ae21e4Smrg}
349a5ae21e4Smrg
350a5ae21e4Smrgsub set_titlemode($) {
351a5ae21e4Smrg    my $set  = shift;
352a5ae21e4Smrg    my $opts = "";
353a5ae21e4Smrg    my $opt;
354a5ae21e4Smrg    printf "\t:";
355a5ae21e4Smrg    while ( ( $opt = &get_tmode($set) ) >= 0 ) {
356a5ae21e4Smrg        $TM |= ( 1 << $opt ) if ($set);
357a5ae21e4Smrg        $TM &= ~( 1 << $opt ) unless ($set);
358a5ae21e4Smrg        $opts .= ";" unless ( $opts eq "" );
359a5ae21e4Smrg        $opts .= $opt;
360a5ae21e4Smrg    }
361a5ae21e4Smrg    if ( $opts ne "" ) {
362a5ae21e4Smrg        &send_command( sprintf( "%s>%s%s", $CSI, $opts, $set ? "t" : "T" ) );
363a5ae21e4Smrg    }
364a5ae21e4Smrg}
365a5ae21e4Smrg
366a5ae21e4Smrgsub utf8_sample($) {
367a5ae21e4Smrg    my $item = shift;
368a5ae21e4Smrg    my $last = 4;
369a5ae21e4Smrg    my $text;
370a5ae21e4Smrg    if ( ( $item % $last ) == 0 ) {
371a5ae21e4Smrg        my $chars = "THE QUICK BROWN FOX\nJUMPED OVER THE LAZY DOG";
372a5ae21e4Smrg        $text = "";
373a5ae21e4Smrg        for my $n ( 0 .. length($chars) ) {
374a5ae21e4Smrg            my $chr = substr( $chars, $n, 1 );
375a5ae21e4Smrg            if ( $chr eq " " ) {
376a5ae21e4Smrg                $chr = "  ";
377a5ae21e4Smrg            }
378a5ae21e4Smrg            elsif ( ord($chr) < 32 ) {
379a5ae21e4Smrg
380a5ae21e4Smrg                # leave control characters as-is
381a5ae21e4Smrg            }
382a5ae21e4Smrg            else {
383a5ae21e4Smrg                $chr = chr( 0xff00 + ord($chr) - 32 );
384a5ae21e4Smrg            }
385a5ae21e4Smrg            $text .= $chr;
386a5ae21e4Smrg        }
387a5ae21e4Smrg    }
388a5ae21e4Smrg    elsif ( ( $item % $last ) == 1 ) {
389a5ae21e4Smrg        $text = chr(0x442) . chr(0x435) . chr(0x441) . chr(0x442);
390a5ae21e4Smrg    }
391a5ae21e4Smrg    elsif ( ( $item % $last ) == 2 ) {
392a5ae21e4Smrg        for my $chr ( 0x391 .. 0x3a9 ) {
393a5ae21e4Smrg            $text .= chr($chr);
394a5ae21e4Smrg        }
395a5ae21e4Smrg    }
396a5ae21e4Smrg    elsif ( ( $item % $last ) == 3 ) {
397a5ae21e4Smrg        for my $chr ( 0x3b1 .. 0x3c9 ) {
398a5ae21e4Smrg            $text .= chr($chr);
399a5ae21e4Smrg        }
400a5ae21e4Smrg    }
401a5ae21e4Smrg    return $text;
402a5ae21e4Smrg}
403a5ae21e4Smrg
404a5ae21e4Smrgsub set_titletext() {
405a5ae21e4Smrg    my $opt = &get_selector;
406a5ae21e4Smrg    if ( $opt >= 0 ) {
407a5ae21e4Smrg        my $text;
408a5ae21e4Smrg        if ($opt_g) {
409a5ae21e4Smrg
410a5ae21e4Smrg            if (&SendUTF8) {
411a5ae21e4Smrg                $text = &utf8_sample( $utf8_sample++ );
412a5ae21e4Smrg            }
413a5ae21e4Smrg            else {
414a5ae21e4Smrg                # ugly code, but mapping the a/e/i/o/u uppercase accented
415a5ae21e4Smrg                # characters that repeat.
416a5ae21e4Smrg                my $a_chars = chr(192) . chr(193) . chr(194) . chr(196);
417a5ae21e4Smrg                my $e_chars = "";
418a5ae21e4Smrg                my $i_chars = " ";
419a5ae21e4Smrg                my $o_chars = chr(210) . chr(211) . chr(212) . chr(214);
420a5ae21e4Smrg                my $u_chars = "";
421a5ae21e4Smrg                my $gap     = " " . chr(215) . " ";
422a5ae21e4Smrg                for my $chr ( 0 .. 3 ) {
423a5ae21e4Smrg                    $e_chars .= chr( $chr + 200 );
424a5ae21e4Smrg                    $i_chars .= chr( $chr + 204 ) . " ";
425a5ae21e4Smrg                    $u_chars .= chr( $chr + 217 );
426a5ae21e4Smrg                }
427a5ae21e4Smrg                $text =
428a5ae21e4Smrg                    $a_chars
429a5ae21e4Smrg                  . $gap
430a5ae21e4Smrg                  . $e_chars
431a5ae21e4Smrg                  . $gap
432a5ae21e4Smrg                  . $i_chars
433a5ae21e4Smrg                  . $gap
434a5ae21e4Smrg                  . $o_chars
435a5ae21e4Smrg                  . $gap
436a5ae21e4Smrg                  . $u_chars;
437a5ae21e4Smrg            }
438a5ae21e4Smrg            printf "%s\r\n", $text;
439a5ae21e4Smrg        }
440a5ae21e4Smrg        else {
441a5ae21e4Smrg            &cooked;
442a5ae21e4Smrg            $text = ReadLine 0;
443a5ae21e4Smrg            chomp $text;
444a5ae21e4Smrg            &raw;
445a5ae21e4Smrg        }
446a5ae21e4Smrg        $titlestack[$SP] = $text;
447a5ae21e4Smrg        $item_stack[$SP] = $opt;
448a5ae21e4Smrg        $mode_stack[$SP] = $TM;
449a5ae21e4Smrg        if (&SendHEX) {
450a5ae21e4Smrg            my $octets =
451a5ae21e4Smrg              encode( ( &SendUTF8 ? "utf-8" : "iso-8859-1" ), $text );
452a5ae21e4Smrg            $text = &to_hex($octets);
453a5ae21e4Smrg        }
454a5ae21e4Smrg        &send_command( sprintf( "%s%s;%s%s", $OSC, $opt, $text, $ST ) );
455a5ae21e4Smrg    }
456a5ae21e4Smrg}
457a5ae21e4Smrg
458a5ae21e4Smrgsub save_title() {
459a5ae21e4Smrg    my $opt = &get_selector;
460a5ae21e4Smrg    if ( $opt >= 0 ) {
461a5ae21e4Smrg        &send_command( sprintf( "%s22;%st", $CSI, $opt ) );
462a5ae21e4Smrg        ++$SP;
463a5ae21e4Smrg        $titlestack[$SP] = $titlestack[ $SP - 1 ];
464a5ae21e4Smrg        $item_stack[$SP] = $opt;
465a5ae21e4Smrg        $mode_stack[$SP] = $mode_stack[ $SP - 1 ];
466a5ae21e4Smrg    }
467a5ae21e4Smrg}
468a5ae21e4Smrg
469a5ae21e4Smrgsub restore_title($) {
470a5ae21e4Smrg    my $set = shift;
471a5ae21e4Smrg    my $opt = &get_selector unless ($set);
472a5ae21e4Smrg    if ( $opt >= 0 and $SP > 0 ) {
473a5ae21e4Smrg        $opt = $item_stack[$SP] if ($set);
474a5ae21e4Smrg        &send_command( sprintf( "%s23;%st", $CSI, $opt ) );
475a5ae21e4Smrg        $SP--;
476a5ae21e4Smrg    }
477a5ae21e4Smrg}
478a5ae21e4Smrg
479a5ae21e4Smrgsub get_xprop($$) {
480a5ae21e4Smrg    my $id   = shift;
481a5ae21e4Smrg    my $name = shift;
482a5ae21e4Smrg    my @data = &read_cmd("xprop -id $id");
483a5ae21e4Smrg    my $prop = "";
484a5ae21e4Smrg    for my $n ( 0 .. $#data ) {
485a5ae21e4Smrg        if ( $data[$n] =~ /$name\([^)]+\) =/ ) {
486a5ae21e4Smrg            $prop = $data[$n];
487a5ae21e4Smrg            $prop =~ s/^[^=]*=\s*//;
488a5ae21e4Smrg            $prop =~ s/"//g;
489a5ae21e4Smrg            last;
490a5ae21e4Smrg        }
491a5ae21e4Smrg    }
492a5ae21e4Smrg    return $prop;
493a5ae21e4Smrg}
494a5ae21e4Smrg
495a5ae21e4Smrgsub get_WM_NAME() {
496a5ae21e4Smrg    $wm_name = "missing WM_NAME";
497a5ae21e4Smrg    my $supwin = `xprop -root '_NET_SUPPORTING_WM_CHECK'`;
498a5ae21e4Smrg    if ( $supwin ne "" ) {
499a5ae21e4Smrg        $supwin =~ s/^.*(0x[[:xdigit:]]+).*/$1/;
500a5ae21e4Smrg        $wm_name = &get_xprop( $supwin, "_NET_WM_NAME" );
501a5ae21e4Smrg        $wm_name = "unknown" if ( $wm_name eq "" );
502a5ae21e4Smrg        printf "** using \"$wm_name\" window manager\n";
503a5ae21e4Smrg    }
504a5ae21e4Smrg}
505a5ae21e4Smrg
506a5ae21e4Smrgsub main::HELP_MESSAGE() {
507a5ae21e4Smrg    printf STDERR <<EOF
508a5ae21e4SmrgUsage: $0 [options]
509a5ae21e4SmrgOptions:
510a5ae21e4Smrg  -8      use 8-bit controls
511a5ae21e4Smrg  -b      use BEL rather than ST for terminating strings
512a5ae21e4Smrg  -g      generate title-strings rather than prompting
513a5ae21e4Smrg  -v      verbose
514a5ae21e4SmrgEOF
515a5ae21e4Smrg      ;
516a5ae21e4Smrg    exit 1;
517a5ae21e4Smrg}
518a5ae21e4Smrg
519a5ae21e4Smrg$Getopt::Std::STANDARD_HELP_VERSION = 1;
520a5ae21e4Smrg&getopts('bgv8') || &main::HELP_MESSAGE;
521a5ae21e4Smrg
522a5ae21e4Smrg$ST = "\007" if ($opt_b);
523a5ae21e4Smrg
524a5ae21e4Smrg$titlestack[ $SP = 0 ] = "unknown";
525a5ae21e4Smrg$item_stack[$SP] = 0;
526a5ae21e4Smrg$mode_stack[$SP] = $TM = 0;
527a5ae21e4Smrg
528a5ae21e4Smrgbinmode( STDOUT, ":utf8" ) if ( $encoding eq "utf-8" );
529a5ae21e4Smrgif ($opt_8) {
530a5ae21e4Smrg    if ( $encoding eq "utf-8" ) {
531a5ae21e4Smrg        undef $opt_8;
532a5ae21e4Smrg        printf "...ignoring -8 option since locale uses %s\n", $encoding;
533a5ae21e4Smrg    }
534a5ae21e4Smrg    else {
535a5ae21e4Smrg        printf STDERR "\x1b G";
536a5ae21e4Smrg        $CSI = "\x9b";
537a5ae21e4Smrg        $OSC = "\x9d";
538a5ae21e4Smrg        $ST  = "\x9c";
539a5ae21e4Smrg    }
540a5ae21e4Smrg}
541a5ae21e4Smrg
542a5ae21e4Smrg&get_WM_NAME;
543a5ae21e4Smrg
544a5ae21e4Smrg&raw;
545a5ae21e4Smrg&raw;
546a5ae21e4Smrgwhile (1) {
547a5ae21e4Smrg    my $cmd;
548a5ae21e4Smrg
549a5ae21e4Smrg    printf "\r\nCommand (? for help):";
550a5ae21e4Smrg    $cmd = ReadKey 0;
551a5ae21e4Smrg    if ( not $cmd ) {
552a5ae21e4Smrg        sleep 1;
553a5ae21e4Smrg    }
554a5ae21e4Smrg    elsif ( $cmd eq "?" ) {
555a5ae21e4Smrg        printf "\r\n? help,"
556a5ae21e4Smrg          . " d=display,"
557a5ae21e4Smrg          . " m/M=set/reset mode,"
558a5ae21e4Smrg          . " p=set title,"
559a5ae21e4Smrg          . " q=quit,"
560a5ae21e4Smrg          . " r=restore,"
561a5ae21e4Smrg          . " s=save\r\n";
562a5ae21e4Smrg    }
563a5ae21e4Smrg    elsif ( $cmd eq "#" ) {
564a5ae21e4Smrg        printf " ...comment\r\n\t#";
565a5ae21e4Smrg        &cooked;
566a5ae21e4Smrg        ReadLine 0;
567a5ae21e4Smrg        &raw;
568a5ae21e4Smrg    }
569a5ae21e4Smrg    elsif ( $cmd eq "!" ) {
570a5ae21e4Smrg        printf " ...shell\r\n";
571a5ae21e4Smrg        &cooked;
572a5ae21e4Smrg        system( $ENV{SHELL} );
573a5ae21e4Smrg        &raw;
574a5ae21e4Smrg    }
575a5ae21e4Smrg    elsif ( $cmd eq "d" ) {
576a5ae21e4Smrg        printf " ...display\r\n";
577a5ae21e4Smrg        &display_info;
578a5ae21e4Smrg    }
579a5ae21e4Smrg    elsif ( $cmd eq "p" ) {
580a5ae21e4Smrg        printf " ...set text\r\n";
581a5ae21e4Smrg        &set_titletext;
582a5ae21e4Smrg    }
583a5ae21e4Smrg    elsif ( $cmd eq "q" ) {
584a5ae21e4Smrg        printf " ...quit\r\n";
585a5ae21e4Smrg        last;
586a5ae21e4Smrg    }
587a5ae21e4Smrg    elsif ( $cmd eq "s" ) {
588a5ae21e4Smrg        printf " ...save title\r\n";
589a5ae21e4Smrg        &save_title;
590a5ae21e4Smrg    }
591a5ae21e4Smrg    elsif ( $cmd eq "r" ) {
592a5ae21e4Smrg        printf " ...restore title\r\n";
593a5ae21e4Smrg        &restore_title(0);
594a5ae21e4Smrg    }
595a5ae21e4Smrg    elsif ( $cmd eq "m" ) {
596a5ae21e4Smrg        printf " ...set title mode\r\n";
597a5ae21e4Smrg        &set_titlemode(1);
598a5ae21e4Smrg    }
599a5ae21e4Smrg    elsif ( $cmd eq "M" ) {
600a5ae21e4Smrg        printf " ...reset title mode\r\n";
601a5ae21e4Smrg        &set_titlemode(0);
602a5ae21e4Smrg    }
603a5ae21e4Smrg}
604a5ae21e4Smrg
605a5ae21e4Smrg# when unstacking here, just use the selector used for the push
606a5ae21e4Smrgwhile ( $SP > 0 ) {
607a5ae21e4Smrg    &restore_title(1);
608a5ae21e4Smrg}
609a5ae21e4Smrg
610a5ae21e4Smrg&send_command( sprintf( "%s>T", $CSI ) );    # reset title-modes to default
611a5ae21e4Smrg
612a5ae21e4Smrg&cooked;
613a5ae21e4Smrg
614a5ae21e4Smrgprintf "\x1b F" if ($opt_8);
615