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