File: damage-duplicate.pl

package info (click to toggle)
libx11-protocol-other-perl 28-1%2Bdeb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 1,692 kB
  • ctags: 556
  • sloc: perl: 17,055; ansic: 624; sh: 238; lisp: 143; makefile: 39
file content (241 lines) | stat: -rwxr-xr-x 9,225 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/perl -w

# Copyright 2011, 2012 Kevin Ryde

# This file is part of X11-Protocol-Other.
#
# X11-Protocol-Other is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 3, or (at your option) any
# later version.
#
# X11-Protocol-Other is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with X11-Protocol-Other.  If not, see <http://www.gnu.org/licenses/>.


# Usage: perl damage-duplicate.pl
#        perl damage-duplicate.pl --id 0x120000f
#
# This is an example of duplicating the contents of a window in real-time by
# listening for changes to it with the DAMAGE extension.
#
# A new $window toplevel displays the contents of a $source window.  The key
# feature of the damage extension is that it reports when $source changes.
# Without that a duplicating program like this would have to re-copy every 1
# second or something like that.
#
# The source window can be given as an XID with the "--id" command line
# option, otherwise an X11::Protocol::ChooseWindow is run so you can click
# on a window like xwininfo does.
#
# Details:
#
# In the event_handler() code care is taken not to do anything which reads a
# reply.  This is because reading the reply may also read and process other
# events, which would call event_handler() recursively and possibly
# endlessly.  Any event handler should bear that in mind.  In this program
# the danger would be that if the $source window is changing rapidly and so
# causing a new DamageNotify event to come very soon after each
# DamageSubtract().
#
# The $gc used for copying has "graphics_expose" off, which means any parts
# not available in the source window are cleared to the destination
# background colour.  This happens when the source is overlapped etc by
# other windows.
#
# In the core protocol there's no easy way to get content from $source when
# it's overlapped, since the server generally doesn't keep the contents or
# generate expose events for obscured parts of windows.
#
# If the "Composite" extension is available then it retains content of
# overlapped windows.  CompositeRedirectWindow() in the setups is all that's
# needed to have full $source contents available for the CopyArea()s.
#
# For simplicity the entire source area is copied whenever it changes.  In a
# more sophisticated program the "$parts_region" of changes from the damage
# object could be a clip mask for the CopyArea().  Changes outside the
# duplicated area would then still go back and forward as DamageNotify and
# CopyArea, but the server would see from the clip region no actual drawing
# required.
#
# If $window is bigger than the $source then the excess is cleared.  Some
# care is taken to clear only the excess area, not the whole of $window,
# since the latter way would make it flash to black and then to the copied
# $source.  On a fast screen you might not notice, but on a slow screen or
# if the server is bogged down then such flashing is very unattractive.
#
# Shortcomings:
#
# The created $window is always on the same screen as $source and uses the
# same depth, visual and colormap.  Doing so means a simple CopyArea
# suffices to copy the contents across.
#
# If source and destination were different depth, visual or colormap then
# pixel colour conversions would be required.  If the destination was on a
# different server or different screen then some data transfers with
# GetImage() and PutImage() would be needed as well as pixel conversions.
# (As usual X11::Protocol::Ext::MIT_SHM might do that through shared memory
# if the program is on the same machine as the server.)
#
# Duplicating the root window is specifically disallowed.  The problem is
# that a draw to $window is a change to the root window contents, so
# generates another DamageNotify, which does another draw, etc, in an
# infinite loop.  It might work if attention was paid to what parts of the
# root had changed.  Changes to the part of the root which is unobscured
# parts of $window will be due to the duplicating drawing and so don't
# require any further drawing.
#

BEGIN { require 5 }
use strict;
use Getopt::Long;
use X11::AtomConstants;
use X11::CursorFont;
use X11::Protocol;
use X11::Protocol::WM;

# uncomment this to run the ### lines
#use Smart::Comments;

my $X = X11::Protocol->new (':0');

if (! $X->init_extension('DAMAGE')) {
  print "DAMAGE extension not available on the server\n";
  exit 1;
}

#------------------------------------------------------------------------------
# command line

my $source;       # source window to duplicate
my $verbose = 1;
GetOptions ('id=s'     => \$source,
            'verbose+' => \$verbose)
  or exit 1;

