File Coverage

blib/lib/X11/Protocol/ChooseWindow.pm
Criterion Covered Total %
statement 13 114 11.4
branch 0 44 0.0
condition 0 28 0.0
subroutine 5 17 29.4
pod 1 8 12.5
total 19 211 9.0


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2016, 2017, 2019 Kevin Ryde
2              
3             # This file is part of X11-Protocol-Other.
4             #
5             # X11-Protocol-Other 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             # X11-Protocol-Other 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 X11-Protocol-Other. If not, see .
17              
18              
19 1     1   1022 BEGIN { require 5 }
20             package X11::Protocol::ChooseWindow;
21 1     1   5 use strict;
  1         2  
  1         20  
22 1     1   4 use Carp;
  1         2  
  1         57  
23              
24 1     1   5 use vars '$VERSION', '$_instance';
  1         2  
  1         57  
25             $VERSION = 31;
26              
27 1     1   728 use X11::Protocol::WM;
  1         3  
  1         1261  
28              
29             # uncomment this to run the ### lines
30             # use Smart::Comments;
31              
32              
33             # undocumented yet ...
34             sub new {
35 0     0 0   my $class = shift;
36 0           return bless { want_client => 1,
37             @_ }, $class;
38             }
39              
40             sub _X {
41 0     0     my ($self) = @_;
42 0   0       return ($self->{'X'} ||= do {
43 0           require X11::Protocol;
44 0           my $display = $self->{'display'};
45             ### $display
46 0 0         X11::Protocol->new (defined $display ? ($display) : ());
47             });
48             }
49              
50             sub choose {
51 0     0 1   my ($self, %options) = @_;
52 0 0         unless (ref $self) {
53 0           $self = $self->new; # X11::Protocol::ChooseWindow->choose()
54             }
55 0           local @{$self}{keys %options} = values %options; # hash slice
  0            
56 0           local $_instance = $self;
57              
58 0           my $X = _X($self);
59             {
60 0           my $old_event_handler = $X->{'event_handler'};
  0            
61             local $X->{'event_handler'} = sub {
62 0     0     $self->handle_event (@_);
63 0           goto $old_event_handler;
64 0           };
65              
66 0           $self->start;
67 0           do {
68 0           $X->handle_input;
69             } until ($self->is_done);
70             }
71              
72 0           return $self->chosen_window;
73             }
74              
75             sub chosen_window {
76 0     0 0   my ($self) = @_;
77 0 0         if ($self->{'want_client'}) {
78 0           return $self->client_window;
79             } else {
80 0           return $self->{'frame_window'};
81             }
82             }
83             sub client_window {
84 0     0 0   my ($self) = @_;
85 0 0         if (! exists $self->{'client_window'}) {
86 0           my $frame_window = $self->{'frame_window'};
87             ### frame_window: $frame_window.sprintf(' 0x%X',$frame_window)
88 0 0 0       $self->{'client_window'}
89             = (defined $frame_window && _num_none($frame_window) != 0
90             ? X11::Protocol::WM::frame_window_to_client(_X($self),$frame_window)
91             : undef);
92             ### client_window: $self->{'client_window'}
93             }
94 0           return $self->{'client_window'};
95             }
96              
97             # undocumented yet ...
98             sub start {
99 0     0 0   my ($self) = @_;
100              
101 0           $self->abort;
102 0           $self->{'frame_window'} = undef;
103 0           delete $self->{'client_window'};
104 0           $self->{'button_released'} = 0;
105 0           my $X = _X($self);
106              
107 0           my $want_free_cursor;
108 0           my $cursor = $self->{'cursor'};
109 0 0         if (! defined $cursor) {
110 0           my $cursor_glyph = $self->{'cursor_glyph'};
111 0 0         if (! defined $cursor_glyph) {
112 0           require X11::CursorFont;
113 0           my $cursor_name = $self->{'cursor_name'};
114 0 0         if (! defined $cursor_name) {
115 0           $cursor_name = 'crosshair'; # default
116             }
117 0           $cursor_glyph = $X11::CursorFont::CURSOR_GLYPH{$cursor_name};
118 0 0         if (! defined $cursor_glyph) {
119 0           croak "Unrecognised cursor_name: ",$cursor_name;
120             }
121             }
122              
123 0           my $cursor_font = $X->new_rsrc;
124 0           $X->OpenFont ($cursor_font, 'cursor');
125              
126 0           $cursor = $X->new_rsrc;
127 0           $X->CreateGlyphCursor ($cursor,
128             $cursor_font, # font
129             $cursor_font, # mask font
130             $cursor_glyph, # glyph number
131             $cursor_glyph+1, # and its mask
132             0,0,0, # foreground, black
133             0xFFFF, 0xFFFF, 0xFFFF); # background, white
134 0           $want_free_cursor = 1;
135 0           $X->CloseFont ($cursor_font);
136             }
137             ### cursor: sprintf '%d %#X', $cursor, $cursor
138              
139 0           my $root = $self->{'root'};
140 0 0         if (! defined $root) {
141 0 0         if (defined (my $screen_number = $self->{'screen'})) {
142 0           $root = $X->{'screens'}->[$screen_number]->{'root'};
143             } else {
144 0           $root = $X->{'root'};
145             }
146             }
147             ### $root
148              
149             # follow any __SWM_VROOT
150 0   0       $root = (X11::Protocol::WM::root_to_virtual_root($X,$root) || $root);
151              
152 0   0       my $time = $self->{'time'} || $self->{'event'}->{'time'} || 'CurrentTime';
153             ### $time
154              
155 0           my $status = $X->GrabPointer
156             ($root, # window
157             0, # owner events
158             $X->pack_event_mask('ButtonPress','ButtonRelease'),
159             'Synchronous', # pointer mode
160             'Asynchronous', # keyboard mode
161             $root, # confine window
162             $cursor, # crosshair cursor
163             $time);
164 0 0         if ($status eq 'Success') {
165 0           $self->{'ungrab_time'} = $time;
166             }
167 0 0         if ($want_free_cursor) {
168 0           $X->FreeCursor ($cursor);
169             }
170 0 0         if ($status ne 'Success') {
171 0           croak "Cannot grab mouse pointer to choose a window: ",$status;
172             }
173 0           $X->AllowEvents ('SyncPointer', 'CurrentTime');
174             }
175              
176             # undocumented yet ...
177             sub handle_event {
178 0     0 0   my ($self, %h) = @_;
179             ### ChooseWindow handle_event: %h
180 0 0         return if $self->is_done;
181              
182 0           my $name = $h{'name'};
183 0           my $X = _X($self);
184              
185 0 0         if ($name eq 'ButtonPress') {
    0          
186             ### ButtonPress
187 0           $self->{'frame_window'} = $h{'child'};
188 0           $self->{'choose_time'} = $h{'time'};
189 0           $X->AllowEvents ('SyncPointer', 'CurrentTime');
190              
191             } elsif ($name eq 'ButtonRelease') {
192             ### ButtonRelease
193             # wait for button pressed to choose window, and then released so the
194             # release event doesn't go to the chosen window
195 0 0         if ($self->{'frame_window'}) {
196             # button press seen, and now release seen
197 0           $self->{'button_released'} = 1;
198 0           $self->{'ungrab_time'} = $h{'time'};
199 0           $self->abort; # ungrab
200             } else {
201 0           $X->AllowEvents ('SyncPointer', 'CurrentTime');
202             }
203             }
204             }
205              
206             # undocumented yet ...
207             sub is_done {
208 0     0 0   my ($self) = @_;
209             return (! defined $self->{'ungrab_time'} # aborted or never started
210 0   0       || ($self->{'frame_window'} && $self->{'button_released'}));
211             }
212              
213             sub DESTROY {
214 0     0     my ($self) = @_;
215 0           my ($X, $ungrab_time);
216 0 0 0       if (defined ($X = $self->{'X'})
217             && defined ($ungrab_time = delete $self->{'ungrab_time'})) {
218             # no errors if connection gone
219 0           eval { $X->UngrabPointer ($ungrab_time) };
  0            
220             }
221             }
222              
223             # undocumented yet ...
224             sub abort {
225 0     0 0   my ($self, $time) = @_;
226 0 0         if (! ref $self) {
227             # class method X11::Protocol::ChooseWindow->abort()
228 0   0       $self = $_instance || return; # if not in a ->choose()
229             }
230 0           my ($X, $ungrab_time);
231 0 0 0       if (defined ($X = $self->{'X'})
232             && defined ($ungrab_time = delete $self->{'ungrab_time'})) {
233 0   0       $X->UngrabPointer ($time || $ungrab_time);
234             }
235             }
236              
237             sub _num_none {
238 0     0     my ($xid) = @_;
239 0 0 0       if (defined $xid && $xid eq "None") {
240 0           return 0;
241             } else {
242 0           return $xid;
243             }
244             }
245              
246             1;
247             __END__