2 Copyright (C) 2004 Bernd Brandstetter <bbrand@freenet.de>
3 Copyright (C) 2010, 2011 Pau Garcia i Quiles <pgquiles@elpauer.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or ( at your option ) any later version.
10 This program 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
13 Library General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "windowgrabber.h"
26 #include <kwindowinfo.h>
34 #include <QtGui/qevent.h>
38 #include <config-ksnapshot.h>
39 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
40 #include <X11/extensions/shape.h>
41 #endif // HAVE_X11_EXTENSIONS_SHAPE_H
46 const int minSize = 8;
49 static inline bool operator< ( const QRect& r1, const QRect& r2 )
51 return r1.width() * r1.height() < r2.width() * r2.height();
55 // Recursively iterates over the window w and its children, thereby building
56 // a tree of window descriptors. Windows in non-viewable state or with height
57 // or width smaller than minSize will be ignored.
60 void getWindowsRecursive( std::vector<QRect> *windows, Window w,
61 int rx = 0, int ry = 0, int depth = 0 )
63 XWindowAttributes atts;
64 XGetWindowAttributes( QX11Info::display(), w, &atts );
66 if ( atts.map_state == IsViewable &&
67 atts.width >= minSize && atts.height >= minSize ) {
74 QRect r( x, y, atts.width, atts.height );
75 if ( std::find( windows->begin(), windows->end(), r ) == windows->end() ) {
76 windows->push_back( r );
81 unsigned int nchildren;
83 if( XQueryTree( QX11Info::display(), w, &root, &parent, &children, &nchildren ) != 0 ) {
84 for( unsigned int i = 0; i < nchildren; ++i ) {
85 getWindowsRecursive( windows, children[ i ], x, y, depth + 1 );
88 if( children != NULL ) {
95 std::sort( windows->begin(), windows->end() );
100 Window findRealWindow( Window w, int depth = 0 )
106 static Atom wm_state = XInternAtom( QX11Info::display(), "WM_STATE", False );
109 unsigned long nitems, after;
112 if( XGetWindowProperty( QX11Info::display(), w, wm_state, 0, 0, False, AnyPropertyType,
113 &type, &format, &nitems, &after, &prop ) == Success ) {
125 unsigned int nchildren;
128 if( XQueryTree( QX11Info::display(), w, &root, &parent, &children, &nchildren ) != 0 ) {
129 for( unsigned int i = 0;
130 i < nchildren && ret == None;
132 ret = findRealWindow( children[ i ], depth + 1 );
135 if( children != NULL ) {
144 Window windowUnderCursor( bool includeDecorations = true )
149 int rootX, rootY, winX, winY;
151 XGrabServer( QX11Info::display() );
152 XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &root, &child,
153 &rootX, &rootY, &winX, &winY, &mask );
155 if( child == None ) {
156 child = QX11Info::appRootWindow();
159 if( !includeDecorations ) {
160 Window real_child = findRealWindow( child );
162 if( real_child != None ) { // test just in case
171 QPixmap grabWindow( Window child, int x, int y, uint w, uint h, uint border,
172 QString *title=0, QString *windowClass=0 )
174 QPixmap pm( QPixmap::grabWindow( QX11Info::appRootWindow(), x, y, w, h ) );
176 KWindowInfo winInfo( findRealWindow(child), NET::WMVisibleName, NET::WM2WindowClass );
179 (*title) = winInfo.visibleName();
183 (*windowClass) = winInfo.windowClassName();
186 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
188 //Check whether the extension is available
189 if ( XShapeQueryExtension( QX11Info::display(), &tmp1, &tmp2 ) ) {
190 QBitmap mask( w, h );
191 //As the first step, get the mask from XShape.
193 XRectangle* rects = XShapeGetRectangles( QX11Info::display(), child,
194 ShapeBounding, &count, &order );
195 //The ShapeBounding region is the outermost shape of the window;
196 //ShapeBounding - ShapeClipping is defined to be the border.
197 //Since the border area is part of the window, we use bounding
198 // to limit our work region
200 //Create a QRegion from the rectangles describing the bounding mask.
202 for ( int pos = 0; pos < count; pos++ )
203 contents += QRegion( rects[pos].x, rects[pos].y,
204 rects[pos].width, rects[pos].height );
207 //Create the bounding box.
208 QRegion bbox( 0, 0, w, h );
211 contents.translate( border, border );
212 contents += QRegion( 0, 0, border, h );
213 contents += QRegion( 0, 0, w, border );
214 contents += QRegion( 0, h - border, w, border );
215 contents += QRegion( w - border, 0, border, h );
218 //Get the masked away area.
219 QRegion maskedAway = bbox - contents;
220 QVector<QRect> maskedAwayRects = maskedAway.rects();
222 //Construct a bitmap mask from the rectangles
224 p.fillRect(0, 0, w, h, Qt::color1);
225 for (int pos = 0; pos < maskedAwayRects.count(); pos++)
226 p.fillRect(maskedAwayRects[pos], Qt::color0);
232 #endif // HAVE_X11_EXTENSIONS_SHAPE_H
238 QString WindowGrabber::title;
239 QString WindowGrabber::windowClass;
240 QPoint WindowGrabber::windowPosition;
242 WindowGrabber::WindowGrabber()
243 : QDialog( 0, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint ),
244 current( -1 ), yPos( -1 )
246 setWindowModality( Qt::WindowModal );
253 XGrabServer( QX11Info::display() );
254 Window child = windowUnderCursor();
255 XGetGeometry( QX11Info::display(), child, &root, &x, &y, &w, &h, &border, &depth );
256 XUngrabServer( QX11Info::display() );
258 QPixmap pm( grabWindow( child, x, y, w, h, border, &title, &windowClass ) );
261 getWindowsRecursive( &windows, child );
263 QPalette p = palette();
264 p.setBrush( backgroundRole(), QBrush( pm ) );
266 setFixedSize( pm.size() );
267 setMouseTracking( true );
268 setGeometry( x, y, w, h );
269 current = windowIndex( mapFromGlobal(QCursor::pos()) );
272 WindowGrabber::~WindowGrabber()
276 QPixmap WindowGrabber::grabCurrent( bool includeDecorations )
281 uint w, h, border, depth;
283 XGrabServer( QX11Info::display() );
284 Window child = windowUnderCursor( includeDecorations );
285 XGetGeometry( QX11Info::display(), child, &root, &x, &y, &w, &h, &border, &depth );
289 unsigned int nchildren;
291 if( XQueryTree( QX11Info::display(), child, &root, &parent,
292 &children, &nchildren ) != 0 ) {
293 if( children != NULL ) {
300 if( XTranslateCoordinates( QX11Info::display(), parent, QX11Info::appRootWindow(),
301 x, y, &newx, &newy, &dummy )) {
307 windowPosition = QPoint(x,y);
308 QPixmap pm( grabWindow( child, x, y, w, h, border, &title, &windowClass ) );
309 XUngrabServer( QX11Info::display() );
316 void WindowGrabber::mousePressEvent( QMouseEvent *e )
318 if ( e->button() == Qt::RightButton ) {
321 if ( current != -1 ) {
322 windowPosition = e->globalPos() - e->pos() + windows[current].topLeft();
323 emit windowGrabbed( palette().brush( backgroundRole() ).texture().copy( windows[ current ] ) );
325 windowPosition = QPoint(0,0);
326 emit windowGrabbed( QPixmap() );
332 void WindowGrabber::mouseReleaseEvent( QMouseEvent *e )
334 if ( e->button() == Qt::RightButton ) {
340 const int minDistance = 10;
342 void WindowGrabber::mouseMoveEvent( QMouseEvent *e )
345 int w = windowIndex( e->pos() );
346 if ( w != -1 && w != current ) {
351 int y = e->globalY();
352 if ( y > yPos + minDistance ) {
353 decreaseScope( e->pos() );
355 } else if ( y < yPos - minDistance ) {
356 increaseScope( e->pos() );
362 void WindowGrabber::wheelEvent( QWheelEvent *e )
364 if ( e->delta() > 0 ) {
365 increaseScope( e->pos() );
366 } else if ( e->delta() < 0 ) {
367 decreaseScope( e->pos() );
373 // Increases the scope to the next-bigger window containing the mouse pointer.
374 // This method is activated by either rotating the mouse wheel forwards or by
375 // dragging the mouse forwards while keeping the right mouse button pressed.
376 void WindowGrabber::increaseScope( const QPoint &pos )
378 for ( uint i = current + 1; i < windows.size(); i++ ) {
379 if ( windows[ i ].contains( pos ) ) {
387 // Decreases the scope to the next-smaller window containing the mouse pointer.
388 // This method is activated by either rotating the mouse wheel backwards or by
389 // dragging the mouse backwards while keeping the right mouse button pressed.
390 void WindowGrabber::decreaseScope( const QPoint &pos )
392 for ( int i = current - 1; i >= 0; i-- ) {
393 if ( windows[ i ].contains( pos ) ) {
401 // Searches and returns the index of the first (=smallest) window
402 // containing the mouse pointer.
403 int WindowGrabber::windowIndex( const QPoint &pos ) const
405 for ( uint i = 0; i < windows.size(); i++ ) {
406 if ( windows[ i ].contains( pos ) ) {
413 // Draws a border around the (child) window currently containing the pointer
414 void WindowGrabber::paintEvent( QPaintEvent * )
416 if ( current >= 0 ) {
419 p.fillRect(rect(), palette().brush( backgroundRole()));
420 p.setPen( QPen( Qt::red, 3 ) );
421 p.drawRect( windows[ current ].adjusted( 0, 0, -1, -1 ) );
426 #include "moc_windowgrabber.cpp"