#------------------------------------------------------------------------------
# source window, from command line or chosen

my $popup_time;

if (defined $source) {
  # command line --id=XID
  $source = oct($source);  # oct() for hex 0xA0000F style as well as decimal
} else {
  require X11::Protocol::ChooseWindow;
  print "Click on a window to duplicate ...\n";
  $source = X11::Protocol::ChooseWindow->choose (X => $X);
  print "  got it\n";
}

if ($verbose) {
  printf "Source window %d (0x%X)\n", $source, $source;
}
if ($source == $X->root) {
  print "Cannot duplicate root window\n";
  exit 1;
}

#------------------------------------------------------------------------------

# use the Composite extension, if available, to keep the contents of $source
# if it's overlapped by other windows.
if ($X->init_extension('Composite')) {
  $X->CompositeRedirectWindow ($source, 'Automatic');
}

#------------------------------------------------------------------------------

my %source_geom = $X->GetGeometry($source);
my %source_attr = $X->GetWindowAttributes($source);

# create new output window to show a duplicate of $source
# same depth, visual, colormap
my $window = $X->new_rsrc;
$X->CreateWindow ($window,
                  $X->root,         # parent
                  'InputOutput',    # class
                  $source_geom{'depth'},
                  $source_attr{'visual'},
                  0,0,              # x,y
                  100,100,          # w,h initial size
                  0,                # border
                  colormap         => $source_attr{'colormap'},
                  background_pixel => $X->black_pixel,
                  event_mask       => $X->pack_event_mask('Exposure'),
                 );
X11::Protocol::WM::set_wm_class ($X, $window,
                                 'damage-duplicate', 'DamageDuplicate');
X11::Protocol::WM::set_wm_name ($X, $window, 'Duplicate Window'); # title
X11::Protocol::WM::set_wm_icon_name ($X, $window, 'Duplicate');
X11::Protocol::WM::set_wm_client_machine_from_syshostname ($X, $window);
X11::Protocol::WM::set_net_wm_pid ($X, $window);
X11::Protocol::WM::set_net_wm_user_time($X, $window, $popup_time);
$X->MapWindow ($window);

# select ConfigureNotify from $source, to know when it resizes
$X->ChangeWindowAttributes
  ($source,
   event_mask => $X->pack_event_mask('StructureNotify'));

# the damage object to monitor $source
# creating this gives DamageNotify events
my $damage = $X->new_rsrc;
$X->DamageCreate ($damage, $source, 'NonEmpty');

my $gc = $X->new_rsrc;
$X->CreateGC ($gc, $window,
              subwindow_mode => 'IncludeInferiors',
              # no "graphics exposures", don't want GraphicsExpose events if
              # a part of the $X->CopyArea is obscured
              graphics_exposures => 0);

sub event_handler {
  my (%h) = @_;
  my $name = $h{'name'};
  ### event_handler()
  ### $name
  if ($name eq 'ConfigureNotify') {
    # $source has resized
    ### height: $h{'height'}
    my $width = $h{'width'};    # of $source
    my $height = $h{'height'};
    # clear any excess if $source has shrunk
    $X->ClearArea ($window, $width,0, 0,0);  # to left of $width
    $X->ClearArea ($window, 0,$height, 0,0); # below $height
    # copy any extra if $source has expanded
    $X->CopyArea ($source, $window, $gc,
                  0,0,                       # src x,y
                  $h{'width'},$h{'height'},  # src w,h
                  0,0);                      # dst x,y

  } elsif ($name eq 'DamageNotify') {
    # $source has been drawn into
    my $rect = $h{'geometry'};
    my ($root_x, $root_y, $width, $height) = @$rect;
    ### $rect
    $X->DamageSubtract ($damage, 'None', 'None');
    $X->CopyArea ($source, $window, $gc,
                  0,0,         # src x,y
                  $width,$height,
                  0,0);        # dst x,y

  } elsif ($name eq 'Expose') {
    # our $window revealed, draw it
    $X->CopyArea ($source, $window, $gc,
                  $h{'x'},$h{'y'},           # src x,y
                  $h{'width'},$h{'height'},  # src w,h
                  $h{'x'},$h{'y'});          # dst x,y
  }
}

$X->{'event_handler'} = \&event_handler;
for (;;) {
  $X->handle_input;
}
exit 0;