1#!/usr/bin/env perl
2# $XTermId: scroll.pl,v 1.4 2022/10/10 17:02:54 tom Exp $
3# -----------------------------------------------------------------------------
4# this file is part of xterm
5#
6# Copyright 2009,2022 by Thomas E. Dickey
7#
8#                         All Rights Reserved
9#
10# Permission is hereby granted, free of charge, to any person obtaining a
11# copy of this software and associated documentation files (the
12# "Software"), to deal in the Software without restriction, including
13# without limitation the rights to use, copy, modify, merge, publish,
14# distribute, sublicense, and/or sell copies of the Software, and to
15# permit persons to whom the Software is furnished to do so, subject to
16# the following conditions:
17#
18# The above copyright notice and this permission notice shall be included
19# in all copies or substantial portions of the Software.
20#
21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24# IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
25# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28#
29# Except as contained in this notice, the name(s) of the above copyright
30# holders shall not be used in advertising or otherwise to promote the
31# sale, use or other dealings in this Software without prior written
32# authorization.
33# -----------------------------------------------------------------------------
34# Tests insert/delete-line feature in xterm.  This applies only to the
35# visible screen (saved-lines are unaffected).
36#
37# TODO:
38#	add option to wrap the test-pattern
39#	use scrolling-margins to help fill-in a chunk
40use strict;
41use warnings;
42
43use Getopt::Std;
44
45# do this so output from successive calls to this script won't get in the
46# wrong order:
47use IO::Handle;
48STDERR->autoflush(1);
49STDOUT->autoflush(1);
50
51our ( $opt_c, $opt_n, $opt_r, $opt_w );
52our ( $lineno, $test_string, $term_height, $term_width, $max_scroll );
53
54our @resize;
55
56sub read_resize($) {
57    my $field  = shift;
58    my $result = shift;
59    if ( $#resize < 0 ) {
60        open( FP, "resize -u |" ) or exit $!;
61        @resize = <FP>;
62        chomp @resize;
63        close(FP);
64    }
65    for my $n ( 0 .. $#resize ) {
66        if ( $resize[$n] =~ /^$field=/ ) {
67            $result = $resize[$n];
68            $result =~ s/^[^=]*=//;
69            $result =~ s/;.*//;
70            last;
71        }
72    }
73    return $result;
74}
75
76# returns the number of rows in the screen
77sub screen_height() {
78    return &read_resize( "LINES", 24 );
79}
80
81# returns the number of columns in the screen
82sub screen_width() {
83    return &read_resize( "COLUMNS", 80 );
84}
85
86sub set_color($) {
87    my $code = $_[0];
88    if ( defined($opt_c) ) {
89        if ( $code == 3 ) {
90            printf "\x1b[1;33;42m";    # yellow-on-green
91        }
92        elsif ( $code == 2 ) {
93            printf "\x1b[0;31;45m";    # red-on-magenta
94        }
95        elsif ( $code == 1 ) {
96            printf "\x1b[0;36;44m";    # cyan-on-blue
97        }
98        else {
99            printf "\x1b[0;39;49m";
100        }
101    }
102}
103
104# returns a string of two-column characters given an ASCII alpha/numeric string
105sub double_cells($) {
106    my $value = $_[0];
107    $value =~ s/ /  /g;
108    pack(
109        "U*",
110        map {
111            ( $_ <= 32 || $_ > 127 )    # if non-ASCII character...
112              ? 32                      # ...just show a blank
113              : ( 0xff00 + ( $_ - 32 ) )    # map to "Fullwidth Form"
114        } unpack( "C*", $value )
115    );                                      # unpack unsigned-char characters
116}
117
118sub clear_screen() {
119    upper_left();
120    printf "\x1b[J";
121}
122
123sub clr_to_eol() {
124    printf "\x1b[K";
125}
126
127sub lower_left() {
128    printf "\x1b[%dH", $term_height;
129}
130
131sub upper_left() {
132    printf "\x1b[H";
133}
134
135sub move_to($) {
136    printf "\x1b[%dG", $_[0] + 1;
137}
138
139sub bak_scroll($) {
140
141    #lower_left;
142    if ( $_[0] ) {
143        printf "\x1b[%dS", $_[0];
144    }
145    else {
146        printf "\x1b[S";
147    }
148}
149
150sub fwd_scroll($) {
151    if ( $_[0] ) {
152        printf "\x1b[%dT", $_[0];
153    }
154    else {
155        printf "\x1b[T";
156    }
157}
158
159sub delete_char() {
160    set_color(2);
161    printf "\x1b[%dP", 1;
162    set_color(1);
163}
164
165sub insert_once($) {
166    my $data = shift;
167    set_color(2);
168    printf "\x1b[%d@%s", length($data), $data;
169    write_chars($data);
170}
171
172sub insert_mode($) {
173    set_color(2);
174    printf "\x1b[%dP", length( $_[0] );
175    printf "\x1b[4h";
176    write_chars( $_[0] );
177    printf "\x1b[4l";
178}
179
180sub write_chars($) {
181    set_color(3);
182    printf "%s", $_[0];
183    set_color(1);
184}
185
186# vary the starting point of each line, to make a more interesting pattern
187sub starts_of($) {
188    my $value = $_[0];
189    if ( defined($opt_w) ) {
190
191        # 0,1,1,2,2,3,3,...
192        $value = ( ( $value + 1 ) / 2 ) % length($test_string);
193    }
194    else {
195        $value %= length($test_string);
196    }
197    return $value;
198}
199
200# write the text for the given line-number
201sub testit($) {
202    my $number = $_[0];
203    my $length = $term_width;
204
205    # use delete-lines to "pull" the screen up, like scrolling.
206    select( undef, undef, undef, 0.1 );
207    if ( ( ( $number / $max_scroll ) % 2 ) != 0 ) {
208        lower_left;
209        fwd_scroll(1);
210    }
211    else {
212        lower_left;
213        bak_scroll(1);
214    }
215    if ( defined($opt_n) ) {
216        printf "%5d ", $number % 99999;
217        $length -= 6;
218    }
219
220    # if we're printing double-column characters, we have half as much
221    # space effectively - but don't forget the remainder, so we can push
222    # the characters by single-columns.
223    if ( defined($opt_c) ) {
224        set_color(1);
225        clr_to_eol();
226    }
227    my $starts = starts_of($number);
228    if ( defined($opt_w) ) {
229        printf " ", if ( ( $number % 2 ) != 0 );
230        $length = ( $length - ( ($number) % 2 ) ) / 2;
231    }
232    my $string = substr( $test_string, $starts );
233    while ( length($string) < $length ) {
234        $string = $string . $test_string;
235    }
236    $string = substr( $string, 0, $length );
237    if ( defined($opt_w) ) {
238        $string = double_cells($string);
239    }
240    printf "%s", $string;
241
242    # now - within the line - modify it
243    move_to( ( 4 * $term_width ) / 5 );
244    insert_mode("XX");
245    move_to( ( 3 * $term_width ) / 5 );
246    delete_char();
247    move_to( ( 2 * $term_width ) / 5 );
248    insert_once('~');
249    move_to( ( 1 * $term_width ) / 5 );
250    write_chars('~');
251    move_to(0);
252    set_color(0);
253}
254
255sub main::HELP_MESSAGE() {
256    printf STDERR <<EOF
257Usage: $0 [options]
258
259Options:
260
261-c   use color
262-n   write line-numbers
263-r   repeat indefinitely
264-w   write wide-characters
265EOF
266      ;
267    exit;
268}
269
270&getopts('cnrw') || die();
271
272$term_height = screen_height();
273$term_width  = screen_width();
274$max_scroll  = $term_height * 2;
275
276$test_string =
277  "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
278
279binmode( STDOUT, ":utf8" );
280clear_screen();
281if ( defined($opt_r) ) {
282    for ( $lineno = 0 ; ; ++$lineno ) {
283        testit($lineno);
284    }
285}
286else {
287    for ( $lineno = 0 ; $lineno < $max_scroll * 2 ; ++$lineno ) {
288        testit($lineno);
289    }
290}
291lower_left();
292clr_to_eol();
293
294exit;
295