1 /****************************************************************************
2 *                                                                           *
3 *   X11tools                                                                *
4 *   Copyright (C) 2008-2014  Piotr Dąbrowski ultr@ultr.pl                   *
5 *   Copyright (C) 2011       Przemysław Rudy prudy1@o2.pl                   *
6 *                                                                           *
7 *   This program 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 2 of the License, or       *
10 *   (at your option) any later version.                                     *
11 *                                                                           *
12 *   This program 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.                            *
16 *                                                                           *
17 *   You should have received a copy of the GNU General Public License       *
18 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
19 *                                                                           *
20 ****************************************************************************/
21 
22 
23 
24 
25 #include <list>
26 #include <unistd.h>
27 
28 #include <X11/Xatom.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xmd.h>
31 #include <X11/Xresource.h>
32 #include <X11/Xutil.h>
33 
34 #include "x11tools.h"
35 
36 
37 
38 
X11_getCardinalProperty(Display * display,Window window,const char * propertyName,uint32_t * value,long offset)39 bool X11_getCardinalProperty( Display *display, Window window, const char *propertyName, uint32_t *value, long offset )
40 {
41 	Atom property = XInternAtom( display, propertyName, False );
42 	if( property == None )
43 		return false;
44 	uint32_t *data = NULL; Atom realtype; int realformat; unsigned long nitems, left;
45 	int result = XGetWindowProperty( display, window, property, offset, 1L, False, XA_CARDINAL, &realtype, &realformat, &nitems, &left, (unsigned char**)&data );
46 	if( result == Success )
47 	{
48 		if( realtype == XA_CARDINAL )
49 		{
50 			if( nitems > 0 )
51 			{
52 				*value = data[0];
53 				XFree( data );
54 				return true;
55 			}
56 		}
57 		XFree( data );
58 	}
59 	return false;
60 }
61 
62 
X11_getFirstPropertyAtom(Display * display,Window window,const char * propertyName,Atom * value)63 bool X11_getFirstPropertyAtom( Display *display, Window window, const char *propertyName, Atom *value )
64 {
65 	Atom property = XInternAtom( display, propertyName, False );
66 	if( property == None )
67 		return false;
68 	Atom *data = NULL; Atom realtype; int realformat; unsigned long nitems, left;
69 	int result = XGetWindowProperty( display, window, property, 0L, 1L, False, XA_ATOM, &realtype, &realformat, &nitems, &left, (unsigned char**)&data );
70 	if( result == Success )
71 	{
72 		if( realtype == XA_ATOM )
73 		{
74 			if( nitems > 0 )
75 			{
76 				*value = data[0L];
77 				XFree( data );
78 				return true;
79 			}
80 		}
81 		XFree( data );
82 	}
83 	return false;
84 }
85 
86 
X11_isPropertyAtomSet(Display * display,Window window,const char * propertyName,const char * atomName)87 bool X11_isPropertyAtomSet( Display *display, Window window, const char *propertyName, const char *atomName )
88 {
89 	Atom property = XInternAtom( display, propertyName, False );
90 	if( property == None )
91 		return false;
92 	Atom atom = XInternAtom( display, atomName, False );
93 	if( atom == None )
94 		return false;
95 	Atom *atoms = NULL; Atom realtype; int realformat; unsigned long nitems, left;
96 	int result = XGetWindowProperty( display, window, property, 0L, 8192L, False, XA_ATOM, &realtype, &realformat, &nitems, &left, (unsigned char**)&atoms );
97 	if( result != Success )
98 		return false;
99 	if( realtype != XA_ATOM )
100 		return false;
101 	for( unsigned long k = 0; k < nitems; k++ )
102 	{
103 		if( atoms[k] == atom )
104 		{
105 			XFree( atoms );
106 			return true;
107 		}
108 	}
109 	XFree( atoms );
110 	return false;
111 }
112 
113 
114 
115 
X11_getResolution(Display * display)116 std::pair<int,int> X11_getResolution( Display *display )
117 {
118 	return X11_getWindowSize( display, DefaultRootWindow( display ) );
119 	// neither XDisplayWidth/XDisplayHeight nor _NET_WORKAREA work for Gnome :|
120 	// they do not get updated after resolution change
121 }
122 
123 
X11_getDesktopSize(Display * display)124 std::pair<int,int> X11_getDesktopSize( Display *display )
125 {
126 	uint32_t width;
127 	if( ! X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_DESKTOP_GEOMETRY", &width, 0 ) )
128 		return std::make_pair( 0, 0 );
129 	uint32_t height;
130 	if( ! X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_DESKTOP_GEOMETRY", &height, 1 ) )
131 		return std::make_pair( 0, 0 );
132 	return std::make_pair( width, height );
133 }
134 
135 
136 
137 
X11_getMousePos(Display * display)138 std::pair<int,int> X11_getMousePos( Display *display )
139 {
140 	int x = 0;
141 	int y = 0;
142 	Window root;
143 	Window child;
144 	int winx, winy;
145 	unsigned int mask;
146 	XQueryPointer( display, DefaultRootWindow( display ), &root, &child, &x, &y, &winx, &winy, &mask );
147 	return std::make_pair( x, y );
148 }
149 
150 
151 
152 
X11_isPointerGrabbed(Display * display)153 bool X11_isPointerGrabbed( Display *display )
154 {
155 	int status = XGrabPointer(
156 		display,
157 		DefaultRootWindow( display ),
158 		True,
159 		ButtonReleaseMask | ButtonMotionMask | ButtonPressMask,
160 		GrabModeAsync,
161 		GrabModeAsync,
162 		None,
163 		None,
164 		0xFFFFFFFFL
165 	);
166 	if( status == AlreadyGrabbed )
167 		return true;
168 	if( status == GrabInvalidTime )
169 	{
170 		_x11toolsdebug( "[TIME]" );
171 	}
172 	if( status == GrabSuccess )
173 	{
174 		_x11toolsdebug( "[GRAB]" );
175 		XUngrabPointer( display, CurrentTime );
176 		XFlush( display );
177 	}
178 	return false;
179 }
180 
181 
182 
183 
X11_isFreeDesktopCompatible(Display * display)184 bool X11_isFreeDesktopCompatible( Display *display )
185 {
186 	if( X11_getDesktopsCount( display, true ) != 1 ) // _NET multiple desktops, FreeDesktop compatible
187 		return true;
188 	std::pair<int,int> resolution  = X11_getResolution(  display );
189 	std::pair<int,int> desktopsize = X11_getDesktopSize( display );
190 	if( resolution == desktopsize ) // one desktop only, so we don't have to care
191 		return true;
192 	if (resolution.first == 0)
193 		return false;
194 	if( ( desktopsize.first % resolution.first != 0 ) || ( desktopsize.second % resolution.second != 0 ) ) // virtual resolution
195 		return true;
196 	// not FreeDesktop compatible :(
197 	return false;
198 }
199 
200 
201 
202 
X11_getDesktopsCount(Display * display,bool forceFreeDesktop)203 uint32_t X11_getDesktopsCount( Display *display, bool forceFreeDesktop )
204 {
205 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
206 	{
207 		std::pair<int,int> resolution  = X11_getResolution(  display );
208 		std::pair<int,int> desktopsize = X11_getDesktopSize( display );
209 		return static_cast<uint32_t>(( desktopsize.second / resolution.second ) * ( desktopsize.first / resolution.first ));
210 	}
211 	else
212 	{
213 		uint32_t value;
214 		if( ! X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_NUMBER_OF_DESKTOPS", &value ) )
215 			return 1; // 0 desktops doesn't make sense, even Compiz returns 1 ()
216 		return value;
217 	}
218 }
219 
220 
X11_getCurrentDesktop(Display * display,bool forceFreeDesktop)221 uint32_t X11_getCurrentDesktop( Display *display, bool forceFreeDesktop )
222 {
223 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
224 	{
225 		uint32_t dx = 0, dy = 0;
226 		X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_DESKTOP_VIEWPORT", &dx, 0 );
227 		X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_DESKTOP_VIEWPORT", &dy, 1 );
228 		std::pair<int,int> desktopsize = X11_getDesktopSize( display );
229 		std::pair<int,int> resolution = X11_getResolution( display );
230 		if (resolution.second == 0)
231 			return 0;
232 		uint32_t desktop = static_cast<uint32_t>(( static_cast<int>(dy) / resolution.second ) * ( desktopsize.first / resolution.first ) + ( static_cast<int>(dx) / resolution.first ));
233 		return desktop;
234 	}
235 	else
236 	{
237 		uint32_t desktop;
238 		if( ! X11_getCardinalProperty( display, DefaultRootWindow( display ), "_NET_CURRENT_DESKTOP", &desktop ) )
239 			return X11_NODESKTOP;
240 		return desktop;
241 	}
242 }
243 
244 
X11_setCurrentDesktop(Display * display,uint32_t desktop,bool forceFreeDesktop)245 void X11_setCurrentDesktop( Display *display, uint32_t desktop, bool forceFreeDesktop )
246 {
247 	if( ( desktop != X11_ALLDESKTOPS ) && ( desktop != X11_NODESKTOP ) && ( desktop != X11_getCurrentDesktop( display, forceFreeDesktop ) ) )
248 	{
249 		// generate MouseLeave event
250 		int rootx, rooty, windowx, windowy;
251 		Window window = X11_getWindowUnderCursor( display, &rootx, &rooty, &windowx, &windowy );
252 		if( window != None )
253 		{
254 			XEvent xev;
255 			xev.type                  = LeaveNotify;
256 			xev.xcrossing.type        = LeaveNotify;
257 			xev.xcrossing.serial      = 0;
258 			xev.xcrossing.send_event  = False;
259 			xev.xcrossing.display     = display;
260 			xev.xcrossing.window      = window;
261 			xev.xcrossing.root        = DefaultRootWindow( display );
262 			xev.xcrossing.subwindow   = None;
263 			xev.xcrossing.time        = CurrentTime;
264 			xev.xcrossing.x           = windowx;
265 			xev.xcrossing.y           = windowy;
266 			xev.xcrossing.x_root      = rootx;
267 			xev.xcrossing.y_root      = rooty;
268 			xev.xcrossing.mode        = NotifyNormal;
269 			xev.xcrossing.detail      = NotifyNonlinear;
270 			xev.xcrossing.same_screen = True;
271 			xev.xcrossing.focus       = True;
272 			xev.xcrossing.state       = 0;
273 			XSendEvent( display, window, True, LeaveWindowMask, &xev );
274 			XFlush( display );
275 		}
276 		// change desktop
277 		if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
278 		{
279 			std::pair<int,int> desktopsize = X11_getDesktopSize( display );
280 			std::pair<int,int> resolution = X11_getResolution( display );
281 			uint32_t dx = ( desktop % ( static_cast<uint32_t>(desktopsize.first / resolution.first) ) ) * static_cast<uint32_t>(resolution.first);
282 			uint32_t dy = ( desktop / ( static_cast<uint32_t>(desktopsize.first / resolution.first) ) ) * static_cast<uint32_t>(resolution.second);
283 			XEvent xev;
284 			xev.type                 = ClientMessage;
285 			xev.xclient.type         = ClientMessage;
286 			xev.xclient.serial       = 0;
287 			xev.xclient.send_event   = True;
288 			xev.xclient.display      = display;
289 			xev.xclient.window       = DefaultRootWindow( display );
290 			xev.xclient.message_type = XInternAtom( display, "_NET_DESKTOP_VIEWPORT", False );
291 			xev.xclient.format       = 32;
292 			xev.xclient.data.l[0]    = dx;
293 			xev.xclient.data.l[1]    = dy;
294 			xev.xclient.data.l[2]    = 0;
295 			xev.xclient.data.l[3]    = 0;
296 			xev.xclient.data.l[4]    = 0;
297 			XSendEvent( display, DefaultRootWindow( display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
298 			XFlush( display );
299 		}
300 		else
301 		{
302 			XEvent xev;
303 			xev.type                 = ClientMessage;
304 			xev.xclient.type         = ClientMessage;
305 			xev.xclient.serial       = 0;
306 			xev.xclient.send_event   = True;
307 			xev.xclient.display      = display;
308 			xev.xclient.window       = DefaultRootWindow( display );
309 			xev.xclient.message_type = XInternAtom( display, "_NET_CURRENT_DESKTOP", False );
310 			xev.xclient.format       = 32;
311 			xev.xclient.data.l[0]    = desktop;
312 			xev.xclient.data.l[1]    = CurrentTime;
313 			xev.xclient.data.l[2]    = 0;
314 			xev.xclient.data.l[3]    = 0;
315 			xev.xclient.data.l[4]    = 0;
316 			XSendEvent( display, DefaultRootWindow( display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
317 		}
318 	}
319 }
320 
321 
X11_getDesktopOfWindow(Display * display,Window window,bool forceFreeDesktop,bool windowareadecides)322 uint32_t X11_getDesktopOfWindow( Display *display, Window window, bool forceFreeDesktop, bool windowareadecides )
323 {
324 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
325 	{
326 		uint32_t currentdesktop = X11_getCurrentDesktop( display, forceFreeDesktop );
327 		std::pair<int,int> pos = X11_getWindowPos( display, window );
328 		std::pair<int,int> desktopsize = X11_getDesktopSize( display );
329 		std::pair<int,int> resolution = X11_getResolution( display );
330 		if( windowareadecides )
331 		{
332 			std::pair<int,int> size = X11_getWindowSize( display, window );
333 			pos.first  += size.first  / 2;
334 			pos.second += size.second / 2;
335 			pos.second %= desktopsize.second;
336 		}
337 		uint32_t desktopofwindow = currentdesktop
338 			+ static_cast<uint32_t>(( pos.second / resolution.second ) * ( desktopsize.first / resolution.first ) + ( pos.first / resolution.first ));
339 		if( pos.first < 0 )
340 			desktopofwindow -= 1;
341 		if( pos.second < 0 )
342 			desktopofwindow -= static_cast<uint32_t>( desktopsize.first / resolution.first );
343 		desktopofwindow %= X11_getDesktopsCount( display, forceFreeDesktop );
344 		return desktopofwindow;
345 	}
346 	else
347 	{
348 		uint32_t desktopofwindow;
349 		if( ! X11_getCardinalProperty( display, window, "_NET_WM_DESKTOP", &desktopofwindow ) )
350 			return X11_NODESKTOP;
351 		return desktopofwindow;
352 	}
353 }
354 
355 
X11_moveWindowToDesktop(Display * display,Window window,uint32_t desktop,bool forceFreeDesktop,bool position,int x,int y)356 void X11_moveWindowToDesktop( Display *display, Window window, uint32_t desktop, bool forceFreeDesktop, bool position, int x, int y )
357 {
358 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
359 	{
360 		std::pair<int,int> pos = X11_getWindowPos( display, window );
361 		std::pair<int,int> desktopsize = X11_getDesktopSize( display );
362 		std::pair<int,int> resolution = X11_getResolution( display );
363 		uint32_t desktopofwindow = X11_getDesktopOfWindow( display, window, forceFreeDesktop );
364 		uint32_t ddx = ( desktop % static_cast<uint32_t>( desktopsize.first / resolution.first ) ) - ( desktopofwindow % static_cast<uint32_t>( desktopsize.first / resolution.first ) );
365 		uint32_t ddy = ( desktop / static_cast<uint32_t>( desktopsize.first / resolution.first ) ) - ( desktopofwindow / static_cast<uint32_t>( desktopsize.first / resolution.first ) );
366 		int newx, newy;
367 		if( position )
368 		{
369 			int oldx = pos.first % resolution.first;
370 			if( oldx < 0 )
371 				oldx += resolution.first;
372 			int oldy = pos.second % resolution.second;
373 			if( oldy < 0 )
374 				oldy += resolution.second;
375 			newx = ( pos.first  - oldx + x ) + static_cast<int>(ddx) * resolution.first;
376 			newy = ( pos.second - oldy + y ) + static_cast<int>(ddy) * resolution.second;
377 		}
378 		else
379 		{
380 			newx = pos.first  + static_cast<int>(ddx) * resolution.first;
381 			newy = pos.second + static_cast<int>(ddy) * resolution.second;
382 		}
383 		X11_moveWindow( display, window, newx, newy );
384 	}
385 	else
386 	{
387 		XEvent xev;
388 		xev.type                 = ClientMessage;
389 		xev.xclient.type         = ClientMessage;
390 		xev.xclient.display      = display;
391 		xev.xclient.window       = window;
392 		xev.xclient.message_type = XInternAtom( display, "_NET_WM_DESKTOP", False);
393 		xev.xclient.format       = 32;
394 		xev.xclient.data.l[0]    = desktop;
395 		xev.xclient.data.l[1]    = 2;  /* indicate we are messaging from a pager */
396 		XSendEvent( display, DefaultRootWindow( display ), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev );
397 		XFlush( display );
398 		if( position )
399 			X11_moveWindow( display, window, x, y );
400 	}
401 }
402 
403 
X11_isWindowOnDesktop(Display * display,Window window,uint32_t desktop,bool forceFreeDesktop)404 bool X11_isWindowOnDesktop( Display *display, Window window, uint32_t desktop, bool forceFreeDesktop )
405 {
406 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
407 	{
408 		uint32_t desktopofwindow = X11_getDesktopOfWindow( display, window, forceFreeDesktop );
409 		return ( desktopofwindow == desktop );
410 	}
411 	else
412 	{
413 		uint32_t desktopofwindow = X11_getDesktopOfWindow( display, window, forceFreeDesktop );
414 		if( desktopofwindow == X11_ALLDESKTOPS )
415 			return true;
416 		return ( desktopofwindow == desktop );
417 	}
418 }
419 
420 
X11_isWholeWindowOnOneDesktop(Display * display,Window window)421 bool X11_isWholeWindowOnOneDesktop( Display *display, Window window )
422 {
423 	std::pair<int,int> pos = X11_getWindowPos( display, window );
424 	std::pair<int,int> size = X11_getWindowSize( display, window );
425 	std::pair<int,int> resolution = X11_getResolution( display );
426 	if( ( pos.first < 0 ) && ( pos.first + size.first > 0 ) )
427 		return false;
428 	if( ( pos.first > 0 ) && ( pos.first + size.first < 0 ) )
429 		return false;
430 	if( ( pos.second < 0 ) && ( pos.second + size.second > 0 ) )
431 		return false;
432 	if( ( pos.second > 0 ) && ( pos.second + size.second < 0 ) )
433 		return false;
434 	if( ( pos.first / resolution.first ) != ( ( pos.first + size.first - 1 ) / resolution.first ) )
435 		return false;
436 	if( ( pos.second / resolution.second ) != ( ( pos.second + size.second - 1 ) / resolution.second ) )
437 		return false;
438 	return true;
439 }
440 
441 
X11_isWindowCovered(Display * display,Window window)442 bool X11_isWindowCovered( Display *display, Window window )
443 {
444 	bool notypes = true;
445 	Window parent = window;
446 	Window root;
447 	Window *children = NULL;
448 	unsigned int nchildren;
449 	while( ( parent != None ) && ( parent != DefaultRootWindow( display ) ) )
450 	{
451 		window = parent;
452 		XQueryTree( display, window, &root, &parent, &children, &nchildren );
453 		XFree( children );
454 		Atom type = None;
455 		if( X11_getFirstPropertyAtom( display, window, "_NET_WM_WINDOW_TYPE", &type ) )
456 			notypes = false;
457 	}
458 	int x, y;
459 	unsigned int width, height, border, depth;
460 	XGetGeometry( display, window, &root, &x, &y, &width, &height, &border, &depth );
461 	int x1 = x;
462 	int y1 = y;
463 	int x2 = x + static_cast<int>(width);
464 	int y2 = y + static_cast<int>(height);
465 	XQueryTree( display, DefaultRootWindow( display ), &root, &parent, &children, &nchildren );
466 	if( children != NULL )
467 	{
468 		unsigned int k = 0;
469 		while( k < nchildren )
470 		{
471 			if( children[k] == window )
472 				break;
473 			k++;
474 		}
475 		k++;
476 		Atom type_dock         = XInternAtom( display, "_NET_WM_WINDOW_TYPE_DOCK"         , False );
477 		Atom type_toolbar      = XInternAtom( display, "_NET_WM_WINDOW_TYPE_TOOLBAR"      , False );
478 		Atom type_menu         = XInternAtom( display, "_NET_WM_WINDOW_TYPE_MENU"         , False );
479 		Atom type_dropdownmenu = XInternAtom( display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False );
480 		Atom type_popupmenu    = XInternAtom( display, "_NET_WM_WINDOW_TYPE_POPUP_MENU"   , False );
481 		Atom type_combo        = XInternAtom( display, "_NET_WM_WINDOW_TYPE_COMBO"        , False );
482 		uint32_t currentdesktop = X11_getCurrentDesktop( display );
483 		for( ; k < nchildren ; k++ )
484 		{
485 			if( children[k] == window )
486 				continue;
487 			if( ! X11_isWindowOnDesktop( display, children[k], currentdesktop ) )
488 				if( X11_getDesktopOfWindow( display, children[k] ) != X11_NODESKTOP )
489 					continue;
490 			XWindowAttributes attr;
491 			XGetWindowAttributes( display, children[k], &attr );
492 			if( attr.map_state != IsViewable )
493 				continue;
494 			std::pair<int,int> pos2 = X11_getWindowPos( display, children[k] );
495 			std::pair<int,int> size2 = X11_getWindowSize( display, children[k] );
496 			int X1 = pos2.first;
497 			int Y1 = pos2.second;
498 			int X2 = pos2.first + size2.first;
499 			int Y2 = pos2.second + size2.second;
500 			if( ( ( x1 <= X1 && X1 <= x2 ) || ( x1 <= X2 && X2 <= x2 ) || ( x1 >= X1 && x2 <= X2 ) )
501 			&&  ( ( y1 <= Y1 && Y1 <= y2 ) || ( y1 <= Y2 && Y2 <= y2 ) || ( y1 >= Y1 && y2 <= Y2 ) )
502 			)
503 			{
504 				Atom type = None;
505 				bool hastype = false;
506 				std::list<Window> windows;
507 				windows.push_back( children[k] );
508 				Window *children2 = NULL;
509 				unsigned int nchildren2;
510 				while( true )
511 				{
512 					if( windows.empty() )
513 						break;
514 					Window w = *(windows.begin());
515 					windows.pop_front();
516 					if( X11_getFirstPropertyAtom( display, w, "_NET_WM_WINDOW_TYPE", &type ) )
517 						hastype = true;
518 					if( hastype )
519 						break;
520 					XQueryTree( display, w, &root, &parent, &children2, &nchildren2 );
521 					if( ( nchildren2 > 0 ) && ( children2 != NULL ) )
522 					{
523 						unsigned int k2 = nchildren2;
524 						while (k2 > 0)
525 							windows.push_back(children2[--k2]);
526 
527 						XFree( children2 );
528 						children2 = NULL;
529 					}
530 				}
531 				windows.clear();
532 				if(
533 					( notypes && ( ! hastype ) ) ||
534 					( hastype &&
535 						( type != type_dock ) && ( type != type_toolbar ) && ( type != type_menu ) &&
536 						( type != type_dropdownmenu ) && ( type != type_popupmenu ) && ( type != type_combo )
537 					)
538 				)
539 				{
540 					XFree( children );
541 					return true;
542 				}
543 			}
544 		}
545 		XFree( children );
546 	}
547 	return false;
548 }
549 
550 
X11_isWindowShaded(Display * display,Window window)551 bool X11_isWindowShaded( Display *display, Window window )
552 {
553 	return X11_isPropertyAtomSet( display, window, "_NET_WM_STATE", "_NET_WM_STATE_SHADED" );
554 }
555 
X11_shadeWindow(Display * display,Window window,bool shade)556 void X11_shadeWindow( Display *display, Window window, bool shade )
557 {
558 	X11_windowSendXEvent( display, window, "_NET_WM_STATE", "_NET_WM_STATE_SHADED", shade );
559 }
560 
X11_isWindowMinimized(Display * display,Window window)561 bool X11_isWindowMinimized( Display *display, Window window )
562 {
563 	if (X11_isPropertyAtomSet( display, window, "_NET_WM_STATE", "_NET_WM_STATE_HIDDEN" ))
564 	{
565 		return true;
566 	}
567 
568 	Atom r_type;
569 	int r_format;
570 	unsigned long count, after;
571 
572 	unsigned char *p = nullptr;
573 
574 	Atom property = XInternAtom(display, "WM_STATE", False);
575 	if (property == None)
576 		return false;
577 	if (!XGetWindowProperty(display, window, property, 0, 2, False, property,
578 		&r_type, &r_format, &count, &after, &p))
579 	{
580 		if (!p)
581 			return true;
582 		auto ret = (long)*p == 3;
583 		XFree(p);
584 		return ret;
585 	}
586 
587 	return false;
588 }
589 
X11_getWindowPos(Display * display,Window window)590 std::pair<int,int> X11_getWindowPos( Display *display, Window window )
591 {
592 	if( window == None )
593 		return std::make_pair( 0, 0 );
594 	Window parent = window;
595 	Window root;
596 	Window *children;
597 	unsigned int nchildren;
598 	if( window != DefaultRootWindow( display ) )
599 	{
600 		while( ( parent != 0x0 ) && ( parent != DefaultRootWindow( display ) ) )
601 		{
602 			window = parent;
603 			if( XQueryTree( display, window, &root, &parent, &children, &nchildren ) == 0 )
604 				return std::make_pair( 0, 0 );
605 			XFree( children );
606 		}
607 	}
608 	int x, y;
609 	unsigned int width, height, border, depth;
610 	if( XGetGeometry( display, window, &root, &x, &y, &width, &height, &border, &depth ) == 0 )
611 		return std::make_pair( 0, 0 );
612 	return std::make_pair( x, y );
613 }
614 
615 
X11_getWindowSize(Display * display,Window window)616 std::pair<int,int> X11_getWindowSize( Display *display, Window window )
617 {
618 	if( window == None )
619 		return std::make_pair( 0, 0 );
620 	Window parent = window;
621 	Window root;
622 	Window *children;
623 	unsigned int nchildren;
624 	if( window != DefaultRootWindow( display ) )
625 	{
626 		while( ( parent != 0x0 ) && ( parent != DefaultRootWindow( display ) ) )
627 		{
628 			window = parent;
629 			if( XQueryTree( display, window, &root, &parent, &children, &nchildren ) == 0 )
630 				return std::make_pair( 0, 0 );
631 			XFree( children );
632 		}
633 	}
634 	int x, y;
635 	unsigned int width, height, border, depth;
636 	if( XGetGeometry( display, window, &root, &x, &y, &width, &height, &border, &depth ) == 0 )
637 		return std::make_pair( 0, 0 );
638 	return std::make_pair( width + 2*border, height + 2*border );
639 }
640 
641 
X11_getWindowFramelessSize(Display * display,Window window)642 std::pair<int,int> X11_getWindowFramelessSize( Display *display, Window window )
643 {
644 	Window root;
645 	int x, y;
646 	unsigned int width, height, border, depth;
647 	if( XGetGeometry( display, window, &root, &x, &y, &width, &height, &border, &depth ) == 0 )
648 		return std::make_pair( 0, 0 );
649 	return std::make_pair( width - 2*border, height - 2*border );
650 }
651 
652 
X11_moveWindow(Display * display,Window window,int x,int y)653 void X11_moveWindow( Display *display, Window window, int x, int y )
654 {
655 	XMoveWindow( display, window, x, y );
656 	XFlush( display );
657 }
658 
659 
X11_centerWindow(Display * display,Window window,uint32_t desktop,bool forceFreeDesktop)660 void X11_centerWindow( Display *display, Window window, uint32_t desktop, bool forceFreeDesktop )
661 {
662 	if( desktop == X11_NODESKTOP )
663 		desktop = X11_getCurrentDesktop( display, forceFreeDesktop );
664 	if( ( ! forceFreeDesktop ) && ( ! X11_isFreeDesktopCompatible( display ) ) )
665 	{
666 		std::pair<int,int> resolution = X11_getResolution( display );
667 		std::pair<int,int> size = X11_getWindowSize( display, window );
668 		int cx = ( resolution.first  - size.first  ) / 2;
669 		int cy = ( resolution.second - size.second ) / 2;
670 		X11_moveWindowToDesktop( display, window, desktop, false, true, cx, cy );
671 	}
672 	else
673 	{
674 		if( X11_getDesktopOfWindow( display, window, true ) != desktop )
675 			X11_moveWindowToDesktop( display, window, desktop, true );
676 		std::pair<int,int> resolution = X11_getResolution( display );
677 		std::pair<int,int> size = X11_getWindowSize( display, window );
678 		int cx = ( resolution.first  - size.first  ) / 2;
679 		int cy = ( resolution.second - size.second ) / 2;
680 		X11_moveWindow( display, window, cx, cy );
681 	}
682 }
683 
684 
X11_resizeWindow(Display * display,Window window,unsigned int width,unsigned int height)685 void X11_resizeWindow( Display *display, Window window, unsigned int width, unsigned int height )
686 {
687 	XResizeWindow( display, window, width, height );
688 	XFlush( display );
689 }
690 
691 
X11_setSizeHintsOfWindow(Display * display,Window window,int minwidth,int minheight,int maxwidth,int maxheight)692 void X11_setSizeHintsOfWindow( Display *display, Window window, int minwidth, int minheight, int maxwidth, int maxheight )
693 {
694 	XSizeHints sizehints;
695 	long supplied_return;
696 	XGetWMNormalHints( display, window, &sizehints, &supplied_return );
697 	sizehints.min_width  = minwidth;
698 	sizehints.min_height = minheight;
699 	sizehints.max_width  = maxwidth;
700 	sizehints.max_height = maxheight;
701 	sizehints.flags |= PMinSize | PMaxSize;
702 	XSetWMNormalHints( display, window, &sizehints );
703 }
704 
705 
706 
707 
X11_getActiveWindow(Display * display)708 Window X11_getActiveWindow( Display *display )
709 {
710 	// _NET_ACTIVE_WINDOW
711 	Atom net_active_window = XInternAtom( display, "_NET_ACTIVE_WINDOW", False );
712 	if( net_active_window != None )
713 	{
714 		Window *windowptr = NULL; Atom realtype; int realformat; unsigned long nitems, left;
715 		int result = XGetWindowProperty( display, XDefaultRootWindow( display ), net_active_window, 0L, sizeof(Window), False, XA_WINDOW, &realtype, &realformat, &nitems, &left, (unsigned char**)&windowptr);
716 		if( result == Success )
717 		{
718 			if( realtype == XA_WINDOW )
719 			{
720 				if( nitems > 0 )
721 				{
722 					Window window = *windowptr;
723 					XFree( windowptr );
724 					return window;
725 				}
726 			}
727 			XFree( windowptr );
728 		}
729 	}
730 	// XGetInputFocus
731 	Window window;
732 	int revertto;
733 	XGetInputFocus( display, &window, &revertto );
734 	return window;
735 }
736 
737 
X11_setActiveWindow(Display * display,Window window)738 void X11_setActiveWindow( Display *display, Window window )
739 {
740 	// raise
741 	XRaiseWindow( display, window );
742 	// NET
743 	Atom net_active_window = XInternAtom( display, "_NET_ACTIVE_WINDOW", False );
744 	XEvent e;
745 	e.xclient.type = ClientMessage;
746 	e.xclient.message_type = net_active_window;
747 	e.xclient.display = display;
748 	e.xclient.window = window;
749 	e.xclient.format = 32;
750 	e.xclient.data.l[0] = 2l;
751 	e.xclient.data.l[1] = CurrentTime;
752 	e.xclient.data.l[2] = None;
753 	e.xclient.data.l[3] = 0l;
754 	e.xclient.data.l[4] = 0l;
755 	XSendEvent( display, DefaultRootWindow( display ), False, SubstructureRedirectMask|SubstructureNotifyMask, &e );
756 }
757 
758 
X11_setActiveWindowCheck(Display * display,Window window,bool forceFreeDesktop)759 void X11_setActiveWindowCheck( Display *display, Window window, bool forceFreeDesktop )
760 {
761 	int time = 0;
762 	while( time < X11_SETACTIVEWINDOW_TIMEOUT )
763 	{
764 		if( X11_isWindowOnDesktop( display, window, X11_getCurrentDesktop( display ), forceFreeDesktop ) )
765 			break;
766 		usleep( X11_SETACTIVEWINDOW_CHECKTIME );
767 		time += X11_SETACTIVEWINDOW_CHECKTIME;
768 	}
769 	if( ! X11_isWindowOnDesktop( display, window, X11_getCurrentDesktop( display ), forceFreeDesktop ) )
770 		return;
771 	X11_setActiveWindow( display, window );
772 }
773 
774 
X11_getTopMostWindow(Display * display)775 Window X11_getTopMostWindow( Display *display )
776 {
777 	Atom listatom = None;
778 	Atom type_return = None;
779 	int format_return;
780 	unsigned long nitems_return;
781 	unsigned long bytesafter_return;
782 	Window *windowarray = NULL;
783 	// _NET_CLIENT_LIST_STACKING
784 	listatom = XInternAtom( display, "_NET_CLIENT_LIST_STACKING", False );
785 	if( XGetWindowProperty( display, DefaultRootWindow( display ), listatom, 0L, (~0L), False, XA_WINDOW, &type_return, &format_return, &nitems_return, &bytesafter_return, (unsigned char**)&windowarray ) == Success )
786 	{
787 		Window window = None;
788 		if( (type_return == XA_WINDOW) && (format_return == 32) && (windowarray) && (nitems_return > 0) )
789 		{
790 			window = windowarray[nitems_return-1];
791 		}
792 		XFree( windowarray );
793 		if( window != None )
794 			return window;
795 	}
796 	// _NET_CLIENT_LIST
797 	listatom = XInternAtom( display, "_NET_CLIENT_LIST" , False );
798 	if( XGetWindowProperty( display, DefaultRootWindow( display ), listatom, 0L, (~0L), False, XA_WINDOW, &type_return, &format_return, &nitems_return, &bytesafter_return, (unsigned char**)&windowarray ) == Success )
799 	{
800 		Window window = None;
801 		if( (type_return == XA_WINDOW) && (format_return == 32) && (windowarray) && (nitems_return > 0) )
802 		{
803 			window = windowarray[nitems_return-1];
804 		}
805 		XFree(windowarray);
806 		if( window != None )
807 			return window;
808 	}
809 	return None;
810 }
811 
812 
X11_getLatestCreatedWindow(Display * display)813 Window X11_getLatestCreatedWindow( Display *display )
814 {
815 	Window window = None;
816 	Window parent;
817 	Window root;
818 	Window *children = NULL;
819 	unsigned int nchildren;
820 	XQueryTree( display, DefaultRootWindow( display ), &root, &parent, &children, &nchildren );
821 	if( children != NULL )
822 		window = children[nchildren-1];
823 	XFree( children );
824 	return window;
825 }
826 
827 
828 
829 
X11_getWindowUnderCursor(Display * display,int * rootx,int * rooty,int * windowx,int * windowy)830 Window X11_getWindowUnderCursor( Display *display, int *rootx, int *rooty, int *windowx, int *windowy )
831 {
832 	Window root = DefaultRootWindow( display );
833 	Window window = None;
834 	int _rootx, _rooty, _windowx, _windowy;
835 	unsigned int mask;
836 	XQueryPointer( display, root, &root, &window,
837 	               ( rootx ? rootx : &_rootx ),
838 	               ( rooty ? rooty : &_rooty ),
839 	               ( windowx ? windowx : &_windowx ),
840 	               ( windowy ? windowy : &_windowy ),
841 	               &mask
842 	             );
843 	return window;
844 }
845 
846 
X11_getInnerMostWindowUnderCursor(Display * display,int * rootx,int * rooty,int * windowx,int * windowy)847 Window X11_getInnerMostWindowUnderCursor( Display *display, int *rootx, int *rooty, int *windowx, int *windowy )
848 {
849 	Window root = DefaultRootWindow( display );
850 	Window child = root;
851 	Window window;
852 	int _rootx, _rooty, _windowx, _windowy;
853 	unsigned int mask;
854 	do
855 	{
856 		window = child;
857 		XQueryPointer( display, window, &root, &child,
858 		               ( rootx ? rootx : &_rootx ),
859 		               ( rooty ? rooty : &_rooty ),
860 		               ( windowx ? windowx : &_windowx ),
861 		               ( windowy ? windowy : &_windowy ),
862 		               &mask
863 		             );
864 	} while ( ( child != None ) && ( child != window ) );
865 	return window;
866 }
867 
868 
869 
870 
X11_getWindowClass(Display * display,Window window)871 std::string X11_getWindowClass( Display *display, Window window )
872 {
873 	XClassHint classhint;
874 	if( XGetClassHint( display, window, &classhint ) == 0 ) // 0 means error
875 		return "";
876 	std::string classstring;
877 	classstring += classhint.res_name;
878 	classstring += ' ';
879 	classstring += classhint.res_class;
880 	XFree( classhint.res_name );
881 	XFree( classhint.res_class );
882 	return classstring;
883 }
884 
885 
X11_getWindowRole(Display * display,Window window)886 std::string X11_getWindowRole( Display *display, Window window )
887 {
888 	XTextProperty textproperty;
889 	std::string s = "";
890 	Atom _XA_WM_WINDOW_ROLE = XInternAtom( display, "WM_WINDOW_ROLE", False );
891 	if( XGetTextProperty( display, window, &textproperty, _XA_WM_WINDOW_ROLE ) )
892 	{
893 		if( textproperty.encoding == XA_STRING && textproperty.format == 8 && textproperty.nitems > 0 )
894 			s += (char *)textproperty.value;
895 	}
896 	XFree( textproperty.value );
897 	return s;
898 }
899 
900 
901 
902 
X11_windowSendXEvent(Display * display,Window window,const char * type,const char * message,bool set)903 void X11_windowSendXEvent( Display *display, Window window, const char *type, const char *message, bool set )
904 {
905 	Atom atomtype    = XInternAtom( display, type   , False );
906 	Atom atommessage = XInternAtom( display, message, False );
907 	XEvent xev;
908 	xev.type                 = ClientMessage;
909 	xev.xclient.type         = ClientMessage;
910 	xev.xclient.serial       = 0;
911 	xev.xclient.send_event   = True;
912 	xev.xclient.window       = window;
913 	xev.xclient.message_type = atomtype;
914 	xev.xclient.format       = 32;
915 	xev.xclient.data.l[0]    = ( set ? 1 : 0 );
916 	xev.xclient.data.l[1]    = static_cast<long>(atommessage);
917 	xev.xclient.data.l[2]    = 0;
918 	xev.xclient.data.l[3]    = 0;
919 	xev.xclient.data.l[4]    = 0;
920 	XSendEvent( display, DefaultRootWindow( display ), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
921 	XFlush( display );
922 }
923 
924 
X11_windowChangeProperty(Display * display,Window window,const char * property,const char * value)925 void X11_windowChangeProperty( Display *display, Window window, const char *property, const char *value )
926 {
927 	Atom atomproperty = XInternAtom( display, property, False );
928 	Atom atomvalue    = XInternAtom( display, value   , False );
929 	XChangeProperty( display, window, atomproperty, XA_ATOM, 32, PropModeReplace, (unsigned char *)&atomvalue, 1 );
930 }
931 
932 
X11_windowSetDecoration(Display * display,Window window,bool set)933 void X11_windowSetDecoration( Display *display, Window window, bool set )
934 {
935 	MotifWMHints hints;
936 	Atom atom = XInternAtom( display, "_MOTIF_WM_HINTS", False );
937 	if( atom != None)
938 	{
939 		hints.flags = MWM_HINTS_DECORATIONS;
940 		hints.decorations = ( set ? 1 : 0 );
941 		XChangeProperty( display, window, atom, atom, 32, PropModeReplace, (unsigned char *)&hints, 5 );
942 	}
943 }
944 
945 
946 
947 
X11_checkFullScreen(Display * display)948 bool X11_checkFullScreen( Display *display )
949 {
950 	_x11toolsdebug( "[A]" );
951 	Window wa = X11_getActiveWindow( display );
952 	if( wa != None )
953 		if( X11_isPropertyAtomSet( display, wa, "_NET_WM_STATE", "_NET_WM_STATE_FULLSCREEN" ) )
954 			return true;
955 	_x11toolsdebug( "[B]" )
956 	Window wt = X11_getTopMostWindow( display );
957 	if( wt != None )
958 		if( X11_isPropertyAtomSet( display, wt, "_NET_WM_STATE", "_NET_WM_STATE_FULLSCREEN" ) )
959 			return true;
960 	_x11toolsdebug( "[C]" );
961 	std::pair<int,int> resolution = X11_getResolution( display );
962 	_x11toolsdebug( "[RES=%dx%d]", resolution.first, resolution.second );
963 	_x11toolsdebug( "[wa=%dx%d]", X11_getWindowSize( display, wa ).first, X11_getWindowSize( display, wa ).second );
964 	_x11toolsdebug( "[wt=%dx%d]", X11_getWindowSize( display, wt ).first, X11_getWindowSize( display, wt ).second );
965 	uint32_t currentdesktop = X11_getCurrentDesktop( display );
966 	_x11toolsdebug( "[cD=%d]", currentdesktop );
967 	if( X11_isPointerGrabbed( display ) )
968 	{
969 		_x11toolsdebug( "[D]" );
970 		if( wt != None )
971 			if( X11_getWindowSize( display, wt ) == resolution )
972 			{
973 				_x11toolsdebug( "[D2]" );
974 				if(
975 						X11_isPropertyAtomSet( display, wt, "_NET_WM_STATE", "_NET_WM_STATE_MAXIMIZED_HORZ" ) &&
976 						X11_isPropertyAtomSet( display, wt, "_NET_WM_STATE", "_NET_WM_STATE_MAXIMIZED_VERT" ) &&
977 						X11_isWindowOnDesktop( display, wt, currentdesktop )
978 					)
979 					return false;
980 				_x11toolsdebug( "[D3]" );
981 				_x11toolsdebug( "[wtD=%d]", X11_getDesktopOfWindow( display, wt ) );
982 				if( X11_isWindowOnDesktop( display, wt, currentdesktop ) )
983 					return true;
984 				_x11toolsdebug( "[D4]" );
985 			}
986 		_x11toolsdebug( "[E]" );
987 		Window wl = X11_getLatestCreatedWindow( display );
988 		_x11toolsdebug( "[wl=%dx%d]", X11_getWindowSize( display, wl ).first, X11_getWindowSize( display, wl ).second );
989 		if( wl != None )
990 			if( X11_getWindowSize( display, wl ) == resolution )
991 			{
992 				_x11toolsdebug( "[E2]" );
993 				_x11toolsdebug( "[wlD=%d]", X11_getDesktopOfWindow( display, wl ) );
994 				if(
995 						X11_isPropertyAtomSet( display, wl, "_NET_WM_STATE", "_NET_WM_STATE_MAXIMIZED_HORZ" ) &&
996 						X11_isPropertyAtomSet( display, wl, "_NET_WM_STATE", "_NET_WM_STATE_MAXIMIZED_VERT" ) &&
997 						X11_isWindowOnDesktop( display, wl, currentdesktop )
998 					)
999 					return false;
1000 				_x11toolsdebug( "[E3]" );
1001 				return true;
1002 			}
1003 		_x11toolsdebug( "[F]" );
1004 		if( wl != None )
1005 		{
1006 			_x11toolsdebug( "[F2]" );
1007 			Atom wl_type = None;
1008 			if( X11_getFirstPropertyAtom( display, wl, "_NET_WM_WINDOW_TYPE", &wl_type ) && ( wl_type != None ) )
1009 			{
1010 				_x11toolsdebug( "[F3]" );
1011 				_x11toolsdebug( "[wlT=%d]", wl_type );
1012 				Atom type_toolbar      = XInternAtom( display, "_NET_WM_WINDOW_TYPE_TOOLBAR"      , False );
1013 				Atom type_menu         = XInternAtom( display, "_NET_WM_WINDOW_TYPE_MENU"         , False );
1014 				Atom type_dropdownmenu = XInternAtom( display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False );
1015 				Atom type_popupmenu    = XInternAtom( display, "_NET_WM_WINDOW_TYPE_POPUP_MENU"   , False );
1016 				Atom type_combo        = XInternAtom( display, "_NET_WM_WINDOW_TYPE_COMBO"        , False );
1017 				if(
1018 					( wl_type == type_toolbar      ) ||
1019 					( wl_type == type_menu         ) ||
1020 					( wl_type == type_dropdownmenu ) ||
1021 					( wl_type == type_popupmenu    ) ||
1022 					( wl_type == type_combo        )
1023 					)
1024 				{
1025 					_x11toolsdebug( "[Ttoolbar=%d]"     , type_toolbar      );
1026 					_x11toolsdebug( "[Tmenu=%d]"        , type_menu         );
1027 					_x11toolsdebug( "[Tdropdownmenu=%d]", type_dropdownmenu );
1028 					_x11toolsdebug( "[Tpopupmenu=%d]"   , type_popupmenu    );
1029 					_x11toolsdebug( "[Tcombo=%d]"       , type_combo        );
1030 					return false;
1031 				}
1032 				_x11toolsdebug( "[F4]" );
1033 			}
1034 		}
1035 		_x11toolsdebug( "[G]" );
1036 		if( ( wa != None ) && ( wt == wa ) )
1037 		{
1038 			_x11toolsdebug( "[G2]" );
1039 			if( wl != None )
1040 			{
1041 				_x11toolsdebug( "[G3]" );
1042 				XWindowAttributes attr;
1043 				Status status = XGetWindowAttributes( display, wl, &attr );
1044 				if( status != 0 )
1045 					if( ( attr.all_event_masks & ( ButtonReleaseMask | KeyReleaseMask ) ) == 0 )
1046 						return false;
1047 				_x11toolsdebug( "[G4]" );
1048 			}
1049 			_x11toolsdebug( "[G5]" );
1050 			return true;
1051 		}
1052 		_x11toolsdebug( "[I]" );
1053 		return false;
1054 	}
1055 	_x11toolsdebug( "[Z]" );
1056 	return false;
1057 }
1058 
1059 
1060 
1061 
X11_waitForWindowMapped(Display * display,Window window)1062 void X11_waitForWindowMapped( Display *display, Window window )
1063 {
1064 	XEvent event;
1065 	do
1066 	{
1067 		XMaskEvent( display, StructureNotifyMask, &event );
1068 	}
1069 	while( ( event.type != MapNotify ) || ( event.xmap.event != window ) );
1070 }
1071 
1072 
1073 
1074 
X11_isCompositingManagerRunning(Display * display)1075 bool X11_isCompositingManagerRunning( Display *display )
1076 {
1077 	Atom netwmcms0 = XInternAtom( display, "_NET_WM_CM_S0", False );
1078 	return XGetSelectionOwner( display, netwmcms0 );
1079 }
1080 
1081 
1082 
1083 
X11_setBlur(Display * display,Window window,bool enable)1084 void X11_setBlur( Display *display, Window window, bool enable )
1085 {
1086 	Atom atom = XInternAtom( display, "_KDE_NET_WM_BLUR_BEHIND_REGION", False );
1087 	if( atom == None )
1088 		return;
1089 	if( enable )
1090 		XChangeProperty( display, window, atom, XA_CARDINAL, 32, PropModeReplace, NULL, 0 );
1091 	else
1092 		XDeleteProperty( display, window, atom );
1093 }
1094