xorblink.pl revision a5ae21e4
1#!/usr/bin/env perl
2# $XTermId: xorblink.pl,v 1.15 2017/12/24 21:03:54 tom Exp $
3# walk through the different states of cursor-blinking, with annotation
4#
5# Manual:
6#        +bc     turn off text cursor blinking.  This overrides the cursorBlink
7#                resource.
8#
9#        -bc     turn on text cursor blinking.  This overrides the cursorBlink
10#                resource.
11#
12#        cursorBlink (class CursorBlink)
13#                Specifies whether to make the cursor blink.  The default is
14#                "false".
15#
16#                Xterm-dev uses two variables to determine whether the cursor
17#                blinks.  One is set by this resource.  The other is set by
18#                control sequences (private mode 12 and DECSCUSR).  Xterm-dev
19#                tests the XOR of the two variables.
20#
21#               Enable Blinking Cursor (resource cursorblink)
22#                      Enable (or disable) the blinking-cursor feature.  This
23#                      corresponds to the -bc option and the cursorBlink
24#                      resource.  There is also an escape sequence (see Xterm-
25#                      dev Control Sequences).  The menu entry and the escape
26#                      sequence states are XOR'd: if both are enabled, the
27#                      cursor will not blink, if only one is enabled, the cursor
28#                      will blink.
29#
30#        set-cursorblink(on/off/toggle)
31#                This action sets, unsets or toggles the cursorBlink resource.
32#                It is also invoked from the cursorblink entry in vtMenu.
33#
34# Control sequences:
35#
36# CSI ? Pm h
37#           DEC Private Mode Set (DECSET).
38#             Ps = 1 2  -> Start Blinking Cursor (att610).
39#
40# CSI ? Pm l
41#           DEC Private Mode Reset (DECRST).
42#             Ps = 1 2  -> Stop Blinking Cursor (att610).
43#
44# CSI Ps SP q
45#           Set cursor style (DECSCUSR, VT520).
46#             Ps = 0  -> blinking block.
47#             Ps = 1  -> blinking block (default).
48#             Ps = 2  -> steady block.
49#             Ps = 3  -> blinking underline.
50#             Ps = 4  -> steady underline.
51#             Ps = 5  -> blinking bar (xterm).
52#             Ps = 6  -> steady bar (xterm).
53#
54use strict;
55
56use Term::ReadKey;
57
58use IO::Handle;
59STDERR->autoflush(1);
60STDOUT->autoflush(1);
61
62our %DECSET = (
63    "\e[?12h", "Start Blinking Cursor (AT&T 610)",
64    "\e[?12l", "Stop Blinking Cursor (AT&T 610)"
65);
66
67our %DECSCUSR = (
68    "\e[0 q",
69    "blinking block",
70    "\e[1 q",
71    "blinking block (default)",
72    "\e[2 q",
73    "steady block",
74    "\e[3 q",
75    "blinking underline",
76    "\e[4 q",
77    "steady underline",
78    "\e[5 q",
79    "blinking bar (xterm)",
80    "\e[6 q",
81    "steady bar (xterm)"
82);
83
84sub show($$) {
85    my $seq = shift;
86    my $txt = shift;
87    printf "%s -> %s\n", &visible($seq), $txt;
88}
89
90sub get_reply($$) {
91    my $seq = shift;
92    my $end = shift;
93    printf STDERR "%s", $seq;
94    my $key;
95    my $result = "";
96    $key = ReadKey(0);
97    $result .= $key;
98    if ( $key eq "\e" ) {
99
100        while (1) {
101            $key = ReadKey(100);
102            $result .= $key;
103            next if ( length($result) < length($end) );
104            last if ( substr( $result, -length($end) ) eq $end );
105        }
106    }
107    return $result;
108}
109
110sub mode_value($) {
111    my $value = shift;
112    if ( $value eq 1 ) {
113        $value = "set";
114    }
115    elsif ( $value eq 2 ) {
116        $value = "reset";
117    }
118    elsif ( $value eq 3 ) {
119        $value = "*set";
120    }
121    elsif ( $value eq 4 ) {
122        $value = "*reset";
123    }
124    else {
125        $value = &visible( "?" . $value );
126    }
127    return $value;
128}
129
130sub DECRQM($) {
131    my $mode     = shift;
132    my $sequence = sprintf( "\e[?%d\$p", $mode );
133    my $reply    = &get_reply( $sequence, "y" );
134    if ( $reply =~ /^\e\[\?$mode;\d+\$y$/ ) {
135        $reply =~ s/^\e\[\?$mode;(\d+)\$y$/$1/;
136    }
137    return &mode_value($reply);
138}
139
140sub DECRQSS($) {
141    my $request  = shift;
142    my $ending   = "\e\\";
143    my $sequence = sprintf( "\eP\$q%s$ending", $request );
144    my $reply    = &get_reply( $sequence, $ending );
145
146    # xterm responds with
147    # DCS 1 $ r Pt ST for valid requests,
148    # DCS 0 $ r Pt ST for invalid requests.
149    #if ( $reply =~ /^\eP1\$r.*$ending$/ ) {
150    if ( $reply =~ /^\eP1\$r\d+ q\e\\$/ ) {
151        $reply =~ s/^\eP1\$r(\d+) q\e\\$/$1/;
152    }
153    return &visible($reply);
154}
155
156sub get_key() {
157    my $key;
158    do {
159        $key = ReadKey(0);
160        if ( $key eq "\e" ) {
161            while ( ReadKey(10) !~ /[@-~]/ ) {
162                #
163            }
164        }
165    } while ( $key eq "\e" );
166    return $key;
167}
168
169sub visible($) {
170    my $txt = shift;
171    $txt =~ s/\e/\\e/g;
172    $txt =~ s/\a/\\a/g;
173    return $txt;
174}
175
176sub test($$) {
177    my $set = shift;
178    my $msg = shift;
179
180    ReadMode 'raw';
181
182    printf STDERR "%s\t[", &visible($set);
183
184    # save the cursor position
185    printf STDERR "\e7";
186
187    # send the escape sequence
188    printf STDERR "%s", $set;
189
190    # print the description
191    printf STDERR "X] ";
192
193    printf STDERR " [C=%s,",  &DECRQSS(" q");
194    printf STDERR "B=%s,",    &DECRQM(12);
195    printf STDERR "M=%s,%s]", &DECRQM(13), &DECRQM(14);
196    printf STDERR " %s",      $msg;
197    printf STDERR "\e[0J";
198
199    # restore the cursor position
200    printf STDERR "\e8";
201
202    # wait for any key
203    my $key = &get_key;
204    ReadMode 'restore';
205
206    # print newline
207    printf STDERR "\n";
208
209    # A backspace response makes the current line reprint (to test menus)
210    return ( $key ne "\b" and $key ne "\177" ) ? 1 : 0;
211}
212
213if ( -t STDOUT ) {
214    printf "Legend:\n";
215    printf "  C = cursor shape (1,2 block, 3,4 underline, 5,6 left-bar)\n";
216    printf "  B = escape-sequence blink\n";
217    printf "  M = menu blink and XOR mode\n";
218    printf "\n";
219    printf "An asterisk means the mode is always set or reset.\n";
220    printf "Press any key to proceed; press backspace to reprint line.\n";
221    printf "\n";
222    my @DECSET   = sort keys %DECSET;
223    my @DECSCUSR = sort keys %DECSCUSR;
224
225    for ( my $h = 0 ; $h <= $#DECSET ; ++$h ) {
226        $h-- unless &test( $DECSET[$h], $DECSET{ $DECSET[$h] } );
227    }
228    for my $l ( 0 .. $#DECSCUSR ) {
229        $l-- unless &test( $DECSCUSR[$l], $DECSCUSR{ $DECSCUSR[$l] } );
230    }
231}
232else {
233    printf "DECSET (AT&T 610 blinking cursor):\n";
234    for my $key ( sort keys %DECSET ) {
235        &show( $key, $DECSET{$key} );
236    }
237
238    printf "DECSCUSR:\n";
239    for my $key ( sort keys %DECSCUSR ) {
240        &show( $key, $DECSCUSR{$key} );
241    }
242}
2431;
244