this is KDE 4.3.0 here now. Fix ABI and API
[kdegraphics.git] / ksnapshot / windowgrabber.cpp
blobe7d19560b2e7c39c045199a0911e7974dc437028
1 /*
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"
22 #include <algorithm>
24 #include <kwindowinfo.h>
25 #include <kdebug.h>
27 #include <QBitmap>
28 #include <QPainter>
29 #include <QPixmap>
30 #include <QMouseEvent>
31 #include <QWheelEvent>
33 #include <X11/Xlib.h>
34 #include <config-ksnapshot.h>
35 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
36 #include <X11/extensions/shape.h>
37 #endif
38 #include <QX11Info>
40 static
41 const int minSize = 8;
43 static
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.
52 static
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 ) {
60 int x = 0, y = 0;
61 if ( depth ) {
62 x = atts.x + rx;
63 y = atts.y + ry;
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 );
71 Window root, parent;
72 Window* children;
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 )
80 XFree( children );
83 if ( depth == 0 )
84 std::sort( windows.begin(), windows.end() );
87 static
88 Window findRealWindow( Window w, int depth = 0 )
90 if( depth > 5 )
91 return None;
92 static Atom wm_state = XInternAtom( QX11Info::display(), "WM_STATE", False );
93 Atom type;
94 int format;
95 unsigned long nitems, after;
96 unsigned char* prop;
97 if( XGetWindowProperty( QX11Info::display(), w, wm_state, 0, 0, False, AnyPropertyType,
98 &type, &format, &nitems, &after, &prop ) == Success ) {
99 if( prop != NULL )
100 XFree( prop );
101 if( type != None )
102 return w;
104 Window root, parent;
105 Window* children;
106 unsigned int nchildren;
107 Window ret = None;
108 if( XQueryTree( QX11Info::display(), w, &root, &parent, &children, &nchildren ) != 0 ) {
109 for( unsigned int i = 0;
110 i < nchildren && ret == None;
111 ++i )
112 ret = findRealWindow( children[ i ], depth + 1 );
113 if( children != NULL )
114 XFree( children );
116 return ret;
119 static
120 Window windowUnderCursor( bool includeDecorations = true )
122 Window root;
123 Window child;
124 uint mask;
125 int rootX, rootY, winX, winY;
126 XGrabServer( QX11Info::display() );
127 XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &root, &child,
128 &rootX, &rootY, &winX, &winY, &mask );
129 if( child == None )
130 child = QX11Info::appRootWindow();
131 if( !includeDecorations ) {
132 Window real_child = findRealWindow( child );
133 if( real_child != None ) // test just in case
134 child = real_child;
136 return child;
139 static
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 );
146 if ( title )
147 (*title) = winInfo.visibleName();
148 if ( windowClass )
149 (*windowClass) = winInfo.windowClassName();
151 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
152 int tmp1, tmp2;
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.
157 int count, order;
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
164 if (rects) {
165 //Create a QRegion from the rectangles describing the bounding mask.
166 QRegion contents;
167 for ( int pos = 0; pos < count; pos++ )
168 contents += QRegion( rects[pos].x, rects[pos].y,
169 rects[pos].width, rects[pos].height );
170 XFree( rects );
172 //Create the bounding box.
173 QRegion bbox( 0, 0, w, h );
175 if( border > 0 ) {
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
188 QPainter p(&mask);
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);
192 p.end();
194 pm.setMask(mask);
197 #endif
199 return pm;
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 );
210 Window root;
211 int y, x;
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 ) );
222 setPalette( p );
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 )
235 Window root;
236 int y, x;
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 );
241 Window parent;
242 Window* children;
243 unsigned int nchildren;
244 if( XQueryTree( QX11Info::display(), child, &root, &parent,
245 &children, &nchildren ) != 0 ) {
246 if( children != NULL )
247 XFree( children );
248 int newx, newy;
249 Window dummy;
250 if( XTranslateCoordinates( QX11Info::display(), parent, QX11Info::appRootWindow(),
251 x, y, &newx, &newy, &dummy )) {
252 x = newx;
253 y = newy;
256 QPixmap pm( grabWindow( child, x, y, w, h, border, &title, &windowClass ) );
257 XUngrabServer( QX11Info::display() );
258 return pm;
261 void WindowGrabber::mousePressEvent( QMouseEvent *e )
263 if ( e->button() == Qt::RightButton )
264 yPos = e->globalY();
265 else {
266 if ( current )
267 emit windowGrabbed( palette().brush( backgroundRole() ).texture().copy( windows[ current ] ) );
268 else
269 emit windowGrabbed( QPixmap() );
270 accept();
274 void WindowGrabber::mouseReleaseEvent( QMouseEvent *e )
276 if ( e->button() == Qt::RightButton )
277 yPos = -1;
280 static
281 const int minDistance = 10;
283 void WindowGrabber::mouseMoveEvent( QMouseEvent *e )
285 if ( yPos == -1 ) {
286 int w = windowIndex( e->pos() );
287 if ( w != -1 && w != current ) {
288 current = w;
289 repaint();
292 else {
293 int y = e->globalY();
294 if ( y > yPos + minDistance ) {
295 decreaseScope( e->pos() );
296 yPos = y;
298 else if ( y < yPos - minDistance ) {
299 increaseScope( e->pos() );
300 yPos = y;
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() );
311 else
312 e->ignore();
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 ) ) {
322 current = i;
323 break;
326 repaint();
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 ) ) {
336 current = i;
337 break;
340 repaint();
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 ) )
349 return i;
351 return -1;
354 // Draws a border around the (child) window currently containing the pointer
355 void WindowGrabber::paintEvent( QPaintEvent * )
357 if ( current >= 0 ) {
358 QPainter p;
359 p.begin( this );
360 p.fillRect(rect(), palette().brush( backgroundRole()));
361 p.setPen( QPen( Qt::red, 3 ) );
362 p.drawRect( windows[ current ].adjusted( 0, 0, -1, -1 ) );
363 p.end();
367 #include "windowgrabber.moc"