1
###################################################
3
# Copyright (C) Mario Kemper <mario.kemper@googlemail.com> and Shutter Team
5
# This file is part of Shutter.
7
# Shutter is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
12
# Shutter is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with Shutter; if not, write to the Free Software
19
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
###################################################
23
package Shutter::Screenshot::Window;
26
#--------------------------------------
29
use Shutter::Screenshot::Main;
31
our @ISA = qw(Shutter::Screenshot::Main);
34
#--------------------------------------
35
use constant TRUE => 1;
36
use constant FALSE => 0;
38
#--------------------------------------
43
#call constructor of super class (shutter_common, include_cursor, delay)
44
my $self = $class->SUPER::new( shift, shift, shift );
47
$self->{_x11} = shift;
48
$self->{_include_border} = shift;
49
$self->{_xid} = shift; #only used by window_by_xid, undef this when selecting a window
50
$self->{_mode} = shift;
51
$self->{_is_in_tray} = shift;
54
$self->{_main_gtk_window} = $self->{_gc}->get_mainwindow;
56
#only used by window_select
57
$self->{_children} = {};
68
my ( $qroot, $qparent, @qkids ) = $self->{_x11}->QueryTree($xid);
69
return undef unless ( $qroot || $qparent );
70
return $xid if ( $qroot == $qparent );
76
my ( $self, $xwindow, $xparent ) = @_;
77
my ( $qroot, $qparent, @qkids ) = $self->{_x11}->QueryTree($xwindow);
80
my $gdk_window = Gtk2::Gdk::Window->foreign_new($_);
81
if ( defined $gdk_window ) {
83
#window needs to be viewable and visible
84
next unless $gdk_window->is_visible;
85
next unless $gdk_window->is_viewable;
88
my ( $xp, $yp, $widthp, $heightp, $depthp ) = $gdk_window->get_geometry;
89
( $xp, $yp ) = $gdk_window->get_origin;
90
next if ( $widthp * $heightp < 4 );
92
#check if $gdk_window is already in hash
94
foreach my $checkchild ( keys %{ $self->{_children}{$xparent} } ) {
96
if $self->{_children}{$xparent}{$checkchild}{'gdk_window'} == $gdk_window;
98
unless ( $dub == TRUE ) {
99
$self->{_children}{$xparent}{$_}{'gdk_window'} = $gdk_window;
100
$self->{_children}{$xparent}{$_}{'x'} = $xp;
101
$self->{_children}{$xparent}{$_}{'y'} = $yp;
102
$self->{_children}{$xparent}{$_}{'width'} = $widthp;
103
$self->{_children}{$xparent}{$_}{'height'} = $heightp;
104
$self->{_children}{$xparent}{$_}{'size'} = $widthp * $heightp;
107
$self->query_children( $gdk_window->XWINDOW, $xparent );
119
print "Calculating window shape\n" if $self->{_gc}->get_debug;
121
my ($ordering, @r) = $self->{_x11}->ShapeGetRectangles($self->find_wm_window($xid), 'Bounding');
123
#do nothing if there are no
124
#shape rectangles (or only one)
125
return $orig if scalar @r <= 1;
127
#create a region from the bounding rectangles
128
my $bregion = Gtk2::Gdk::Region->new;
131
print "Current $rect[0],$rect[1],$rect[2],$rect[3]\n" if $self->{_gc}->get_debug;
132
$bregion->union_with_rect(Gtk2::Gdk::Rectangle->new ($rect[0],$rect[1],$rect[2],$rect[3]));
135
#create target pixbuf with dimensions if selected/current window
136
my $target = Gtk2::Gdk::Pixbuf->new ($orig->get_colorspace, TRUE, 8, $orig->get_width, $orig->get_height);
137
#whole pixbuf is transparent
138
$target->fill('0x00000000');
140
#copy all rectangles of bounding region to the target pixbuf
141
foreach my $r($bregion->get_rectangles){
142
print $r->x." ".$r->y." ".$r->width." ".$r->height."\n" if $self->{_gc}->get_debug;
144
next if($r->x > $orig->get_width);
145
next if($r->y > $orig->get_height);
147
$r->width($orig->get_width - $r->x) if($r->x+$r->width > $orig->get_width);
148
$r->height($orig->get_height - $r->y) if($r->y+$r->height > $orig->get_height);
150
$orig->copy_area ($r->x, $r->y, $r->width, $r->height, $target, $r->x, $r->y);
156
sub get_window_size {
157
my ( $self, $wnck_window, $gdk_window, $border ) = @_;
159
my ( $xp, $yp, $widthp, $heightp ) = $wnck_window->get_geometry;
164
= Gtk2::Gdk::Window->foreign_new( $self->find_wm_window( $wnck_window->get_xid ) );
165
$gdk_window = $wm_window if $wm_window;
168
my ( $xp2, $yp2, $widthp2, $heightp2 ) = $gdk_window->get_geometry;
169
( $xp2, $yp2 ) = $gdk_window->get_origin;
171
#check the correct rect
172
if ( $xp2 + $widthp2 > $xp + $widthp
173
&& $yp2 + $heightp2 > $yp + $heightp )
175
( $xp, $yp, $widthp, $heightp ) = ( $xp2, $yp2, $widthp2, $heightp2 );
179
( $widthp, $heightp ) = $gdk_window->get_size;
180
( $xp, $yp ) = $gdk_window->get_origin;
183
return ( $xp, $yp, $widthp, $heightp );
189
my $gdk_window = Gtk2::Gdk::Window->foreign_new( $self->{_xid} );
190
my $wnck_window = Gnome2::Wnck::Window->get( $self->{_xid} );
192
my ( $xp, $yp, $widthp, $heightp )
193
= $self->get_window_size( $wnck_window, $gdk_window, $self->{_include_border} );
195
#focus selected window (maybe it is hidden)
196
$gdk_window->focus(time);
198
sleep 1 if $self->{_delay} < 1;
200
my $output = $self->get_pixbuf_from_drawable( $self->{_root}, $xp, $yp, $widthp, $heightp,
201
$self->{_include_cursor},
204
#respect rounded corners of wm decorations (metacity for example - does not work with compiz currently)
205
if($self->{_x11}{ext_shape}){
206
$output = $self->get_shape($self->{_xid}, $output);
220
my @wnck_windows = $self->{_wnck_screen}->get_windows;
222
#...and window "pick" cursor
223
my $hand_cursor2 = Gtk2::Gdk::Cursor->new('GDK_HAND2');
225
#define graphics context
227
my $white = Gtk2::Gdk::Color->new( 65535, 65535, 65535 );
228
my $black = Gtk2::Gdk::Color->new( 0, 0, 0 );
229
my $gc = Gtk2::Gdk::GC->new( $self->{_root}, undef );
230
$gc->set_line_attributes( 5, 'solid', 'round', 'round' );
231
$gc->set_rgb_bg_color($black);
232
$gc->set_rgb_fg_color($white);
233
$gc->set_subwindow('include-inferiors');
234
$gc->set_function('xor');
236
my $grab_counter = 0;
237
while ( !Gtk2::Gdk->pointer_is_grabbed && $grab_counter < 100 ) {
238
Gtk2::Gdk->pointer_grab(
247
Gtk2->get_current_event_time
249
Gtk2::Gdk->keyboard_grab( $self->{_root}, 0, Gtk2->get_current_event_time );
253
if ( Gtk2::Gdk->pointer_is_grabbed ) {
255
$self->{_children} = ();
256
my $drawable = undef;
257
my $window_selected = FALSE;
258
$self->{_children}{'last_win'}{'gdk_window'} = 0;
259
my $active_workspace = $self->{_wnck_screen}->get_active_workspace;
261
#something went wrong here, no active workspace detected
262
unless ( $active_workspace ) {
263
$self->ungrab_pointer_and_keyboard( FALSE, FALSE, FALSE );
268
Gtk2::Gdk::Event->handler_set(
270
my ( $event, $data ) = @_;
271
return FALSE unless defined $event;
273
#handle key events here
274
if ( $event->type eq 'key-press' ) {
275
next unless defined $event->keyval;
276
if ( $event->keyval == $Gtk2::Gdk::Keysyms{Escape} ) {
278
#clear the last rectangle
279
if ( defined $self->{_children}{'last_win'} ) {
280
$self->{_root}->draw_rectangle(
283
$self->{_children}{'last_win'}{'x'},
284
$self->{_children}{'last_win'}{'y'},
285
$self->{_children}{'last_win'}{'width'},
286
$self->{_children}{'last_win'}{'height'}
291
$self->ungrab_pointer_and_keyboard( FALSE, TRUE, TRUE );
295
} elsif ( $event->type eq 'button-release' ) {
296
print "Type: " . $event->type . "\n"
297
if ( defined $event && $self->{_gc}->get_debug );
299
#looking for a section of a window?
300
#keep current window in mind and search for children
301
if ( ( $self->{_mode} eq "section" || $self->{_mode} eq "tray_section" )
302
&& !$window_selected )
305
#something went wrong here, no window on screen detected
306
unless ( $self->{_children}{'last_win'}{'gdk_window'} ) {
307
$self->ungrab_pointer_and_keyboard( FALSE, TRUE, TRUE );
312
$self->query_children(
313
$self->{_children}{'last_win'}{'gdk_window'}->XWINDOW,
314
$self->{_children}{'last_win'}{'gdk_window'}->XWINDOW
317
#focus selected window (maybe it is hidden)
318
$self->{_children}{'last_win'}{'gdk_window'}->focus(time);
321
= $self->{_children}{'curr_win'}{'gdk_window'};
326
$self->ungrab_pointer_and_keyboard( FALSE, TRUE, TRUE );
328
#clear the last rectangle
329
if ( defined $self->{_children}{'last_win'}
330
&& $self->{_children}{'last_win'}{'gdk_window'} )
332
$self->{_root}->draw_rectangle(
335
$self->{_children}{'last_win'}{'x'},
336
$self->{_children}{'last_win'}{'y'},
337
$self->{_children}{'last_win'}{'width'},
338
$self->{_children}{'last_win'}{'height'}
341
#focus selected window (maybe it is hidden)
342
$self->{_children}{'last_win'}{'gdk_window'}->focus(time);
344
sleep 1 if $self->{_delay} < 1;
346
$output = $self->get_pixbuf_from_drawable(
348
$self->{_children}{'curr_win'}{'x'},
349
$self->{_children}{'curr_win'}{'y'},
350
$self->{_children}{'curr_win'}{'width'},
351
$self->{_children}{'curr_win'}{'height'},
352
$self->{_include_cursor},
356
#respect rounded corners of wm decorations (metacity for example - does not work with compiz currently)
357
if($self->{_x11}{ext_shape}){
358
my $xid = $self->{_children}{ 'curr_win' }{ 'gdk_window' }->get_xid;
359
#do not try this for child windows
360
foreach my $win ($self->{_wnck_screen}->get_windows){
361
if($win->get_xid == $xid){
362
$output = $self->get_shape($xid, $output);
370
} elsif ( $event->type eq 'motion-notify' ) {
371
print "Type: " . $event->type . "\n"
372
if ( defined $event && $self->{_gc}->get_debug );
374
my $min_x = $self->{_root}->{w};
375
my $min_y = $self->{_root}->{h};
377
#if there is no window already selected
378
unless ($window_selected) {
379
print "Searching for window...\n"
380
if $self->{_gc}->get_debug;
381
foreach my $curr_window (@wnck_windows) {
382
$drawable = Gtk2::Gdk::Window->foreign_new( $curr_window->get_xid );
383
next unless defined $drawable;
385
print "Do not detect gscrot main window...\n"
386
if $self->{_gc}->get_debug;
388
#do not detect gscrot window when it is hidden
389
if ( $self->{_main_gtk_window}->window
390
&& $self->{_is_in_tray} )
393
if ( $curr_window->get_xid
394
== $self->{_main_gtk_window}->window->get_xid );
397
my ( $xp, $yp, $widthp, $heightp )
398
= $self->get_window_size( $curr_window, $drawable,
399
$self->{_include_border} );
401
print "Create region of window...\n"
402
if $self->{_gc}->get_debug;
403
my $window_region = Gtk2::Gdk::Region->rectangle(
404
Gtk2::Gdk::Rectangle->new( $xp, $yp, $widthp, $heightp ) );
406
print "determine if window fits on screen...\n"
407
if $self->{_gc}->get_debug;
408
if ($curr_window->is_visible_on_workspace(
411
&& $window_region->point_in( $event->x, $event->y )
414
print "Parent X: $xp, Y: $yp, Width: $widthp, Height: $heightp\n"
415
if $self->{_gc}->get_debug;
416
$self->{_children}{'curr_win'}{'window'} = $curr_window;
417
$self->{_children}{'curr_win'}{'gdk_window'} = $drawable;
418
$self->{_children}{'curr_win'}{'x'} = $xp;
419
$self->{_children}{'curr_win'}{'y'} = $yp;
420
$self->{_children}{'curr_win'}{'width'} = $widthp;
421
$self->{_children}{'curr_win'}{'height'} = $heightp;
422
$min_x = $xp + $widthp;
423
$min_y = $yp + $heightp;
427
} #end if toplevel window loop
429
#something went wrong here, no window on screen detected
430
unless ( $self->{_children}{'curr_win'}{'window'} ) {
431
$self->ungrab_pointer_and_keyboard( FALSE, TRUE, TRUE );
436
#window selected, search for children now
437
} elsif ( ( $self->{_mode} eq "section" || $self->{_mode} eq "tray_section" )
438
&& $window_selected )
440
print "Searching for children now...\n"
441
if $self->{_gc}->get_debug;
443
#selected window is parent
444
my $curr_parent = $window_selected->XWINDOW;
445
foreach my $curr_child ( keys %{ $self->{_children}{$curr_parent} } ) {
446
next unless defined $curr_child;
447
print "Child Current Event x: " . $event->x . ", y: " . $event->y . "\n"
448
if $self->{_gc}->get_debug;
450
my $section_region = Gtk2::Gdk::Region->rectangle(
451
Gtk2::Gdk::Rectangle->new(
452
$self->{_children}{$curr_parent}{$curr_child}{'x'},
453
$self->{_children}{$curr_parent}{$curr_child}{'y'},
454
$self->{_children}{$curr_parent}{$curr_child}{'width'},
455
$self->{_children}{$curr_parent}{$curr_child}{'height'}
459
if ($section_region->point_in( $event->x, $event->y )
462
( ( $self->{_children}{$curr_parent}{$curr_child}{'x'}
463
+ $self->{_children}{$curr_parent}{$curr_child}{'width'}
465
$self->{_children}{$curr_parent}{$curr_child}{'y'}
466
+ $self->{_children}{$curr_parent}{$curr_child}
473
$self->{_children}{'curr_win'}{'gdk_window'}
474
= $self->{_children}{$curr_parent}{$curr_child}{'gdk_window'};
475
$self->{_children}{'curr_win'}{'x'}
476
= $self->{_children}{$curr_parent}{$curr_child}{'x'};
477
$self->{_children}{'curr_win'}{'y'}
478
= $self->{_children}{$curr_parent}{$curr_child}{'y'};
479
$self->{_children}{'curr_win'}{'width'}
480
= $self->{_children}{$curr_parent}{$curr_child}{'width'};
481
$self->{_children}{'curr_win'}{'height'}
482
= $self->{_children}{$curr_parent}{$curr_child}{'height'};
483
$min_x = $self->{_children}{$curr_parent}{$curr_child}{'x'}
484
+ $self->{_children}{$curr_parent}{$curr_child}{'width'};
485
$min_y = $self->{_children}{$curr_parent}{$curr_child}{'y'}
486
+ $self->{_children}{$curr_parent}{$curr_child}{'height'};
489
} #endif search for children
492
if ( $self->{_children}{'last_win'}{'gdk_window'} ne
493
$self->{_children}{'curr_win'}{'gdk_window'} )
496
#clear last rectangle
497
if ( $self->{_children}{'last_win'}{'gdk_window'} ) {
498
$self->{_root}->draw_rectangle(
501
$self->{_children}{'last_win'}{'x'},
502
$self->{_children}{'last_win'}{'y'},
503
$self->{_children}{'last_win'}{'width'},
504
$self->{_children}{'last_win'}{'height'}
509
#draw new rectangle for current window
510
if ( $self->{_children}{'curr_win'}{'gdk_window'} ) {
511
$self->{_root}->draw_rectangle(
514
$self->{_children}{'curr_win'}{'x'} - 3,
515
$self->{_children}{'curr_win'}{'y'} - 3,
516
$self->{_children}{'curr_win'}{'width'} + 5,
517
$self->{_children}{'curr_win'}{'height'} + 5
521
$self->{_children}{'last_win'}{'window'}
522
= $self->{_children}{'curr_win'}{'window'};
523
$self->{_children}{'last_win'}{'gdk_window'}
524
= $self->{_children}{'curr_win'}{'gdk_window'};
525
$self->{_children}{'last_win'}{'x'}
526
= $self->{_children}{'curr_win'}{'x'} - 3;
527
$self->{_children}{'last_win'}{'y'}
528
= $self->{_children}{'curr_win'}{'y'} - 3;
529
$self->{_children}{'last_win'}{'width'}
530
= $self->{_children}{'curr_win'}{'width'} + 5;
531
$self->{_children}{'last_win'}{'height'}
532
= $self->{_children}{'curr_win'}{'height'} + 5;
536
Gtk2->main_do_event($event);
542
} else { #pointer not grabbed
544
$self->ungrab_pointer_and_keyboard( FALSE, FALSE, FALSE );