1#!/usr/bin/perl
2#
3# panner.pl: start up x11vnc in '-clip' mode viewing a small (WxH)
4#            rectangular region of the screen.  Allow the viewer user
5#            to 'pan' around the display region by moving the mouse.
6#
7#            Remote interaction with applications, e.g. clicking a
8#            button though the VNC viewer, will be very difficult.
9#            This may be useful in a 'demo' mode where the user sitting
10#            at the physical display is the only one moving the mouse.
11#            Depending on your usage the following x11vnc options may
12#            be useful: -nonap
13#
14# Usage:  panner.pl WxH      <x11vnc-args>  (e.g. -display ...)
15# or      panner.pl WxH:0.05 <x11vnc-args>  (e.g. 0.05 is polling time in secs.)
16
17use strict;
18
19my $WxH = shift;
20my $poll_time;
21
22# split off poll time:
23#
24($WxH, $poll_time) = split(/:/, $WxH);
25my ($W, $H) = split(/x/, $WxH);
26
27$poll_time = 0.1 unless $poll_time ne '';
28
29# set to x11vnc command (e.g. full PATH)
30#
31my $x11vnc = "x11vnc";
32
33# check if display was given:
34#
35my $query_args = "";
36for (my $i=0; $i < @ARGV; $i++) {
37	if ($ARGV[$i] eq '-display') {
38		$query_args = "-display $ARGV[$i+1]";
39	}
40}
41
42# find the size of display and the current mouse position:
43my %v;
44vset("DIRECT:wdpy_x,wdpy_y,pointer_x,pointer_y,pointer_same");
45
46# set a -clip argument based on the above:
47#
48my $clip = '';
49clip_set();
50$clip = "${W}x${H}+0+0" unless $v{pointer_same};
51
52# launch x11vnc with -clip in the background:
53#
54my $cmd = "$x11vnc -clip $clip -bg " . join(" ", @ARGV);
55print STDERR "running: $cmd\n";
56system $cmd;
57
58# user can hit Ctrl-C or kill this script to quit (and stop x11vnc)
59#
60sub quit {
61	system("$x11vnc $query_args -R stop");
62	exit 0;
63}
64
65$SIG{INT}  = \&quit;
66$SIG{TERM} = \&quit;
67
68# loop forever waiting for mouse position to change, then shift -clip:
69#
70my $clip_old = $clip;
71while (1) {
72	fsleep($poll_time);
73	vset("pointer_x,pointer_y,pointer_same");
74	next unless $v{pointer_same};
75	clip_set();
76	if ($clip ne $clip_old) {
77		system("$x11vnc $query_args -R clip:$clip");
78		$clip_old = $clip
79	}
80}
81
82exit 0;
83
84# short sleep:
85#
86sub fsleep {
87	my ($time) = @_;
88	select(undef, undef, undef, $time) if $time;
89}
90
91# set the -clip string, making sure view doesn't go off edges of display:
92#
93sub clip_set {
94	my $x = int($v{pointer_x} - $W/2);
95	my $y = int($v{pointer_y} - $H/2);
96	$x = 0 if $x < 0;
97	$y = 0 if $y < 0;
98	$x = $v{wdpy_x} - $W if $x + $W > $v{wdpy_x};
99	$y = $v{wdpy_y} - $H if $y + $H > $v{wdpy_y};
100	$clip = "${W}x${H}+$x+$y";
101}
102
103# query x11vnc for values, put results in the %v hash:
104#
105sub vset {
106	my $str = shift;
107	my $out = `$x11vnc $query_args -Q $str 2>/dev/null`;
108	chomp $out;
109	foreach my $pair (split(/,/, $out)) {
110		$pair =~ s/^a..=//;
111		my ($k, $v) = split(/:/, $pair, 2);
112		if ($k ne '' && $v ne '') {
113			print STDERR "k=$k v=$v\n" if $ENV{DEBUG};
114			$v{$k} = $v;
115		}
116	}
117}
118