1a5ae21e4Smrg#!/usr/bin/env perl
204b94745Smrg# $XTermId: setpos.pl,v 1.18 2019/05/26 23:19:29 tom Exp $
3a5ae21e4Smrg# -----------------------------------------------------------------------------
4a5ae21e4Smrg# Copyright 2019 by Thomas E. Dickey
5a5ae21e4Smrg#
6a5ae21e4Smrg#                         All Rights Reserved
7a5ae21e4Smrg#
8a5ae21e4Smrg# Permission is hereby granted, free of charge, to any person obtaining a
9a5ae21e4Smrg# copy of this software and associated documentation files (the
10a5ae21e4Smrg# "Software"), to deal in the Software without restriction, including
11a5ae21e4Smrg# without limitation the rights to use, copy, modify, merge, publish,
12a5ae21e4Smrg# distribute, sublicense, and/or sell copies of the Software, and to
13a5ae21e4Smrg# permit persons to whom the Software is furnished to do so, subject to
14a5ae21e4Smrg# the following conditions:
15a5ae21e4Smrg#
16a5ae21e4Smrg# The above copyright notice and this permission notice shall be included
17a5ae21e4Smrg# in all copies or substantial portions of the Software.
18a5ae21e4Smrg#
19a5ae21e4Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20a5ae21e4Smrg# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21a5ae21e4Smrg# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22a5ae21e4Smrg# IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23a5ae21e4Smrg# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24a5ae21e4Smrg# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25a5ae21e4Smrg# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26a5ae21e4Smrg#
27a5ae21e4Smrg# Except as contained in this notice, the name(s) of the above copyright
28a5ae21e4Smrg# holders shall not be used in advertising or otherwise to promote the
29a5ae21e4Smrg# sale, use or other dealings in this Software without prior written
30a5ae21e4Smrg# authorization.
31a5ae21e4Smrg# -----------------------------------------------------------------------------
32a5ae21e4Smrg# Exercise CSI 3/13 t which set/get the window position.
33a5ae21e4Smrg
34a5ae21e4Smrguse strict;
35a5ae21e4Smrg
36a5ae21e4Smrguse Getopt::Std;
37a5ae21e4Smrguse IO::Handle;
38a5ae21e4Smrg
39a5ae21e4Smrg$| = 1;
40a5ae21e4Smrg
41a5ae21e4Smrgour ( $opt_a, $opt_n, $opt_p, $opt_v, $opt_x, $opt_8 );
42a5ae21e4Smrgour $default_y = 100;
43a5ae21e4Smrgour $default_x = 150;
44a5ae21e4Smrg
45a5ae21e4Smrgsub main::HELP_MESSAGE() {
46a5ae21e4Smrg    printf STDERR <<EOF
47a5ae21e4SmrgUsage: $0 [options]
48a5ae21e4SmrgOptions:
49a5ae21e4Smrg  -8      use 8-bit controls
50a5ae21e4Smrg  -a      test position/report for middle and four corners
51a5ae21e4Smrg  -n N    repeat unless -a option used (default: 3)
52a5ae21e4Smrg  -p Y,X  use this position rather than $default_y,$default_x
53a5ae21e4Smrg  -v      verbose
54a5ae21e4Smrg  -x      report xwininfo's position for \$WINDOWID
55a5ae21e4SmrgEOF
56a5ae21e4Smrg      ;
57a5ae21e4Smrg    exit 1;
58a5ae21e4Smrg}
59a5ae21e4Smrg
60a5ae21e4Smrg$Getopt::Std::STANDARD_HELP_VERSION = 1;
61a5ae21e4Smrg&getopts('an:p:vx8') || &main::HELP_MESSAGE;
62a5ae21e4Smrg
63a5ae21e4Smrgour $repeat = 3;
64a5ae21e4Smrg$repeat = $opt_n if ($opt_n);
65a5ae21e4Smrg&main::HELP_MESSAGE unless ( $repeat =~ /^\d+$/ );
66a5ae21e4Smrg
67a5ae21e4Smrgour $CSI = "\x1b\[";
68a5ae21e4Smrg$CSI = "\x9b" if ($opt_8);
69a5ae21e4Smrg
70a5ae21e4Smrgif ($opt_p) {
71a5ae21e4Smrg    &main::HELP_MESSAGE unless ( $opt_p =~ /^[-]?\d+,[-]?\d+$/ );
72a5ae21e4Smrg    my @coord = split /,/, $opt_p;
73a5ae21e4Smrg    $default_y = $coord[0];
74a5ae21e4Smrg    $default_x = $coord[1];
75a5ae21e4Smrg}
76a5ae21e4Smrg
77a5ae21e4Smrgour $wm_name = "unknown";
78a5ae21e4Smrgour @extents;
79a5ae21e4Smrg
80a5ae21e4Smrgsub no_reply($) {
81a5ae21e4Smrg    open TTY, "+</dev/tty" or die("Cannot open /dev/tty\n");
82a5ae21e4Smrg    autoflush TTY 1;
83a5ae21e4Smrg    my $old = `stty -g`;
84a5ae21e4Smrg    system "stty raw -echo min 0 time 5";
85a5ae21e4Smrg
86a5ae21e4Smrg    print TTY @_;
87a5ae21e4Smrg    close TTY;
88a5ae21e4Smrg    system "stty $old";
89a5ae21e4Smrg}
90a5ae21e4Smrg
91a5ae21e4Smrgsub get_reply($) {
92a5ae21e4Smrg    open TTY, "+</dev/tty" or die("Cannot open /dev/tty\n");
93a5ae21e4Smrg    autoflush TTY 1;
94a5ae21e4Smrg    my $old = `stty -g`;
95a5ae21e4Smrg    system "stty raw -echo min 0 time 5";
96a5ae21e4Smrg
97a5ae21e4Smrg    print TTY @_;
98a5ae21e4Smrg    my $reply = <TTY>;
99a5ae21e4Smrg    close TTY;
100a5ae21e4Smrg    system "stty $old";
101a5ae21e4Smrg    if ( defined $reply ) {
102a5ae21e4Smrg        die("^C received\n") if ( "$reply" eq "\003" );
103a5ae21e4Smrg    }
104a5ae21e4Smrg    return $reply;
105a5ae21e4Smrg}
106a5ae21e4Smrg
107a5ae21e4Smrgsub read_cmd($) {
108a5ae21e4Smrg    my $cmd = shift;
109a5ae21e4Smrg    my @result;
110a5ae21e4Smrg    if ( open my $fh, "$cmd |" ) {
111a5ae21e4Smrg        @result = <$fh>;
112a5ae21e4Smrg        close $fh;
113a5ae21e4Smrg        chomp @result;
114a5ae21e4Smrg    }
115a5ae21e4Smrg    return @result;
116a5ae21e4Smrg}
117a5ae21e4Smrg
118a5ae21e4Smrgsub get_xprop($$) {
119a5ae21e4Smrg    my $id   = shift;
120a5ae21e4Smrg    my $name = shift;
121a5ae21e4Smrg    my @data = &read_cmd("xprop -id $id");
122a5ae21e4Smrg    my $prop = "";
123a5ae21e4Smrg    for my $n ( 0 .. $#data ) {
124a5ae21e4Smrg        if ( $data[$n] =~ /$name\([^)]+\) =/ ) {
125a5ae21e4Smrg            $prop = $data[$n];
126a5ae21e4Smrg            $prop =~ s/^[^=]*=\s*//;
127a5ae21e4Smrg            $prop =~ s/"//g;
128a5ae21e4Smrg            last;
129a5ae21e4Smrg        }
130a5ae21e4Smrg    }
131a5ae21e4Smrg    return $prop;
132a5ae21e4Smrg}
133a5ae21e4Smrg
134a5ae21e4Smrgsub visible($) {
135a5ae21e4Smrg    my $reply = $_[0];
136a5ae21e4Smrg    my $n;
137a5ae21e4Smrg    my $result = "";
138a5ae21e4Smrg    for ( $n = 0 ; $n < length($reply) ; ) {
139a5ae21e4Smrg        my $c = substr( $reply, $n, 1 );
140a5ae21e4Smrg        if ( $c =~ /[[:print:]]/ ) {
141a5ae21e4Smrg            $result .= $c;
142a5ae21e4Smrg        }
143a5ae21e4Smrg        else {
144a5ae21e4Smrg            my $k = ord substr( $reply, $n, 1 );
145a5ae21e4Smrg            if ( ord $k == 0x1b ) {
146a5ae21e4Smrg                $result .= "\\E";
147a5ae21e4Smrg            }
148a5ae21e4Smrg            elsif ( $k == 0x7f ) {
149a5ae21e4Smrg                $result .= "^?";
150a5ae21e4Smrg            }
151a5ae21e4Smrg            elsif ( $k == 32 ) {
152a5ae21e4Smrg                $result .= "\\s";
153a5ae21e4Smrg            }
154a5ae21e4Smrg            elsif ( $k < 32 ) {
155a5ae21e4Smrg                $result .= sprintf( "^%c", $k + 64 );
156a5ae21e4Smrg            }
157a5ae21e4Smrg            elsif ( $k > 128 ) {
158a5ae21e4Smrg                $result .= sprintf( "\\%03o", $k );
159a5ae21e4Smrg            }
160a5ae21e4Smrg            else {
161a5ae21e4Smrg                $result .= chr($k);
162a5ae21e4Smrg            }
163a5ae21e4Smrg        }
164a5ae21e4Smrg        $n += 1;
165a5ae21e4Smrg    }
166a5ae21e4Smrg
167a5ae21e4Smrg    return $result;
168a5ae21e4Smrg}
169a5ae21e4Smrg
170a5ae21e4Smrgsub limited($) {
171a5ae21e4Smrg    my $value = shift;
172a5ae21e4Smrg    if ( $value >= 65536 ) {
173a5ae21e4Smrg        $value %= 65536;
174a5ae21e4Smrg    }
175a5ae21e4Smrg    if ( $value >= 32768 ) {
176a5ae21e4Smrg        $value -= 65536;
177a5ae21e4Smrg    }
178a5ae21e4Smrg    return $value;
179a5ae21e4Smrg}
180a5ae21e4Smrg
181a5ae21e4Smrgsub check_position($$$) {
182a5ae21e4Smrg    my $name   = shift;
183a5ae21e4Smrg    my $expect = shift;
184a5ae21e4Smrg    my $actual = shift;
185a5ae21e4Smrg    printf " ?%s:%d", $name, $expect if ( $expect != $actual );
186a5ae21e4Smrg}
187a5ae21e4Smrg
188a5ae21e4Smrgsub report_position() {
189a5ae21e4Smrg    my $reply = &get_reply( sprintf "%s13t", $CSI );
190a5ae21e4Smrg    my $status = 0;
191a5ae21e4Smrg    my @result;
192a5ae21e4Smrg    if ( index( $reply, $CSI ) == 0 ) {
193a5ae21e4Smrg        $reply = substr( $reply, length($CSI) );
194a5ae21e4Smrg        $status = 1;
195a5ae21e4Smrg    }
196a5ae21e4Smrg    if ( $reply =~ /^3;\d+;\d+t$/ ) {
197a5ae21e4Smrg        my $y = $reply;
198a5ae21e4Smrg        $y =~ s/^3;(\d+);.*/$1/;
199a5ae21e4Smrg        my $x = $reply;
200a5ae21e4Smrg        $x =~ s/^3;\d+;(\d+).*/$1/;
201a5ae21e4Smrg        $result[0] = &limited($y);
202a5ae21e4Smrg        $result[1] = &limited($x);
203a5ae21e4Smrg        printf "OK ->%s ->%d,%d", &visible($reply), $result[0], $result[1];
204a5ae21e4Smrg    }
205a5ae21e4Smrg    else {
206a5ae21e4Smrg        printf "ERR ->%s", &visible($reply);
207a5ae21e4Smrg    }
208a5ae21e4Smrg    if ( $opt_x and $ENV{WINDOWID} ) {
209a5ae21e4Smrg        my @actual = `xwininfo -id $ENV{WINDOWID} | grep " upper-left [XY]:"`;
210a5ae21e4Smrg        for my $n ( 0 .. $#actual ) {
211a5ae21e4Smrg            $actual[$n] =~ s/^.*:\s+//;
212a5ae21e4Smrg        }
213a5ae21e4Smrg        if ( $#actual == 3 ) {
214a5ae21e4Smrg            printf " abs(%d,%d) rel(%d,%d)", $actual[0], $actual[1],
215a5ae21e4Smrg              $actual[2], $actual[3]
216a5ae21e4Smrg              if ($opt_v);
217a5ae21e4Smrg            my $expect_y;
218a5ae21e4Smrg            my $expect_x;
219a5ae21e4Smrg            if ( $wm_name =~ /^gnome/i ) {
220a5ae21e4Smrg                $expect_x = $actual[0] - ( $extents[0] + $extents[1] );
221a5ae21e4Smrg                $expect_y = $actual[1] - ( $extents[2] + $extents[3] );
222a5ae21e4Smrg            }
223a5ae21e4Smrg            elsif ( $#extents == 3
224a5ae21e4Smrg                and ( $wm_name !~ /^fvwm/i )
225a5ae21e4Smrg                and ( $wm_name !~ /^enlightenment/i ) )
226a5ae21e4Smrg            {
227a5ae21e4Smrg                $expect_x = $actual[0] - ( $extents[0] );
228a5ae21e4Smrg                $expect_y = $actual[1] - ( $extents[2] );
229a5ae21e4Smrg            }
230a5ae21e4Smrg            else {
231a5ae21e4Smrg                $expect_x = $actual[0] - $actual[2];
232a5ae21e4Smrg                $expect_y = $actual[1] - $actual[3];
233a5ae21e4Smrg            }
234a5ae21e4Smrg            if ( $#result > 0 ) {
235a5ae21e4Smrg                &check_position( "X", $expect_x, $result[0] );
236a5ae21e4Smrg                &check_position( "Y", $expect_y, $result[1] );
237a5ae21e4Smrg            }
238a5ae21e4Smrg        }
239a5ae21e4Smrg    }
240a5ae21e4Smrg    printf "\n";
241a5ae21e4Smrg    return @result;
242a5ae21e4Smrg}
243a5ae21e4Smrg
244a5ae21e4Smrgsub update_position() {
245a5ae21e4Smrg    my @pos = @{ $_[0] };
246a5ae21e4Smrg    printf "** update %d,%d\n", $pos[0], $pos[1];
247a5ae21e4Smrg    $pos[0] += 65536 if ( $pos[0] < 0 );
248a5ae21e4Smrg    $pos[1] += 65536 if ( $pos[1] < 0 );
249a5ae21e4Smrg    &no_reply( sprintf "%s3;%d;%dt", $CSI, $pos[0], $pos[1] );
250a5ae21e4Smrg}
251a5ae21e4Smrg
252a5ae21e4Smrgsub update_and_report($) {
253a5ae21e4Smrg    my @pos = @{ $_[0] };
254a5ae21e4Smrg    &update_position( \@pos );
255a5ae21e4Smrg    sleep 1 if ($opt_a);
256a5ae21e4Smrg    return &report_position;
257a5ae21e4Smrg}
258a5ae21e4Smrg
259a5ae21e4Smrgsub get_screensize() {
260a5ae21e4Smrg    my $reply = &get_reply( sprintf "%s15t", $CSI );
261a5ae21e4Smrg    my @result;
262a5ae21e4Smrg    if ( index( $reply, $CSI ) == 0 ) {
263a5ae21e4Smrg        $reply = substr( $reply, length($CSI) );
264a5ae21e4Smrg        if ( $reply =~ /^5;\d+;\d+t$/ ) {
265a5ae21e4Smrg            my $y = $reply;
266a5ae21e4Smrg            $y =~ s/^5;(\d+);.*/$1/;
267a5ae21e4Smrg            my $x = $reply;
268a5ae21e4Smrg            $x =~ s/^5;\d+;(\d+).*/$1/;
269a5ae21e4Smrg            $result[0] = $x;
270a5ae21e4Smrg            $result[1] = $y;
271a5ae21e4Smrg        }
272a5ae21e4Smrg    }
273a5ae21e4Smrg    return @result;
274a5ae21e4Smrg}
275a5ae21e4Smrg
276a5ae21e4Smrgsub doit() {
277a5ae21e4Smrg    my @old = &report_position;
278a5ae21e4Smrg    if ($opt_a) {
279a5ae21e4Smrg        my @size = &get_screensize;
280a5ae21e4Smrg        if (@size) {
281a5ae21e4Smrg            printf "Screen %dx%d\n", $size[0], $size[1];
282a5ae21e4Smrg            my $ulx = -$default_x;
283a5ae21e4Smrg            my $uly = -$default_y;
284a5ae21e4Smrg            my $lrx = $size[0] - $default_x;
285a5ae21e4Smrg            my $lry = $size[1] - $default_y;
286a5ae21e4Smrg            &update_and_report( [ $ulx, $uly ] );
287a5ae21e4Smrg            &update_and_report( [ $ulx, $lry ] );
288a5ae21e4Smrg            &update_and_report( [ $lrx, $lry ] );
289a5ae21e4Smrg            &update_and_report( [ $lrx, $uly ] );
290a5ae21e4Smrg            &update_position( \@old );
291a5ae21e4Smrg        }
292a5ae21e4Smrg    }
293a5ae21e4Smrg    else {
294a5ae21e4Smrg        my @pos = ( $default_y, $default_x );
295a5ae21e4Smrg        for my $n ( 1 .. $repeat ) {
296a5ae21e4Smrg            @pos = &update_and_report( \@pos );
297a5ae21e4Smrg        }
298a5ae21e4Smrg    }
299a5ae21e4Smrg}
300a5ae21e4Smrg
301a5ae21e4Smrgprintf "\x1b G" if ($opt_8);
302a5ae21e4Smrg
303a5ae21e4Smrgif ( $opt_x and $ENV{WINDOWID} ) {
304a5ae21e4Smrg    my $extents = &get_xprop( $ENV{WINDOWID}, "_NET_FRAME_EXTENTS" );
305a5ae21e4Smrg    if ( $extents ne "" ) {
306a5ae21e4Smrg        @extents = split /,\s*/, $extents;
307a5ae21e4Smrg        printf "** has EWMH extents: $extents\n";
308a5ae21e4Smrg        my $supwin = `xprop -root '_NET_SUPPORTING_WM_CHECK'`;
309a5ae21e4Smrg        if ( $supwin ne "" ) {
310a5ae21e4Smrg            $supwin =~ s/^.*(0x[[:xdigit:]]+).*/$1/;
311a5ae21e4Smrg            $wm_name = &get_xprop( $supwin, "_NET_WM_NAME" );
312a5ae21e4Smrg            $wm_name = "unknown" unless ( $wm_name ne "" );
313a5ae21e4Smrg            printf "** using \"$wm_name\"\n";
314a5ae21e4Smrg        }
315a5ae21e4Smrg    }
316a5ae21e4Smrg}
317a5ae21e4Smrg
318a5ae21e4Smrg&doit;
319a5ae21e4Smrg
320a5ae21e4Smrgprintf "\x1b F" if ($opt_8);
321a5ae21e4Smrg
322a5ae21e4Smrg1;
323