File Coverage

blib/lib/Image/Base/X11/Protocol/Window.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2013 Kevin Ryde
2              
3             # This file is part of Image-Base-X11-Protocol.
4             #
5             # Image-Base-X11-Protocol is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU General Public License as published
7             # by the Free Software Foundation; either version 3, or (at your option) any
8             # later version.
9             #
10             # Image-Base-X11-Protocol is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13             # Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Image-Base-X11-Protocol. If not, see .
17              
18              
19             # X11::Protocol::Ext::SHAPE
20             # /usr/share/doc/x11proto-xext-dev/shape.txt.gz
21             # /usr/share/doc/x11proto-core-dev/x11protocol.txt.gz
22             #
23              
24             package Image::Base::X11::Protocol::Window;
25 1     1   668 use 5.004;
  1         3  
  1         35  
26 1     1   5 use strict;
  1         2  
  1         24  
27 1     1   4 use Carp;
  1         1  
  1         80  
28 1     1   10 use vars '@ISA', '$VERSION';
  1         1  
  1         54  
29              
30 1     1   792 use Image::Base::X11::Protocol::Drawable;
  0            
  0            
31             @ISA = ('Image::Base::X11::Protocol::Drawable');
32              
33             # uncomment this to run the ### lines
34             # use Smart::Comments;
35              
36             $VERSION = 14;
37              
38              
39             sub new {
40             my ($class, %params) = @_;
41             ### X11-Protocol-Window new()
42              
43             # lookup -colormap from -window if not supplied
44             if (! defined $params{'-colormap'}) {
45             my %attrs = $params{'-X'}->GetWindowAttributes ($params{'-window'});
46             $params{'-colormap'} = $attrs{'colormap'};
47             }
48              
49             # alias -window to -drawable
50             if (my $win = delete $params{'-window'}) {
51             $params{'-drawable'} = $win;
52             }
53              
54             return $class->SUPER::new (%params);
55             }
56              
57             sub DESTROY {
58             my ($self) = @_;
59             ### X11-Protocol-Window DESTROY
60             _free_bitmap_gc($self);
61             shift->SUPER::DESTROY (@_);
62             }
63             sub _free_bitmap_gc {
64             my ($self) = @_;
65             if (my $bitmap_gc = delete $self->{'_bitmap_gc'}) {
66             ### FreeGC bitmap_gc: $bitmap_gc
67             $self->{'-X'}->FreeGC ($bitmap_gc);
68             }
69             }
70              
71             my %get_window_attributes = (-colormap => 1,
72             -visual => 1);
73             sub _get {
74             my ($self, $key) = @_;
75             ### X11-Protocol-Window _get(): $key
76              
77             if (! exists $self->{$key}) {
78             if ($get_window_attributes{$key}) {
79             my $attr = ($self->{'_during_get'}->{'GetWindowAttributes'} ||= do {
80             my %attr = $self->{'-X'}->GetWindowAttributes ($self->{'-drawable'});
81             foreach my $field ('visual') {
82             if (! exists $self->{"-$field"}) { # unchanging
83             $self->{"-$field"} = $attr{$field};
84             }
85             }
86             \%attr
87             });
88             return $attr->{substr($key,1)};
89             }
90             }
91             return $self->SUPER::_get($key);
92             }
93              
94             sub set {
95             my ($self, %params) = @_;
96              
97             if (exists $params{'-drawable'}) {
98             _free_bitmap_gc ($self);
99             delete $self->{'-visual'}; # must be refetched, or provided in %params
100             }
101              
102             my $width = delete $params{'-width'};
103             my $height = delete $params{'-height'};
104              
105             # set -drawable before applying -width and -height
106             $self->SUPER::set (%params);
107              
108             if (defined $width || defined $height) {
109             $self->{'-X'}->ConfigureWindow
110             ($self->{'-drawable'},
111             (defined $width ? (width => $width) : ()),
112             (defined $height ? (height => $height) : ()));
113             }
114             }
115              
116             #------------------------------------------------------------------------------
117             # drawing
118              
119             sub xy {
120             my ($self, $x, $y, $colour) = @_;
121             ### Window xy(): "$x, $y".(@_>=4 && ", $colour")
122              
123             if ((my $X = $self->{'-X'})->{'ext'}->{'SHAPE'}) {
124              
125             # don't overflow INT16 in requests
126             if ($x < 0 || $y < 0 || $x > 0x7FFF || $y > 0x7FFF) {
127             ### entirely outside max possible drawable ...
128             return undef; # fetch or store
129             }
130              
131             if (@_ >= 4) {
132             if ($colour eq 'None') {
133             ### Window xy() subtract shape ...
134             $X->ShapeRectangles ($self->{'-drawable'},
135             'Bounding',
136             'Subtract',
137             0,0, # offset
138             'YXBanded',
139             [ $x,$y, 1,1 ]);
140             return;
141             }
142             } else {
143             ### Window xy() fetch shape ...
144             my ($ordering, @rects) = $X->ShapeGetRectangles ($self->{'-drawable'},
145             'Bounding');
146             ### @rects
147             if (! _rects_contain_xy($x,$y,@rects)) {
148             return 'None';
149             }
150             }
151             }
152             shift->SUPER::xy (@_);
153             }
154              
155             sub line {
156             my ($self, $x1,$y1, $x2,$y2, $colour) = @_;
157             ### X11-Protocol-Window line(): $x1,$y1, $x2,$y2, $colour
158              
159             if ($colour eq 'None'
160             && (my $X = $self->{'-X'}) ->{'ext'}->{'SHAPE'}) {
161              
162             unless (Image::Base::X11::Protocol::Drawable::_line_any_positive($x1,$y1, $x2,$y2)) {
163             ### nothing positive ...
164             return;
165             }
166             my $bitmap_width = abs($x2-$x1)+1;
167             my $bitmap_height = abs($y2-$y1)+1;
168             if ($bitmap_width > 0x7FFF || $bitmap_height > 0x7FFF
169             || $x1 < -0x8000 || $x2 < -0x8000
170             || $x1 > 0x7FFF || $x2 > 0x7FFF
171             || $y1 < -0x8000 || $y2 < -0x8000
172             || $y1 > 0x7FFF || $y2 > 0x7FFF) {
173             ### coordinates would overflow, use superclass ...
174             shift->SUPER::line(@_);
175             return;
176             }
177              
178             my ($bitmap, $bitmap_gc) = _make_bitmap_and_gc
179             ($self, $bitmap_width , $bitmap_height);
180              
181             my $xmin = ($x1 < $x2 ? $x1 : $x2);
182             my $ymin = ($y1 < $y2 ? $y1 : $y2);
183             $X->PolySegment ($bitmap, $bitmap_gc,
184             $x1-$xmin,$y1-$ymin, $x2-$xmin,$y2-$ymin);
185             $X->ShapeMask ($self->{'-drawable'},
186             'Bounding',
187             'Subtract',
188             $xmin,$ymin, # offset
189             $bitmap);
190             $X->FreePixmap ($bitmap);
191             } else {
192             shift->SUPER::line (@_);
193             }
194             }
195              
196             sub rectangle {
197             my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
198             ### Window rectangle: $x1, $y1, $x2, $y2, $colour, $fill
199             if ($colour eq 'None'
200             && (my $X = $self->{'-X'}) ->{'ext'}->{'SHAPE'}) {
201              
202             unless ($x2 >= 0 && $y2 >= 0 && $x1 <= 0x7FFF && $y1 <= 0x7FFF) {
203             ### entirely outside max possible drawable ...
204             return;
205             }
206              
207             # don't underflow INT16 -0x8000 x,y in request
208             # retain negativeness so as not to bring unfilled sides into view
209             if ($x1 < -1) { $x1 = -1; }
210             if ($y1 < -1) { $y1 = -1; }
211              
212             # don't overflow CARD16 width,height in request
213             if ($x2 > 0x7FFF) { $x2 = 0x7FFF; }
214             if ($y2 > 0x7FFF) { $y2 = 0x7FFF; }
215              
216             my @rects;
217             my $width = $x2 - $x1 + 1;
218             my $height = $y2 - $y1 + 1;
219             if ($fill
220             || $width <= 2
221             || $height <= 2) {
222             # filled, or unfilled 2xN or Nx2 as one rectangle
223             @rects = ([ $x1, $y1, $width, $height ]);
224             } else {
225             # unfilled, line segments
226             @rects = ([ $x1, $y1, $width, 1 ], # top
227             [ $x1,$y1+1, 1, $height-2 ], # left
228             [ $x2,$y1+1, 1, $height-2 ], # right
229             [ $x1, $y2, $width, 1 ]); # bottom
230             }
231             $X->ShapeRectangles ($self->{'-drawable'},
232             'Bounding',
233             'Subtract',
234             0,0, # offset
235             'YXBanded', @rects);
236              
237             } else {
238             $self->SUPER::rectangle ($x1, $y1, $x2, $y2, $colour, $fill);
239             }
240             }
241             sub Image_Base_Other_rectangles {
242             ### X11-Protocol-Window rectangles() ...
243             my $self = shift;
244             my $colour = shift;
245             my $fill = shift;
246              
247             # ENHANCE-ME: multiple rectangles at once to ShapeRectangles()
248             ### rectangles: @_
249             while (@_) {
250             $self->rectangle (shift,shift,shift,shift, $colour, $fill);
251             }
252             }
253              
254             sub ellipse {
255             my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
256             ### Window ellipse(): $x1,$y1, $x2,$y2, $colour
257             if ($colour eq 'None'
258             && (my $X = $self->{'-X'}) ->{'ext'}->{'SHAPE'}) {
259             ### use shape ...
260              
261             unless ($x2 >= 0 && $y2 >= 0 && $x1 <= 0x7FFF && $y1 <= 0x7FFF) {
262             ### entirely outside max possible drawable ...
263             return;
264             }
265             if ($x1 < -0x8000 || $x2 > 0x7FFF || $y1 < -0x8000 || $y2 > 0x7FFF) {
266             ### coordinates would overflow, use superclass ...
267             shift->SUPER::ellipse(@_);
268             return;
269             }
270              
271             my $win = $self->{'-drawable'};
272             my $w = $x2 - $x1;
273             my $h = $y2 - $y1;
274             if ($w <= 1 || $h <= 1) {
275             $X->ShapeRectangles ($win,
276             'Bounding',
277             'Subtract',
278             0,0, # offset
279             'YXBanded',
280             [ $x1, $y1, $w+1, $h+1 ]);
281             } else {
282             my ($bitmap, $bitmap_gc) = _make_bitmap_and_gc ($self, $w+1, $h+1);
283              
284             # fill+outline per comments in Drawable.pm
285             my @args = ($bitmap, $bitmap_gc, [ 0, 0, $w, $h, 0, 365*64 ]);
286             if ($fill) {
287             $X->PolyFillArc (@args);
288             }
289             $X->PolyArc (@args);
290              
291             $X->ShapeMask ($self->{'-drawable'},
292             'Bounding',
293             'Subtract',
294             $x1,$y1, # offset
295             $bitmap);
296             $X->FreePixmap ($bitmap);
297             }
298             } else {
299             shift->SUPER::ellipse (@_);
300             }
301             }
302              
303             sub diamond {
304             my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
305             ### Window diamond(): $x1,$y1, $x2,$y2, $colour
306              
307             if ($colour eq 'None'
308             && (my $X = $self->{'-X'}) ->{'ext'}->{'SHAPE'}) {
309             ### use shape ...
310              
311             if ($x1==$x2 && $y1==$y2) {
312             # 1x1 polygon draws nothing, do it as a point instead
313             $self->xy ($x1,$y1, $colour);
314             return;
315             }
316              
317             unless ($x2 >= 0 && $y2 >= 0 && $x1 <= 0x7FFF && $y1 <= 0x7FFF) {
318             ### entirely outside max possible drawable ...
319             return;
320             }
321             if ($x1 < -0x8000 || $x2 > 0x7FFF || $y1 < -0x8000 || $y2 > 0x7FFF) {
322             ### coordinates would overflow, use superclass ...
323             shift->SUPER::diamond(@_);
324             return;
325             }
326              
327             my $drawable = $self->{'-drawable'};
328              
329             $x2 -= $x1; # offset so 0,0 to x2,y2
330             $y2 -= $y1;
331             my ($bitmap, $bitmap_gc)
332             = _make_bitmap_and_gc ($self, $x2+1, $y2+1); # width,height
333             Image::Base::X11::Protocol::Drawable::_diamond_drawable
334             ($X, $bitmap, $bitmap_gc, 0,0, $x2,$y2, $fill);
335             $X->ShapeMask ($drawable,
336             'Bounding',
337             'Subtract',
338             $x1,$y1, # offset
339             $bitmap);
340             $X->FreePixmap ($bitmap);
341              
342             } else {
343             shift->SUPER::diamond (@_);
344             }
345             }
346              
347             #------------------------------------------------------------------------------
348             sub _make_bitmap_and_gc {
349             my ($self, $width, $height) = @_;
350             ### _make_bitmap_and_gc(): "$width,$height"
351             my $X = $self->{'-X'};
352              
353             my $bitmap = $X->new_rsrc;
354             ### CreatePixmap of bitmap: $bitmap
355             $X->CreatePixmap ($bitmap, $self->{'-drawable'}, 1, $width, $height);
356              
357             my $bitmap_gc = $self->{'_bitmap_gc'};
358             if ($bitmap_gc) {
359             $X->ChangeGC ($bitmap_gc, foreground => 0);
360             } else {
361             $bitmap_gc = $X->new_rsrc;
362             $X->CreateGC ($bitmap_gc, $bitmap, foreground => 0);
363             }
364             $X->PolyFillRectangle ($bitmap, $bitmap_gc, [0,0, $width,$height]);
365             $X->ChangeGC ($bitmap_gc, foreground => 1);
366             return ($bitmap, $bitmap_gc);
367             }
368              
369             #------------------------------------------------------------------------------
370              
371             # _rects_contain_xy($x,$y, [$rx,$ry,$rw,$rh],...) returns true if pixel
372             # $x,$y is within any of the given rectangle arrayrefs.
373             #
374             # For any order except Unsorted could stop searching when $ry > $y, if that
375             # was worth the extra code.
376             #
377             sub _rects_contain_xy {
378             ### _rects_contain_xy() ...
379             my $x = shift;
380             my $y = shift;
381             while (@_) {
382             my ($rx,$ry,$width,$height) = @{(shift)};
383             if ($rx <= $x && $rx+$width > $x
384             && $ry <= $y && $ry+$height > $y) {
385             ### found: "$x,$y in $rx,$ry, $width,$height"
386             return 1;
387             }
388             }
389             ### not found ...
390             return 0;
391             }
392              
393              
394             1;
395             __END__