xorblink.pl revision 04b94745
1#!/usr/bin/env perl
2# $XTermId: xorblink.pl,v 1.16 2017/12/24 21:03:54 tom Exp $
3# -----------------------------------------------------------------------------
4# Copyright 2017 by Thomas E. Dickey
5#
6#                         All Rights Reserved
7#
8# Permission is hereby granted, free of charge, to any person obtaining a
9# copy of this software and associated documentation files (the
10# "Software"), to deal in the Software without restriction, including
11# without limitation the rights to use, copy, modify, merge, publish,
12# distribute, sublicense, and/or sell copies of the Software, and to
13# permit persons to whom the Software is furnished to do so, subject to
14# the following conditions:
15#
16# The above copyright notice and this permission notice shall be included
17# in all copies or substantial portions of the Software.
18#
19# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22# IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26#
27# Except as contained in this notice, the name(s) of the above copyright
28# holders shall not be used in advertising or otherwise to promote the
29# sale, use or other dealings in this Software without prior written
30# authorization.
31# -----------------------------------------------------------------------------
32# walk through the different states of cursor-blinking, with annotation
33#
34# Manual:
35#        +bc     turn off text cursor blinking.  This overrides the cursorBlink
36#                resource.
37#
38#        -bc     turn on text cursor blinking.  This overrides the cursorBlink
39#                resource.
40#
41#        cursorBlink (class CursorBlink)
42#                Specifies whether to make the cursor blink.  The default is
43#                "false".
44#
45#                Xterm-dev uses two variables to determine whether the cursor
46#                blinks.  One is set by this resource.  The other is set by
47#                control sequences (private mode 12 and DECSCUSR).  Xterm-dev
48#                tests the XOR of the two variables.
49#
50#               Enable Blinking Cursor (resource cursorblink)
51#                      Enable (or disable) the blinking-cursor feature.  This
52#                      corresponds to the -bc option and the cursorBlink
53#                      resource.  There is also an escape sequence (see Xterm-
54#                      dev Control Sequences).  The menu entry and the escape
55#                      sequence states are XOR'd: if both are enabled, the
56#                      cursor will not blink, if only one is enabled, the cursor
57#                      will blink.
58#
59#        set-cursorblink(on/off/toggle)
60#                This action sets, unsets or toggles the cursorBlink resource.
61#                It is also invoked from the cursorblink entry in vtMenu.
62#
63# Control sequences:
64#
65# CSI ? Pm h
66#           DEC Private Mode Set (DECSET).
67#             Ps = 1 2  -> Start Blinking Cursor (att610).
68#
69# CSI ? Pm l
70#           DEC Private Mode Reset (DECRST).
71#             Ps = 1 2  -> Stop Blinking Cursor (att610).
72#
73# CSI Ps SP q
74#           Set cursor style (DECSCUSR, VT520).
75#             Ps = 0  -> blinking block.
76#             Ps = 1  -> blinking block (default).
77#             Ps = 2  -> steady block.
78#             Ps = 3  -> blinking underline.
79#             Ps = 4  -> steady underline.
80#             Ps = 5  -> blinking bar (xterm).
81#             Ps = 6  -> steady bar (xterm).
82#
83use strict;
84
85use Term::ReadKey;
86
87use IO::Handle;
88STDERR->autoflush(1);
89STDOUT->autoflush(1);
90
91our %DECSET = (
92    "\e[?12h", "Start Blinking Cursor (AT&T 610)",
93    "\e[?12l", "Stop Blinking Cursor (AT&T 610)"
94);
95
96our %DECSCUSR = (
97    "\e[0 q",
98    "blinking block",
99    "\e[1 q",
100    "blinking block (default)",
101    "\e[2 q",
102    "steady block",
103    "\e[3 q",
104    "blinking underline",
105    "\e[4 q",
106    "steady underline",
107    "\e[5 q",
108    "blinking bar (xterm)",
109    "\e[6 q",
110    "steady bar (xterm)"
111);
112
113sub show($$) {
114    my $seq = shift;
115    my $txt = shift;
116    printf "%s -> %s\n", &visible($seq), $txt;
117}
118
119sub get_reply($$) {
120    my $seq = shift;
121    my $end = shift;
122    printf STDERR "%s", $seq;
123    my $key;
124    my $result = "";
125    $key = ReadKey(0);
126    $result .= $key;
127    if ( $key eq "\e" ) {
128
129        while (1) {
130            $key = ReadKey(100);
131            $result .= $key;
132            next if ( length($result) < length($end) );
133            last if ( substr( $result, -length($end) ) eq $end );
134        }
135    }
136    return $result;
137}
138
139sub mode_value($) {
140    my $value = shift;
141    if ( $value eq 1 ) {
142        $value = "set";
143    }
144    elsif ( $value eq 2 ) {
145        $value = "reset";
146    }
147    elsif ( $value eq 3 ) {
148        $value = "*set";
149    }
150    elsif ( $value eq 4 ) {
151        $value = "*reset";
152    }
153    else {
154        $value = &visible( "?" . $value );
155    }
156    return $value;
157}
158
159sub DECRQM($) {
160    my $mode     = shift;
161    my $sequence = sprintf( "\e[?%d\$p", $mode );
162    my $reply    = &get_reply( $sequence, "y" );
163    if ( $reply =~ /^\e\[\?$mode;\d+\$y$/ ) {
164        $reply =~ s/^\e\[\?$mode;(\d+)\$y$/$1/;
165    }
166    return &mode_value($reply);
167}
168
169sub DECRQSS($) {
170    my $request  = shift;
171    my $ending   = "\e\\";
172    my $sequence = sprintf( "\eP\$q%s$ending", $request );
173    my $reply    = &get_reply( $sequence, $ending );
174
175    # xterm responds with
176    # DCS 1 $ r Pt ST for valid requests,
177    # DCS 0 $ r Pt ST for invalid requests.
178    #if ( $reply =~ /^\eP1\$r.*$ending$/ ) {
179    if ( $reply =~ /^\eP1\$r\d+ q\e\\$/ ) {
180        $reply =~ s/^\eP1\$r(\d+) q\e\\$/$1/;
181    }
182    return &visible($reply);
183}
184
185sub get_key() {
186    my $key;
187    do {
188        $key = ReadKey(0);
189        if ( $key eq "\e" ) {
190            while ( ReadKey(10) !~ /[@-~]/ ) {
191                #
192            }
193        }
194    } while ( $key eq "\e" );
195    return $key;
196}
197
198sub visible($) {
199    my $txt = shift;
200    $txt =~ s/\e/\\e/g;
201    $txt =~ s/\a/\\a/g;
202    return $txt;
203}
204
205sub test($$) {
206    my $set = shift;
207    my $msg = shift;
208
209    ReadMode 'raw';
210
211    printf STDERR "%s\t[", &visible($set);
212
213    # save the cursor position
214    printf STDERR "\e7";
215
216    # send the escape sequence
217    printf STDERR "%s", $set;
218
219    # print the description
220    printf STDERR "X] ";
221
222    printf STDERR " [C=%s,",  &DECRQSS(" q");
223    printf STDERR "B=%s,",    &DECRQM(12);
224    printf STDERR "M=%s,%s]", &DECRQM(13), &DECRQM(14);
225    printf STDERR " %s",      $msg;
226    printf STDERR "\e[0J";
227
228    # restore the cursor position
229    printf STDERR "\e8";
230
231    # wait for any key
232    my $key = &get_key;
233    ReadMode 'restore';
234
235    # print newline
236    printf STDERR "\n";
237
238    # A backspace response makes the current line reprint (to test menus)
239    return ( $key ne "\b" and $key ne "\177" ) ? 1 : 0;
240}
241
242if ( -t STDOUT ) {
243    printf "Legend:\n";
244    printf "  C = cursor shape (1,2 block, 3,4 underline, 5,6 left-bar)\n";
245    printf "  B = escape-sequence blink\n";
246    printf "  M = menu blink and XOR mode\n";
247    printf "\n";
248    printf "An asterisk means the mode is always set or reset.\n";
249    printf "Press any key to proceed; press backspace to reprint line.\n";
250    printf "\n";
251    my @DECSET   = sort keys %DECSET;
252    my @DECSCUSR = sort keys %DECSCUSR;
253
254    for ( my $h = 0 ; $h <= $#DECSET ; ++$h ) {
255        $h-- unless &test( $DECSET[$h], $DECSET{ $DECSET[$h] } );
256    }
257    for my $l ( 0 .. $#DECSCUSR ) {
258        $l-- unless &test( $DECSCUSR[$l], $DECSCUSR{ $DECSCUSR[$l] } );
259    }
260}
261else {
262    printf "DECSET (AT&T 610 blinking cursor):\n";
263    for my $key ( sort keys %DECSET ) {
264        &show( $key, $DECSET{$key} );
265    }
266
267    printf "DECSCUSR:\n";
268    for my $key ( sort keys %DECSCUSR ) {
269        &show( $key, $DECSCUSR{$key} );
270    }
271}
2721;
273