2 Copyright (C) 2004 Bernd Brandstetter <bbrand@freenet.de>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or ( at your option ) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this library; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "windowgrabber.h"
24 #include <kwindowinfo.h>
30 #include <QMouseEvent>
31 #include <QWheelEvent>
34 #include <config-ksnapshot.h>
35 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
36 #include <X11/extensions/shape.h>
41 const int minSize
= 8;
44 bool operator< ( const QRect
& r1
, const QRect
& r2
)
46 return r1
.width() * r1
.height() < r2
.width() * r2
.height();
49 // Recursively iterates over the window w and its children, thereby building
50 // a tree of window descriptors. Windows in non-viewable state or with height
51 // or width smaller than minSize will be ignored.
53 void getWindowsRecursive( std::vector
<QRect
>& windows
, Window w
,
54 int rx
= 0, int ry
= 0, int depth
= 0 )
56 XWindowAttributes atts
;
57 XGetWindowAttributes( QX11Info::display(), w
, &atts
);
58 if ( atts
.map_state
== IsViewable
&&
59 atts
.width
>= minSize
&& atts
.height
>= minSize
) {
66 QRect
r( x
, y
, atts
.width
, atts
.height
);
67 if ( std::find( windows
.begin(), windows
.end(), r
) == windows
.end() ) {
68 windows
.push_back( r
);
73 unsigned int nchildren
;
75 if( XQueryTree( QX11Info::display(), w
, &root
, &parent
, &children
, &nchildren
) != 0 ) {
76 for( unsigned int i
= 0; i
< nchildren
; ++i
) {
77 getWindowsRecursive( windows
, children
[ i
], x
, y
, depth
+ 1 );
79 if( children
!= NULL
)
84 std::sort( windows
.begin(), windows
.end() );
88 Window
findRealWindow( Window w
, int depth
= 0 )
92 static Atom wm_state
= XInternAtom( QX11Info::display(), "WM_STATE", False
);
95 unsigned long nitems
, after
;
97 if( XGetWindowProperty( QX11Info::display(), w
, wm_state
, 0, 0, False
, AnyPropertyType
,
98 &type
, &format
, &nitems
, &after
, &prop
) == Success
) {
106 unsigned int nchildren
;
108 if( XQueryTree( QX11Info::display(), w
, &root
, &parent
, &children
, &nchildren
) != 0 ) {
109 for( unsigned int i
= 0;
110 i
< nchildren
&& ret
== None
;
112 ret
= findRealWindow( children
[ i
], depth
+ 1 );
113 if( children
!= NULL
)
120 Window
windowUnderCursor( bool includeDecorations
= true )
125 int rootX
, rootY
, winX
, winY
;
126 XGrabServer( QX11Info::display() );
127 XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &root
, &child
,
128 &rootX
, &rootY
, &winX
, &winY
, &mask
);
130 child
= QX11Info::appRootWindow();
131 if( !includeDecorations
) {
132 Window real_child
= findRealWindow( child
);
133 if( real_child
!= None
) // test just in case
140 QPixmap
grabWindow( Window child
, int x
, int y
, uint w
, uint h
, uint border
,
141 QString
*title
=0, QString
*windowClass
=0 )
143 QPixmap
pm( QPixmap::grabWindow( QX11Info::appRootWindow(), x
, y
, w
, h
) );
145 KWindowInfo
winInfo( findRealWindow(child
), NET::WMVisibleName
, NET::WM2WindowClass
);
147 (*title
) = winInfo
.visibleName();
149 (*windowClass
) = winInfo
.windowClassName();
151 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
153 //Check whether the extension is available
154 if ( XShapeQueryExtension( QX11Info::display(), &tmp1
, &tmp2
) ) {
155 QBitmap
mask( w
, h
);
156 //As the first step, get the mask from XShape.
158 XRectangle
* rects
= XShapeGetRectangles( QX11Info::display(), child
,
159 ShapeBounding
, &count
, &order
);
160 //The ShapeBounding region is the outermost shape of the window;
161 //ShapeBounding - ShapeClipping is defined to be the border.
162 //Since the border area is part of the window, we use bounding
163 // to limit our work region
165 //Create a QRegion from the rectangles describing the bounding mask.
167 for ( int pos
= 0; pos
< count
; pos
++ )
168 contents
+= QRegion( rects
[pos
].x
, rects
[pos
].y
,
169 rects
[pos
].width
, rects
[pos
].height
);
172 //Create the bounding box.
173 QRegion
bbox( 0, 0, w
, h
);
176 contents
.translate( border
, border
);
177 contents
+= QRegion( 0, 0, border
, h
);
178 contents
+= QRegion( 0, 0, w
, border
);
179 contents
+= QRegion( 0, h
- border
, w
, border
);
180 contents
+= QRegion( w
- border
, 0, border
, h
);
183 //Get the masked away area.
184 QRegion maskedAway
= bbox
- contents
;
185 QVector
<QRect
> maskedAwayRects
= maskedAway
.rects();
187 //Construct a bitmap mask from the rectangles
189 p
.fillRect(0, 0, w
, h
, Qt::color1
);
190 for (int pos
= 0; pos
< maskedAwayRects
.count(); pos
++)
191 p
.fillRect(maskedAwayRects
[pos
], Qt::color0
);
202 QString
WindowGrabber::title
;
203 QString
WindowGrabber::windowClass
;
205 WindowGrabber::WindowGrabber()
206 : QDialog( 0, Qt::FramelessWindowHint
| Qt::WindowStaysOnTopHint
| Qt::X11BypassWindowManagerHint
),
207 current( -1 ), yPos( -1 )
209 setWindowModality( Qt::WindowModal
);
212 uint w
, h
, border
, depth
;
213 XGrabServer( QX11Info::display() );
214 Window child
= windowUnderCursor();
215 XGetGeometry( QX11Info::display(), child
, &root
, &x
, &y
, &w
, &h
, &border
, &depth
);
216 QPixmap
pm( grabWindow( child
, x
, y
, w
, h
, border
, &title
, &windowClass
) );
217 getWindowsRecursive( windows
, child
);
218 XUngrabServer( QX11Info::display() );
220 QPalette p
= palette();
221 p
.setBrush( backgroundRole(), QBrush( pm
) );
223 setFixedSize( pm
.size() );
224 setMouseTracking( true );
225 setGeometry( x
, y
, w
, h
);
226 current
= windowIndex( mapFromGlobal(QCursor::pos()) );
229 WindowGrabber::~WindowGrabber()
233 QPixmap
WindowGrabber::grabCurrent( bool includeDecorations
)
237 uint w
, h
, border
, depth
;
238 XGrabServer( QX11Info::display() );
239 Window child
= windowUnderCursor( includeDecorations
);
240 XGetGeometry( QX11Info::display(), child
, &root
, &x
, &y
, &w
, &h
, &border
, &depth
);
243 unsigned int nchildren
;
244 if( XQueryTree( QX11Info::display(), child
, &root
, &parent
,
245 &children
, &nchildren
) != 0 ) {
246 if( children
!= NULL
)
250 if( XTranslateCoordinates( QX11Info::display(), parent
, QX11Info::appRootWindow(),
251 x
, y
, &newx
, &newy
, &dummy
)) {
256 QPixmap
pm( grabWindow( child
, x
, y
, w
, h
, border
, &title
, &windowClass
) );
257 XUngrabServer( QX11Info::display() );
261 void WindowGrabber::mousePressEvent( QMouseEvent
*e
)
263 if ( e
->button() == Qt::RightButton
)
267 emit
windowGrabbed( palette().brush( backgroundRole() ).texture().copy( windows
[ current
] ) );
269 emit
windowGrabbed( QPixmap() );
274 void WindowGrabber::mouseReleaseEvent( QMouseEvent
*e
)
276 if ( e
->button() == Qt::RightButton
)
281 const int minDistance
= 10;
283 void WindowGrabber::mouseMoveEvent( QMouseEvent
*e
)
286 int w
= windowIndex( e
->pos() );
287 if ( w
!= -1 && w
!= current
) {
293 int y
= e
->globalY();
294 if ( y
> yPos
+ minDistance
) {
295 decreaseScope( e
->pos() );
298 else if ( y
< yPos
- minDistance
) {
299 increaseScope( e
->pos() );
305 void WindowGrabber::wheelEvent( QWheelEvent
*e
)
307 if ( e
->delta() > 0 )
308 increaseScope( e
->pos() );
309 else if ( e
->delta() < 0 )
310 decreaseScope( e
->pos() );
315 // Increases the scope to the next-bigger window containing the mouse pointer.
316 // This method is activated by either rotating the mouse wheel forwards or by
317 // dragging the mouse forwards while keeping the right mouse button pressed.
318 void WindowGrabber::increaseScope( const QPoint
&pos
)
320 for ( uint i
= current
+ 1; i
< windows
.size(); i
++ ) {
321 if ( windows
[ i
].contains( pos
) ) {
329 // Decreases the scope to the next-smaller window containing the mouse pointer.
330 // This method is activated by either rotating the mouse wheel backwards or by
331 // dragging the mouse backwards while keeping the right mouse button pressed.
332 void WindowGrabber::decreaseScope( const QPoint
&pos
)
334 for ( int i
= current
- 1; i
>= 0; i
-- ) {
335 if ( windows
[ i
].contains( pos
) ) {
343 // Searches and returns the index of the first (=smallest) window
344 // containing the mouse pointer.
345 int WindowGrabber::windowIndex( const QPoint
&pos
) const
347 for ( uint i
= 0; i
< windows
.size(); i
++ ) {
348 if ( windows
[ i
].contains( pos
) )
354 // Draws a border around the (child) window currently containing the pointer
355 void WindowGrabber::paintEvent( QPaintEvent
* )
357 if ( current
>= 0 ) {
360 p
.fillRect(rect(), palette().brush( backgroundRole()));
361 p
.setPen( QPen( Qt::red
, 3 ) );
362 p
.drawRect( windows
[ current
].adjusted( 0, 0, -1, -1 ) );
367 #include "windowgrabber.moc"