1 //
2 // X11.cc for pekwm
3 // Copyright (C) 2009-2021 the pekwm development team
4 //
5 // This program is licensed under the GNU GPL.
6 // See the LICENSE file for more information.
7 //
8 
9 #include "config.h"
10 
11 #include <string>
12 #include <iostream>
13 #include <cassert>
14 #ifdef PEKWM_HAVE_LIMITS
15 #include <limits>
16 #endif // PEKWM_HAVE_LIMITS
17 
18 extern "C" {
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/select.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/cursorfont.h>
28 #ifdef PEKWM_HAVE_SHAPE
29 #include <X11/extensions/shape.h>
30 #endif // PEKWM_HAVE_SHAPE
31 #ifdef PEKWM_HAVE_XRANDR
32 #include <X11/extensions/Xrandr.h>
33 #endif // PEKWM_HAVE_XRANDR
34 #include <X11/keysym.h> // For XK_ entries
35 #ifdef PEKWM_HAVE_X11_XKBLIB_H
36 #include <X11/XKBlib.h>
37 #endif
38 	bool xerrors_ignore = false;
39 
40 	unsigned int xerrors_count = 0;
41 }
42 
43 #include "X11.hh"
44 #include "Debug.hh"
45 
46 const uint X11::MODIFIER_TO_MASK[] = {
47 	ShiftMask, LockMask, ControlMask,
48 	Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
49 };
50 const uint X11::MODIFIER_TO_MASK_NUM =
51 	sizeof(X11::MODIFIER_TO_MASK) /
52 	sizeof(X11::MODIFIER_TO_MASK[0]);
53 Atom X11::_atoms[MAX_NR_ATOMS];
54 
55 extern "C" {
56 	/**
57 	 * Invoked after all Xlib calls if run in synchronous mode.
58 	 */
afterXlibCall(Display * dpy)59 	static int afterXlibCall(Display *dpy)
60 	{
61 		static uint last_xerrors_count = 0;
62 		if (xerrors_count != last_xerrors_count) {
63 			last_xerrors_count = xerrors_count;
64 		}
65 		return 0;
66 	}
67 
68 	/**
69 	 * XError handler, prints error.
70 	 */
71 	static int
handleXError(Display * dpy,XErrorEvent * ev)72 	handleXError(Display *dpy, XErrorEvent *ev)
73 	{
74 		if (xerrors_ignore) {
75 			return 0;
76 		}
77 
78 		++xerrors_count;
79 		if (Debug::getLevel() >= Debug::LEVEL_TRACE) {
80 			char error_buf[256];
81 			XGetErrorText(dpy, ev->error_code, error_buf, 256);
82 			P_TRACE("XError: " << error_buf << " id: " << ev->resourceid);
83 		}
84 
85 		return 0;
86 	}
87 }
88 
89 static const char *atomnames[] = {
90 	// EWMH atoms
91 	"_NET_SUPPORTED",
92 	"_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING",
93 	"_NET_NUMBER_OF_DESKTOPS",
94 	"_NET_DESKTOP_GEOMETRY", "_NET_DESKTOP_VIEWPORT",
95 	"_NET_CURRENT_DESKTOP", "_NET_DESKTOP_NAMES",
96 	"_NET_ACTIVE_WINDOW", "_NET_WORKAREA",
97 	"_NET_DESKTOP_LAYOUT", "_NET_SUPPORTING_WM_CHECK",
98 	"_NET_CLOSE_WINDOW",
99 	"_NET_WM_MOVERESIZE",
100 	"_NET_REQUEST_FRAME_EXTENTS",
101 	"_NET_WM_NAME", "_NET_WM_VISIBLE_NAME",
102 	"_NET_WM_ICON_NAME", "_NET_WM_VISIBLE_ICON_NAME",
103 	"_NET_WM_ICON", "_NET_WM_DESKTOP",
104 	"_NET_WM_STRUT", "_NET_WM_PID",
105 	"_NET_WM_USER_TIME",
106 	"_NET_FRAME_EXTENTS",
107 	"_NET_WM_WINDOW_OPACITY",
108 
109 	"_NET_WM_WINDOW_TYPE",
110 	"_NET_WM_WINDOW_TYPE_DESKTOP",
111 	"_NET_WM_WINDOW_TYPE_DOCK",
112 	"_NET_WM_WINDOW_TYPE_TOOLBAR",
113 	"_NET_WM_WINDOW_TYPE_MENU",
114 	"_NET_WM_WINDOW_TYPE_UTILITY",
115 	"_NET_WM_WINDOW_TYPE_SPLASH",
116 	"_NET_WM_WINDOW_TYPE_DIALOG",
117 	"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
118 	"_NET_WM_WINDOW_TYPE_POPUP_MENU",
119 	"_NET_WM_WINDOW_TYPE_TOOLTIP",
120 	"_NET_WM_WINDOW_TYPE_NOTIFICATION",
121 	"_NET_WM_WINDOW_TYPE_COMBO",
122 	"_NET_WM_WINDOW_TYPE_DND",
123 	"_NET_WM_WINDOW_TYPE_NORMAL",
124 
125 	"_NET_WM_STATE",
126 	"_NET_WM_STATE_MODAL", "_NET_WM_STATE_STICKY",
127 	"_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ",
128 	"_NET_WM_STATE_SHADED",
129 	"_NET_WM_STATE_SKIP_TASKBAR", "_NET_WM_STATE_SKIP_PAGER",
130 	"_NET_WM_STATE_HIDDEN", "_NET_WM_STATE_FULLSCREEN",
131 	"_NET_WM_STATE_ABOVE", "_NET_WM_STATE_BELOW",
132 	"_NET_WM_STATE_DEMANDS_ATTENTION",
133 
134 	"_NET_WM_ALLOWED_ACTIONS",
135 	"_NET_WM_ACTION_MOVE", "_NET_WM_ACTION_RESIZE",
136 	"_NET_WM_ACTION_MINIMIZE", "_NET_WM_ACTION_SHADE",
137 	"_NET_WM_ACTION_STICK",
138 	"_NET_WM_ACTION_MAXIMIZE_VERT", "_NET_WM_ACTION_MAXIMIZE_HORZ",
139 	"_NET_WM_ACTION_FULLSCREEN", "_NET_WM_ACTION_CHANGE_DESKTOP",
140 	"_NET_WM_ACTION_CLOSE",
141 
142 	"UTF8_STRING",
143 	"STRING",
144 	"MANAGER",
145 
146 	// pekwm atoms
147 	"_PEKWM_FRAME_ID",
148 	"_PEKWM_FRAME_ORDER",
149 	"_PEKWM_FRAME_ACTIVE",
150 	"_PEKWM_FRAME_DECOR",
151 	"_PEKWM_FRAME_SKIP",
152 	"_PEKWM_TITLE",
153 	"_PEKWM_BG_PID",
154 	"_PEKWM_CMD",
155 	"_PEKWM_THEME",
156 
157 	// ICCCM atoms
158 	"WM_NAME",
159 	"WM_ICON_NAME",
160 	"WM_HINTS",
161 	"WM_CLASS",
162 	"WM_STATE",
163 	"WM_CHANGE_STATE",
164 	"WM_PROTOCOLS",
165 	"WM_DELETE_WINDOW",
166 	"WM_COLORMAP_WINDOWS",
167 	"WM_TAKE_FOCUS",
168 	"WM_WINDOW_ROLE",
169 	"WM_CLIENT_MACHINE",
170 
171 	// miscellaneous atoms
172 	"_MOTIF_WM_HINTS",
173 
174 	"_XROOTPMAP_ID",
175 	"_XSETROOT_ID",
176 };
177 
178 std::ostream&
operator <<(std::ostream & os,const Geometry & gm)179 operator<<(std::ostream& os, const Geometry& gm)
180 {
181 	os << "Geometry";
182 	os << " x:" << gm.x << " y: " << gm.y;
183 	os << " width: " << gm.width << " height: " << gm.height;
184 	return os;
185 }
186 
Geometry(void)187 Geometry::Geometry(void)
188 	: x(0),
189 	  y(0),
190 	  width(1),
191 	  height(1)
192 {
193 }
194 
Geometry(int _x,int _y,unsigned int _width,unsigned int _height)195 Geometry::Geometry(int _x, int _y, unsigned int _width, unsigned int _height)
196 	: x(_x),
197 	  y(_y),
198 	  width(_width),
199 	  height(_height)
200 {
201 }
202 
Geometry(const Geometry & gm)203 Geometry::Geometry(const Geometry &gm)
204 	: x(gm.x),
205 	  y(gm.y),
206 	  width(gm.width),
207 	  height(gm.height)
208 {
209 }
210 
~Geometry(void)211 Geometry::~Geometry(void)
212 {
213 }
214 
215 Geometry&
operator =(const Geometry & gm)216 Geometry::operator=(const Geometry& gm)
217 {
218 	x = gm.x;
219 	y = gm.y;
220 	width = gm.width;
221 	height = gm.height;
222 	return *this;
223 }
224 
225 bool
operator ==(const Geometry & gm)226 Geometry::operator==(const Geometry& gm)
227 {
228 	return ((x == gm.x) && (y == gm.y) &&
229 		(width == gm.width) && (height == gm.height));
230 }
231 
232 bool
operator !=(const Geometry & gm)233 Geometry::operator!=(const Geometry& gm)
234 {
235 	return (x != gm.x) || (y != gm.y) ||
236 		(width != gm.width) || (height != gm.height);
237 }
238 
239 int
diffMask(const Geometry & old_gm)240 Geometry::diffMask(const Geometry &old_gm)
241 {
242 	int mask = 0;
243 	if (x != old_gm.x) {
244 		mask |= X_VALUE;
245 	}
246 	if (y != old_gm.y) {
247 		mask |= Y_VALUE;
248 	}
249 	if (width != old_gm.width) {
250 		mask |= WIDTH_VALUE;
251 	}
252 	if (height != old_gm.height) {
253 		mask |= HEIGHT_VALUE;
254 	}
255 	return mask;
256 }
257 
Strut(long l,long r,long t,long b,int nhead)258 Strut::Strut(long l, long r, long t, long b, int nhead)
259 	: left(l),
260 	  right(r),
261 	  top(t),
262 	  bottom(b),
263 	  head(nhead)
264 {
265 }
266 
~Strut(void)267 Strut::~Strut(void)
268 {
269 }
270 
271 bool
isSet(void) const272 Strut::isSet(void) const
273 {
274 	return left != 0 || right != 0 || top != 0 || bottom != 0;
275 }
276 
277 void
clear(void)278 Strut::clear(void)
279 {
280 	left = 0;
281 	right = 0;
282 	top = 0;
283 	bottom = 0;
284 }
285 
286 void
operator =(const long * s)287 Strut::operator=(const long *s)
288 {
289 	left = s[0];
290 	right = s[1];
291 	top = s[2];
292 	bottom = s[3];
293 }
294 
295 bool
operator ==(const Strut & rhs) const296 Strut::operator==(const Strut& rhs) const
297 {
298 	return left == rhs.left
299 		&& right == rhs.right
300 		&& top == rhs.top
301 		&& bottom == rhs.bottom
302 		&& head == rhs.head;
303 }
304 
305 bool
operator !=(const Strut & rhs) const306 Strut::operator!=(const Strut& rhs) const
307 {
308 	return !operator==(rhs);
309 }
310 
operator <<(std::ostream & stream,const Strut & strut)311 std::ostream& operator<<(std::ostream &stream, const Strut &strut)
312 {
313 	stream << "Strut l: " << strut.left << " r: " << strut.right;
314 	stream << " t: " << strut.top << " b: " << strut.bottom;
315 	stream << " head " << strut.head;
316 	return stream;
317 }
318 
319 /**
320  * Helper class for XColor.
321  */
322 class X11::ColorEntry {
323 public:
ColorEntry(const std::string & name)324 	ColorEntry(const std::string &name) : _name(name), _ref(0) { }
~ColorEntry(void)325 	~ColorEntry(void) { }
326 
getColor(void)327 	inline XColor *getColor(void) { return &_xc; }
328 
getRef(void) const329 	inline uint getRef(void) const { return _ref; }
incRef(void)330 	inline void incRef(void) { _ref++; }
decRef(void)331 	inline void decRef(void) { if (_ref > 0) { _ref--; } }
332 
operator ==(const std::string & name)333 	inline bool operator==(const std::string &name) {
334 		return (::strcasecmp(_name.c_str(), name.c_str()) == 0);
335 	}
336 
337 private:
338 	std::string _name;
339 	XColor _xc;
340 	uint _ref;
341 };
342 
343 /**
344  * Init X11 connection, must be called before any X11:: call is made.
345  */
346 void
init(Display * dpy,bool synchronous,bool honour_randr)347 X11::init(Display *dpy, bool synchronous, bool honour_randr)
348 {
349 	if (_dpy) {
350 		throw std::string("X11, trying to create multiple instances");
351 	}
352 
353 	_dpy = dpy;
354 	_honour_randr = honour_randr;
355 
356 	if (synchronous) {
357 		XSynchronize(_dpy, True);
358 		XSetAfterFunction(_dpy, afterXlibCall);
359 	}
360 	XSetErrorHandler(handleXError);
361 
362 	grabServer();
363 
364 	_fd = ConnectionNumber(dpy);
365 	_screen = DefaultScreen(_dpy);
366 	_root = RootWindow(_dpy, _screen);
367 
368 	_depth = DefaultDepth(_dpy, _screen);
369 	_visual = DefaultVisual(_dpy, _screen);
370 	_gc = DefaultGC(_dpy, _screen);
371 	XGCValues gv;
372 	gv.function = GXcopy;
373 	XChangeGC(_dpy, _gc, GCFunction, &gv);
374 	_colormap = DefaultColormap(_dpy, _screen);
375 	_modifier_map = XGetModifierMapping(_dpy);
376 
377 	_screen_gm.width = WidthOfScreen(ScreenOfDisplay(_dpy, _screen));
378 	_screen_gm.height = HeightOfScreen(ScreenOfDisplay(_dpy, _screen));
379 
380 	// create resize cursors
381 	_cursor_map[CURSOR_TOP_LEFT] = XCreateFontCursor(_dpy, XC_top_left_corner);
382 	_cursor_map[CURSOR_TOP] = XCreateFontCursor(_dpy, XC_top_side);
383 	_cursor_map[CURSOR_TOP_RIGHT] =
384 		XCreateFontCursor(_dpy, XC_top_right_corner);
385 	_cursor_map[CURSOR_LEFT] = XCreateFontCursor(_dpy, XC_left_side);
386 	_cursor_map[CURSOR_RIGHT] = XCreateFontCursor(_dpy, XC_right_side);
387 	_cursor_map[CURSOR_BOTTOM_LEFT] =
388 		XCreateFontCursor(_dpy, XC_bottom_left_corner);
389 	_cursor_map[CURSOR_BOTTOM] = XCreateFontCursor(_dpy, XC_bottom_side);
390 	_cursor_map[CURSOR_BOTTOM_RIGHT] =
391 		XCreateFontCursor(_dpy, XC_bottom_right_corner);
392 	// create other cursors
393 	_cursor_map[CURSOR_ARROW] = XCreateFontCursor(_dpy, XC_left_ptr);
394 	_cursor_map[CURSOR_MOVE]  = XCreateFontCursor(_dpy, XC_fleur);
395 	_cursor_map[CURSOR_RESIZE] = XCreateFontCursor(_dpy, XC_plus);
396 
397 #ifdef PEKWM_HAVE_SHAPE
398 	{
399 		int dummy_error;
400 		_has_extension_shape =
401 			XShapeQueryExtension(_dpy, &_event_shape, &dummy_error);
402 	}
403 #endif // PEKWM_HAVE_SHAPE
404 
405 #ifdef PEKWM_HAVE_XRANDR
406 	{
407 		int dummy_error;
408 		_has_extension_xrandr =
409 			XRRQueryExtension(_dpy, &_event_xrandr, &dummy_error);
410 	}
411 #endif // PEKWM_HAVE_XRANDR
412 
413 #ifdef PEKWM_HAVE_X11_XKBLIB_H
414 	{
415 		int major = XkbMajorVersion;
416 		int minor = XkbMinorVersion;
417 		int ext_opcode, ext_ev, ext_err;
418 		_has_extension_xkb =
419 			XkbQueryExtension(_dpy, &ext_opcode, &ext_ev, &ext_err,
420 					  &major, &minor);
421 	}
422 #endif // PEKWM_HAVE_X11_XKBLIB_H
423 
424 	// Now screen geometry has been read and extensions have been
425 	// looked for, read head information.
426 	initHeads();
427 
428 	// initialize array values
429 	for (uint i = 0; i < (BUTTON_NO - 1); ++i) {
430 		_last_click_time[i] = 0;
431 	}
432 
433 	// Figure out what keys the Num and Scroll Locks are
434 	setLockKeys();
435 
436 	ungrabServer(true);
437 
438 	_xc_default.pixel = BlackPixel(_dpy, _screen);
439 	_xc_default.red = _xc_default.green = _xc_default.blue = 0;
440 
441 	assert(sizeof(atomnames)/sizeof(char*) == MAX_NR_ATOMS);
442 	if (! XInternAtoms(_dpy, const_cast<char**>(atomnames), MAX_NR_ATOMS,
443 			   0, _atoms)) {
444 		P_ERR("XInternAtoms did not return all requested atoms");
445 	}
446 }
447 
448 //! @brief X11 destructor
449 void
destruct(void)450 X11::destruct(void) {
451 	if (_colors.size() > 0) {
452 		ulong *pixels = new ulong[_colors.size()];
453 		for (uint i=0; i < _colors.size(); ++i) {
454 			pixels[i] = _colors[i]->getColor()->pixel;
455 			delete _colors[i];
456 		}
457 		XFreeColors(_dpy, X11::getColormap(),
458 			    pixels, _colors.size(), 0);
459 		delete [] pixels;
460 	}
461 
462 	if (_modifier_map) {
463 		XFreeModifiermap(_modifier_map);
464 	}
465 
466 	for (Cursor i = CURSOR_0; i != CURSOR_NONE; i++) {
467 		XFreeCursor(_dpy, _cursor_map[i]);
468 	}
469 
470 	// Under certain circumstances trying to restart pekwm can cause it to
471 	// use 100% of the CPU without making any progress with the restart.
472 	// This X11:sync() seems to be work around the issue (c.f. #300).
473 	X11::sync(True);
474 
475 	XCloseDisplay(_dpy);
476 	_dpy = 0;
477 }
478 
479 XColor *
getColor(const std::string & color)480 X11::getColor(const std::string &color)
481 {
482 	if (strcasecmp(color.c_str(), "EMPTY") == 0) {
483 		return &_xc_default;
484 	}
485 
486 	std::vector<ColorEntry*>::iterator it = _colors.begin();
487 	for (; it != _colors.end(); ++it) {
488 		if (*(*it) == color) {
489 			(*it)->incRef();
490 			return (*it)->getColor();
491 		}
492 	}
493 
494 	// create new entry
495 	ColorEntry *entry = new ColorEntry(color);
496 
497 	// X alloc
498 	XColor dummy;
499 	if (XAllocNamedColor(_dpy, X11::getColormap(),
500 			     color.c_str(), entry->getColor(), &dummy) == 0) {
501 		P_ERR("failed to alloc color: " << color);
502 		delete entry;
503 		entry = 0;
504 	} else {
505 		_colors.push_back(entry);
506 		entry->incRef();
507 	}
508 
509 	return entry ? entry->getColor() : &_xc_default;
510 }
511 
512 void
returnColor(XColor * & xc)513 X11::returnColor(XColor*& xc)
514 {
515 	if (&_xc_default == xc) { // no need to return default color
516 		return;
517 	}
518 
519 	std::vector<ColorEntry*>::iterator it = _colors.begin();
520 	for (; it != _colors.end(); ++it) {
521 		if ((*it)->getColor() == xc) {
522 			(*it)->decRef();
523 			if (((*it)->getRef() == 0)) {
524 				ulong pixels[1] = { (*it)->getColor()->pixel };
525 				XFreeColors(X11::getDpy(), X11::getColormap(), pixels, 1, 0);
526 
527 				delete *it;
528 				_colors.erase(it);
529 			}
530 			break;
531 		}
532 	}
533 
534 	xc = nullptr;
535 }
536 
537 ulong
getWhitePixel(void)538 X11::getWhitePixel(void)
539 {
540 	return WhitePixel(_dpy, _screen);
541 }
542 
543 ulong
getBlackPixel(void)544 X11::getBlackPixel(void)
545 {
546 	return BlackPixel(_dpy, _screen);
547 }
548 
549 void
free(void * data)550 X11::free(void* data)
551 {
552 	XFree(data);
553 }
554 
555 void
warpPointer(int x,int y)556 X11::warpPointer(int x, int y)
557 {
558 	if (_dpy) {
559 		XWarpPointer(_dpy, None, _root, 0, 0, 0, 0, x, y);
560 	}
561 }
562 
563 void
moveWindow(Window win,int x,int y)564 X11::moveWindow(Window win, int x, int y)
565 {
566 	if (_dpy) {
567 		XMoveWindow(_dpy, win, x, y);
568 	}
569 }
570 void
resizeWindow(Window win,unsigned int width,unsigned int height)571 X11::resizeWindow(Window win,
572                   unsigned int width, unsigned int height)
573 {
574 	if (_dpy) {
575 		XResizeWindow(_dpy, win, width, height);
576 	}
577 }
578 
579 void
moveResizeWindow(Window win,int x,int y,unsigned int width,unsigned int height)580 X11::moveResizeWindow(Window win, int x, int y,
581                       unsigned int width, unsigned int height)
582 {
583 	if (_dpy) {
584 		XMoveResizeWindow(_dpy, win, x, y, width, height);
585 	}
586 }
587 
588 /**
589  * Remove state modifiers such as NumLock from state.
590  */
591 void
stripStateModifiers(unsigned int * state)592 X11::stripStateModifiers(unsigned int *state)
593 {
594 	*state &= ~(_num_lock | _scroll_lock | LockMask |
595 		    KbdLayoutMask1 | KbdLayoutMask2);
596 }
597 
598 /**
599  * Remove button modifiers from state.
600  */
601 void
stripButtonModifiers(unsigned int * state)602 X11::stripButtonModifiers(unsigned int *state)
603 {
604 	*state &= ~(Button1Mask | Button2Mask | Button3Mask |
605 		    Button4Mask | Button5Mask);
606 }
607 
608 /**
609  * Figure out what keys the Num and Scroll Locks are
610  */
611 void
setLockKeys(void)612 X11::setLockKeys(void)
613 {
614 	_num_lock = getMaskFromKeycode(XKeysymToKeycode(_dpy, XK_Num_Lock));
615 	_scroll_lock = getMaskFromKeycode(XKeysymToKeycode(_dpy, XK_Scroll_Lock));
616 }
617 
618 void
flush(void)619 X11::flush(void)
620 {
621 	if (_dpy) {
622 		XFlush(_dpy);
623 	}
624 }
625 
626 int
pending(void)627 X11::pending(void)
628 {
629 	if (_dpy) {
630 		return XPending(_dpy);
631 	}
632 	return 0;
633 }
634 
635 /**
636  * Get next event using select to avoid signal blocking
637  *
638  * @param ev Event to fill in.
639  * @return true if event was fetched, else false.
640  */
641 bool
getNextEvent(XEvent & ev,struct timeval * timeout)642 X11::getNextEvent(XEvent &ev, struct timeval *timeout)
643 {
644 	if (pending()) {
645 		XNextEvent(_dpy, &ev);
646 		return true;
647 	}
648 
649 	int ret;
650 	fd_set rfds;
651 
652 	flush();
653 
654 	FD_ZERO(&rfds);
655 	FD_SET(_fd, &rfds);
656 
657 	ret = select(_fd + 1, &rfds, nullptr, nullptr, timeout);
658 	if (ret > 0) {
659 		XNextEvent(_dpy, &ev);
660 	}
661 
662 	return ret > 0;
663 }
664 
665 void
allowEvents(int event_mode,Time time)666 X11::allowEvents(int event_mode, Time time)
667 {
668 	if (_dpy) {
669 		XAllowEvents(_dpy, event_mode, time);
670 	}
671 }
672 
673 //! @brief Grabs the server, counting number of grabs
674 bool
grabServer(void)675 X11::grabServer(void)
676 {
677 	if (_server_grabs == 0) {
678 		P_TRACE("grabbing server");
679 		XGrabServer(_dpy);
680 		++_server_grabs;
681 	} else {
682 		++_server_grabs;
683 	}
684 	return _server_grabs == 1;
685 }
686 
687 //! @brief Ungrabs the server, counting number of grabs
688 bool
ungrabServer(bool sync)689 X11::ungrabServer(bool sync)
690 {
691 	if (_server_grabs > 0) {
692 		if (--_server_grabs == 0) {
693 			if (sync) {
694 				P_TRACE("0 server grabs left, syncing and ungrab.");
695 				X11::sync(False);
696 			} else {
697 				P_TRACE("0 server grabs left, ungrabbing server.");
698 			}
699 			XUngrabServer(_dpy);
700 		}
701 	}
702 	return _server_grabs == 0;
703 }
704 
705 //! @brief Grabs the keyboard
706 bool
grabKeyboard(Window win)707 X11::grabKeyboard(Window win)
708 {
709 	P_TRACE("grabbing keyboard");
710 	if (XGrabKeyboard(_dpy, win, false, GrabModeAsync, GrabModeAsync,
711 			  CurrentTime) == GrabSuccess) {
712 		return true;
713 	}
714 	P_ERR("failed to grab keyboard on " << win);
715 	return false;
716 }
717 
718 //! @brief Ungrabs the keyboard
719 bool
ungrabKeyboard(void)720 X11::ungrabKeyboard(void)
721 {
722 	P_TRACE("ungrabbing keyboard");
723 	XUngrabKeyboard(_dpy, CurrentTime);
724 	return false;
725 }
726 
727 //! @brief Grabs the pointer
728 bool
grabPointer(Window win,uint event_mask,CursorType type)729 X11::grabPointer(Window win, uint event_mask, CursorType type)
730 {
731 	P_TRACE("grabbing pointer");
732 	Cursor cursor = type < CURSOR_NONE ? _cursor_map[type] : None;
733 	if (XGrabPointer(_dpy, win, false, event_mask, GrabModeAsync, GrabModeAsync,
734 			 None, cursor, CurrentTime) == GrabSuccess) {
735 		return true;
736 	}
737 	P_ERR("failed to grab pointer on " << win
738 	      << ", event_mask " << event_mask
739 	      << ", type " << type);
740 	return false;
741 }
742 
743 //! @brief Ungrabs the pointer
744 bool
ungrabPointer(void)745 X11::ungrabPointer(void)
746 {
747 	P_TRACE("ungrabbing pointer");
748 	XUngrabPointer(_dpy, CurrentTime);
749 	return false;
750 }
751 
752 /**
753  * Refetches the root-window size.
754  */
755 bool
updateGeometry(uint width,uint height)756 X11::updateGeometry(uint width, uint height)
757 {
758 #ifdef PEKWM_HAVE_XRANDR
759 	if (! _honour_randr || ! _has_extension_xrandr) {
760 		return false;
761 	}
762 
763 	// The screen has changed geometry in some way. To handle this the
764 	// head information is read once again, the root window is re sized
765 	// and strut information is updated.
766 	initHeads();
767 
768 	_screen_gm.width = width;
769 	_screen_gm.height = height;
770 	return true;
771 #else // ! PEKWM_HAVE_XRANDR
772 	return false;
773 #endif // PEKWM_HAVE_XRANDR
774 }
775 
776 bool
selectXRandrInput(void)777 X11::selectXRandrInput(void)
778 {
779 #ifdef PEKWM_HAVE_XRANDR
780 	if (_honour_randr && _has_extension_xrandr) {
781 		XRRSelectInput(_dpy, _root, RRScreenChangeNotifyMask);
782 		return true;
783 	}
784 #endif // PEKWM_HAVE_XRANDR
785 	return false;
786 }
787 
788 bool
getScreenChangeNotification(XEvent * ev,ScreenChangeNotification & scn)789 X11::getScreenChangeNotification(XEvent *ev, ScreenChangeNotification &scn)
790 {
791 #ifdef PEKWM_HAVE_XRANDR
792 	if (_honour_randr
793 	    && _has_extension_xrandr
794 	    && ev->type == _event_xrandr + RRScreenChangeNotify) {
795 		XRRScreenChangeNotifyEvent* scr_ev =
796 			reinterpret_cast<XRRScreenChangeNotifyEvent*>(ev);
797 		if  (scr_ev->rotation == RR_Rotate_90
798 		     || scr_ev->rotation == RR_Rotate_270) {
799 			scn.width = scr_ev->height;
800 			scn.height = scr_ev->width;
801 		} else {
802 			scn.width = scr_ev->width;
803 			scn.height = scr_ev->height;
804 		}
805 		return true;
806 	}
807 #endif // PEKWM_HAVE_XRANDR
808 	return false;
809 }
810 
811 //! @brief Searches for the head closest to the coordinates x,y.
812 //! @return The nearest head.  Head numbers are indexed from 0.
813 uint
getNearestHead(int x,int y)814 X11::getNearestHead(int x, int y)
815 {
816 	if(_heads.size() > 1) {
817 		// set distance to the highest uint value
818 		uint min_distance = std::numeric_limits<uint>::max();
819 		uint nearest_head = 0;
820 
821 		uint distance;
822 		int head_t, head_b, head_l, head_r;
823 		for(uint head = 0; head < _heads.size(); ++head) {
824 			head_t = _heads[head].y;
825 			head_b = _heads[head].y + _heads[head].height;
826 			head_l = _heads[head].x;
827 			head_r = _heads[head].x + _heads[head].width;
828 
829 			if(x > head_r) {
830 				if(y < head_t) {
831 					// above and right of the head
832 					distance = calcDistance(x, y, head_r, head_t);
833 				} else if(y > head_b) {
834 					// below and right of the head
835 					distance = calcDistance(x, y, head_r, head_b);
836 				} else {
837 					// right of the head
838 					distance = calcDistance(x, head_r);
839 				}
840 			} else if(x < head_l) {
841 				if(y < head_t) {
842 					// above and left of the head
843 					distance = calcDistance(x, y, head_l, head_t);
844 				} else if(y > head_b) {
845 					// below and left of the head
846 					distance = calcDistance(x, y, head_l, head_b);
847 				} else {
848 					// left of the head
849 					distance = calcDistance(x, head_l);
850 				}
851 			} else {
852 				if(y < head_t) {
853 					// above the head
854 					distance = calcDistance(y, head_t);
855 				} else if(y > head_b) {
856 					// below the head
857 					distance = calcDistance(y, head_b);
858 				} else {
859 					// on the head
860 					return head;
861 				}
862 			}
863 
864 
865 			if(distance < min_distance) {
866 				min_distance = distance;
867 				nearest_head = head;
868 			}
869 		}
870 		return nearest_head;
871 	} else {
872 		return 0;
873 	}
874 }
875 
876 /**
877  * Searches for the head that the pointer currently is on.
878  *
879  * @return Head number for the head that the cursor is on.
880  */
881 uint
getCursorHead(void)882 X11::getCursorHead(void)
883 {
884 	uint head = 0;
885 
886 	if (_heads.size() > 1) {
887 		int x = 0, y = 0;
888 		getMousePosition(x, y);
889 		head = getNearestHead(x, y);
890 	}
891 
892 	return head;
893 }
894 
895 
896 void
addHead(const Head & head)897 X11::addHead(const Head &head)
898 {
899 	_heads.push_back(head);
900 }
901 
902 /**
903  * Fills head_info with info about head nr head
904  *
905  * @param head Head number to examine
906  * @param head_info Returning info about the head
907  * @return true if xinerama is off or head exists.
908  */
909 bool
getHeadInfo(uint head,Geometry & head_info)910 X11::getHeadInfo(uint head, Geometry &head_info)
911 {
912 	if (head  < _heads.size()) {
913 		head_info.x = _heads[head].x;
914 		head_info.y = _heads[head].y;
915 		head_info.width = _heads[head].width;
916 		head_info.height = _heads[head].height;
917 		return true;
918 	} else {
919 		P_ERR("head " << head << " does not exist");
920 		return false;
921 	}
922 }
923 
924 /**
925  * Fill head_info with info about head at x/y.
926  */
927 void
getHeadInfo(int x,int y,Geometry & head_info)928 X11::getHeadInfo(int x, int y, Geometry &head_info)
929 {
930 	std::vector<Head>::iterator it = _heads.begin();
931 	for (; it != _heads.end(); ++it) {
932 		if (x >= it->x && x <= signed(it->x + it->width)
933 		    && y >= it->y && y <= signed(it->y + it->height)) {
934 			head_info.x = it->x;
935 			head_info.y = it->y;
936 			head_info.width = it->width;
937 			head_info.height = it->height;
938 			return;
939 		}
940 	}
941 	head_info = _screen_gm;
942 }
943 
944 /**
945  * Same as getHeadInfo but returns Geometry instead of filling it in.
946  */
947 Geometry
getHeadGeometry(uint head)948 X11::getHeadGeometry(uint head)
949 {
950 	Geometry gm(_screen_gm);
951 	getHeadInfo(head, gm);
952 	return gm;
953 }
954 
955 int
getNumHeads(void)956 X11::getNumHeads(void)
957 {
958 	return _heads.size();
959 }
960 
961 const char*
getAtomString(AtomName name)962 X11::getAtomString(AtomName name)
963 {
964 	return atomnames[name];
965 }
966 
967 std::string
getAtomIdString(Atom id)968 X11::getAtomIdString(Atom id)
969 {
970 	std::string name;
971 	char *c_name = XGetAtomName(_dpy, id);
972 	if (c_name != nullptr) {
973 		name = c_name;
974 		XFree(c_name);
975 	}
976 	return name;
977 }
978 
979 AtomName
getAtomName(Atom atom)980 X11::getAtomName(Atom atom)
981 {
982 	for (int i = 0; i < MAX_NR_ATOMS; i++) {
983 		if (_atoms[i] == atom) {
984 			return static_cast<AtomName>(i);
985 		}
986 	}
987 	return MAX_NR_ATOMS;
988 }
989 
990 void
setAtom(Window win,AtomName aname,AtomName value)991 X11::setAtom(Window win, AtomName aname, AtomName value)
992 {
993 	changeProperty(win, _atoms[aname], XA_ATOM, 32,
994 		       PropModeReplace, (uchar *) &_atoms[value], 1);
995 }
996 
997 void
setAtoms(Window win,AtomName aname,Atom * values,int size)998 X11::setAtoms(Window win, AtomName aname, Atom *values, int size)
999 {
1000 	changeProperty(win, _atoms[aname], XA_ATOM, 32,
1001 		       PropModeReplace, (uchar *) values, size);
1002 }
1003 
1004 void
setEwmhAtomsSupport(Window win)1005 X11::setEwmhAtomsSupport(Window win)
1006 {
1007 	changeProperty(win, _atoms[NET_SUPPORTED], XA_ATOM, 32,
1008 		       PropModeReplace, (uchar *) _atoms, UTF8_STRING+1);
1009 }
1010 
1011 bool
getWindow(Window win,AtomName aname,Window & value)1012 X11::getWindow(Window win, AtomName aname, Window& value)
1013 {
1014 	uchar *udata = 0;
1015 	if (getProperty(win, _atoms[aname], XA_WINDOW, 1L, &udata, 0)) {
1016 		value = *reinterpret_cast<Window*>(udata);
1017 		X11::free(udata);
1018 		return true;
1019 	}
1020 	return false;
1021 }
1022 
1023 void
setWindow(Window win,AtomName aname,Window value)1024 X11::setWindow(Window win, AtomName aname, Window value)
1025 {
1026 	changeProperty(win, _atoms[aname], XA_WINDOW, 32,
1027 		       PropModeReplace, (uchar *) &value, 1);
1028 }
1029 
1030 void
setWindows(Window win,AtomName aname,Window * values,int size)1031 X11::setWindows(Window win, AtomName aname, Window *values,
1032                 int size)
1033 {
1034 	changeProperty(win, _atoms[aname], XA_WINDOW, 32,
1035 		       PropModeReplace, (uchar *) values, size);
1036 }
1037 
1038 bool
getCardinal(Window win,AtomName aname,Cardinal & value,long format)1039 X11::getCardinal(Window win, AtomName aname, Cardinal &value, long format)
1040 {
1041 	uchar *udata = nullptr;
1042 	if (getProperty(win, _atoms[aname], format, 1L, &udata, 0)) {
1043 		value = *reinterpret_cast<Cardinal*>(udata);
1044 		X11::free(udata);
1045 		return true;
1046 	}
1047 	return false;
1048 }
1049 
1050 void
setCardinals(Window win,AtomName aname,Cardinal * values,int num)1051 X11::setCardinals(Window win, AtomName aname,
1052                   Cardinal *values, int num)
1053 {
1054 	changeProperty(win, _atoms[aname], XA_CARDINAL, 32, PropModeReplace,
1055 		       reinterpret_cast<uchar*>(values), num);
1056 }
1057 
1058 bool
getUtf8String(Window win,AtomName aname,std::string & value)1059 X11::getUtf8String(Window win, AtomName aname, std::string &value)
1060 {
1061 	return getUtf8StringId(win, _atoms[aname], value);
1062 }
1063 
1064 bool
getUtf8StringId(Window win,Atom id,std::string & value)1065 X11::getUtf8StringId(Window win, Atom id, std::string &value)
1066 {
1067 	uchar *data = nullptr;
1068 	if (getProperty(win, id, _atoms[UTF8_STRING], 0, &data, 0)) {
1069 		value = std::string(reinterpret_cast<char*>(data));
1070 		X11::free(data);
1071 		return true;
1072 	}
1073 	return false;
1074 }
1075 
1076 void
setUtf8String(Window win,AtomName aname,const std::string & value)1077 X11::setUtf8String(Window win, AtomName aname,
1078                    const std::string &value)
1079 {
1080 	changeProperty(win, _atoms[aname], _atoms[UTF8_STRING], 8,
1081 		       PropModeReplace,
1082 		       reinterpret_cast<const uchar*>(value.c_str()),
1083 		       value.size());
1084 }
1085 
1086 void
setUtf8StringArray(Window win,AtomName aname,unsigned char * values,uint length)1087 X11::setUtf8StringArray(Window win, AtomName aname,
1088                         unsigned char *values, uint length)
1089 {
1090 	changeProperty(win, _atoms[aname], _atoms[UTF8_STRING], 8,
1091 		       PropModeReplace, values, length);
1092 }
1093 
1094 bool
getString(Window win,AtomName aname,std::string & value)1095 X11::getString(Window win, AtomName aname, std::string &value)
1096 {
1097 	return X11::getStringId(win, _atoms[aname], value);
1098 }
1099 
1100 bool
getStringId(Window win,Atom id,std::string & value)1101 X11::getStringId(Window win, Atom id, std::string &value)
1102 {
1103 	uchar *data = 0;
1104 	long uint actual;
1105 	if (getProperty(win, id, XA_STRING, 0, &data, &actual)) {
1106 		value = std::string((const char*) data);
1107 		while (value.size() < actual) {
1108 			value += ',';
1109 			value += ((const char*) data) + value.size();
1110 		}
1111 		X11::free(data);
1112 		return true;
1113 	}
1114 	return false;
1115 }
1116 
1117 void
setString(Window win,AtomName aname,const std::string & value)1118 X11::setString(Window win, AtomName aname, const std::string &value)
1119 {
1120 	changeProperty(win, _atoms[aname], XA_STRING, 8, PropModeReplace,
1121 		       (uchar*)value.c_str(), value.size());
1122 }
1123 
1124 bool
listProperties(Window win,std::vector<Atom> & atoms)1125 X11::listProperties(Window win, std::vector<Atom>& atoms)
1126 {
1127 	if (! _dpy) {
1128 		return false;
1129 	}
1130 
1131 	int num_props;
1132 	Atom *c_atoms = XListProperties(_dpy, win, &num_props);
1133 	if (c_atoms) {
1134 		for (int i = 0; i < num_props; i++) {
1135 			atoms.push_back(c_atoms[i]);
1136 		}
1137 		XFree(c_atoms);
1138 	}
1139 	return c_atoms != nullptr;
1140 }
1141 
1142 bool
getProperty(Window win,Atom atom,Atom type,ulong expected,uchar ** data_ret,ulong * actual)1143 X11::getProperty(Window win, Atom atom, Atom type,
1144                  ulong expected, uchar **data_ret, ulong *actual)
1145 {
1146 	if (expected == 0) {
1147 		expected = 1024;
1148 	}
1149 
1150 	uchar *data = nullptr;
1151 	ulong read = 0, left = 0;
1152 	do {
1153 		if (data != nullptr) {
1154 			X11::free(data);
1155 			data = nullptr;
1156 		}
1157 		expected += left;
1158 
1159 		Atom r_type;
1160 		int r_format, status;
1161 		status =
1162 			XGetWindowProperty(_dpy, win, atom,
1163 					   0L, expected, False, type,
1164 					   &r_type, &r_format, &read, &left, &data);
1165 		if (status != Success || type != r_type || read == 0) {
1166 			if (data != nullptr) {
1167 				X11::free(data);
1168 				data = nullptr;
1169 			}
1170 			left = 0;
1171 		}
1172 	} while (left);
1173 
1174 	if (actual) {
1175 		*actual = read;
1176 	}
1177 	*data_ret = data;
1178 
1179 	return data != nullptr;
1180 }
1181 
1182 bool
getTextProperty(Window win,Atom atom,std::string & value)1183 X11::getTextProperty(Window win, Atom atom, std::string &value)
1184 {
1185 	// Read text property, return if it fails.
1186 	XTextProperty text_property;
1187 	if (! XGetTextProperty(_dpy, win, &text_property, atom)
1188 	    || ! text_property.value || ! text_property.nitems) {
1189 		return false;
1190 	}
1191 
1192 	if (text_property.encoding == XA_STRING) {
1193 		value = reinterpret_cast<const char*>(text_property.value);
1194 	} else {
1195 		char **mb_list;
1196 		int num;
1197 
1198 		XmbTextPropertyToTextList(_dpy, &text_property, &mb_list, &num);
1199 		if (mb_list && num > 0) {
1200 			value = *mb_list;
1201 			XFreeStringList(mb_list);
1202 		}
1203 	}
1204 
1205 	X11::free(text_property.value);
1206 
1207 	return true;
1208 }
1209 
1210 void*
getEwmhPropData(Window win,AtomName prop,Atom type,int & num)1211 X11::getEwmhPropData(Window win, AtomName prop, Atom type, int &num)
1212 {
1213 	Atom type_ret;
1214 	int format_ret;
1215 	ulong items_ret, after_ret;
1216 	uchar *prop_data = 0;
1217 
1218 	XGetWindowProperty(_dpy, win, _atoms[prop], 0, 0x7fffffff,
1219 			   False, type, &type_ret, &format_ret, &items_ret,
1220 			   &after_ret, &prop_data);
1221 	num = items_ret;
1222 	return prop_data;
1223 }
1224 
1225 void
unsetProperty(Window win,AtomName aname)1226 X11::unsetProperty(Window win, AtomName aname)
1227 {
1228 	if (_dpy) {
1229 		XDeleteProperty(_dpy, win, _atoms[aname]);
1230 	}
1231 }
1232 
1233 void
getMousePosition(int & x,int & y)1234 X11::getMousePosition(int &x, int &y)
1235 {
1236 	Window d_root, d_win;
1237 	int win_x, win_y;
1238 	uint mask;
1239 
1240 	XQueryPointer(_dpy, _root, &d_root, &d_win, &x, &y, &win_x, &win_y, &mask);
1241 }
1242 
1243 uint
getButtonFromState(uint state)1244 X11::getButtonFromState(uint state)
1245 {
1246 	uint button = 0;
1247 
1248 	if (state&Button1Mask)
1249 		button = BUTTON1;
1250 	else if (state&Button2Mask)
1251 		button = BUTTON2;
1252 	else if (state&Button3Mask)
1253 		button = BUTTON3;
1254 	else if (state&Button4Mask)
1255 		button = BUTTON4;
1256 	else if (state&Button5Mask)
1257 		button = BUTTON5;
1258 
1259 	return button;
1260 }
1261 
1262 int
sendEvent(Window dest,Window win,Atom atom,long mask,long v1,long v2,long v3,long v4,long v5)1263 X11::sendEvent(Window dest, Window win, Atom atom, long mask,
1264                long v1, long v2, long v3, long v4, long v5)
1265 {
1266 	XEvent e;
1267 	e.type = e.xclient.type = ClientMessage;
1268 	e.xclient.serial = 0;
1269 	e.xclient.send_event = True;
1270 	e.xclient.message_type = atom;
1271 	e.xclient.window = win;
1272 	e.xclient.format = 32;
1273 
1274 	e.xclient.data.l[0] = v1;
1275 	e.xclient.data.l[1] = v2;
1276 	e.xclient.data.l[2] = v3;
1277 	e.xclient.data.l[3] = v4;
1278 	e.xclient.data.l[4] = v5;
1279 
1280 	return sendEvent(dest, False, mask, &e);
1281 }
1282 
1283 int
sendEvent(Window dest,Bool propagate,long mask,XEvent * ev)1284 X11::sendEvent(Window dest, Bool propagate, long mask, XEvent *ev)
1285 {
1286 	if (_dpy) {
1287 		return XSendEvent(_dpy, dest, propagate, mask, ev);
1288 	}
1289 	return BadValue;
1290 }
1291 
1292 void
setCardinal(Window win,AtomName aname,Cardinal value,long format)1293 X11::setCardinal(Window win, AtomName aname, Cardinal value, long format)
1294 {
1295 	changeProperty(win, _atoms[aname], format, 32,
1296 		       PropModeReplace, reinterpret_cast<uchar*>(&value), 1);
1297 }
1298 
1299 int
changeProperty(Window win,Atom prop,Atom type,int format,int mode,const unsigned char * data,int num_e)1300 X11::changeProperty(Window win, Atom prop, Atom type, int format,
1301                     int mode, const unsigned char *data, int num_e)
1302 {
1303 	if (_dpy) {
1304 		return XChangeProperty(_dpy, win, prop, type, format, mode,
1305 				       data, num_e);
1306 	}
1307 	return BadImplementation;
1308 }
1309 
1310 int
getGeometry(Window win,unsigned * w,unsigned * h,unsigned * bw)1311 X11::getGeometry(Window win, unsigned *w, unsigned *h, unsigned *bw)
1312 {
1313 	Window wn;
1314 	int x, y;
1315 	unsigned int depth_return;
1316 	if (_dpy) {
1317 		return XGetGeometry(_dpy, win, &wn, &x, &y,
1318 				    w, h, bw, &depth_return);
1319 	}
1320 	return BadImplementation;
1321 }
1322 
1323 bool
getWindowAttributes(Window win,XWindowAttributes * wa)1324 X11::getWindowAttributes(Window win, XWindowAttributes *wa)
1325 {
1326 	if (_dpy) {
1327 		return XGetWindowAttributes(_dpy, win, wa);
1328 	}
1329 	return BadImplementation;
1330 }
1331 
1332 GC
createGC(Drawable d,ulong mask,XGCValues * values)1333 X11::createGC(Drawable d, ulong mask, XGCValues *values)
1334 {
1335 	if (_dpy) {
1336 		return XCreateGC(_dpy, d, mask, values);
1337 	}
1338 	return None;
1339 }
1340 
1341 void
freeGC(GC gc)1342 X11::freeGC(GC gc)
1343 {
1344 	if (_dpy) {
1345 		XFreeGC(_dpy, gc);
1346 	}
1347 }
1348 
1349 Pixmap
createPixmapMask(unsigned w,unsigned h)1350 X11::createPixmapMask(unsigned w, unsigned h)
1351 {
1352 	if (_dpy) {
1353 		return XCreatePixmap(_dpy, _root, w, h, 1);
1354 	}
1355 	return None;
1356 }
1357 
1358 Pixmap
createPixmap(unsigned w,unsigned h)1359 X11::createPixmap(unsigned w, unsigned h)
1360 {
1361 	if (_dpy) {
1362 		return XCreatePixmap(_dpy, _root, w, h, _depth);
1363 	}
1364 	return None;
1365 }
1366 
1367 void
freePixmap(Pixmap & pixmap)1368 X11::freePixmap(Pixmap& pixmap)
1369 {
1370 	if (_dpy && pixmap != None) {
1371 		XFreePixmap(_dpy, pixmap);
1372 	}
1373 	pixmap = None;
1374 }
1375 
1376 XImage*
createImage(char * data,uint width,uint height)1377 X11::createImage(char *data, uint width, uint height)
1378 {
1379 	if (_dpy) {
1380 		return XCreateImage(_dpy, _visual, 24, ZPixmap,
1381 				    0, data, width, height, 32, 0);
1382 	}
1383 	return nullptr;
1384 }
1385 
1386 XImage*
getImage(Drawable src,int x,int y,uint width,uint height,unsigned long plane_mask,int format)1387 X11::getImage(Drawable src, int x, int y, uint width, uint height,
1388               unsigned long plane_mask, int format)
1389 {
1390 	if (_dpy) {
1391 		return XGetImage(_dpy, src, x, y, width, height,
1392 				 plane_mask, format);
1393 	}
1394 	return nullptr;
1395 }
1396 
1397 void
putImage(Drawable dest,GC gc,XImage * ximage,int src_x,int src_y,int dest_x,int dest_y,uint width,uint height)1398 X11::putImage(Drawable dest, GC gc, XImage *ximage,
1399               int src_x, int src_y, int dest_x, int dest_y,
1400               uint width, uint height)
1401 {
1402 	if (_dpy) {
1403 		XPutImage(_dpy, dest, gc, ximage,
1404 			  src_x, src_y, dest_x, dest_y, width, height);
1405 	}
1406 }
1407 
1408 void
destroyImage(XImage * ximage)1409 X11::destroyImage(XImage *ximage)
1410 {
1411 	if (ximage) {
1412 		XDestroyImage(ximage);
1413 	}
1414 }
1415 
1416 void
setWindowBackground(Window window,ulong pixel)1417 X11::setWindowBackground(Window window, ulong pixel)
1418 {
1419 	if (_dpy) {
1420 		XSetWindowBackground(_dpy, window, pixel);
1421 	}
1422 }
1423 
1424 void
setWindowBackgroundPixmap(Window window,Pixmap pixmap)1425 X11::setWindowBackgroundPixmap(Window window, Pixmap pixmap)
1426 {
1427 	if (_dpy) {
1428 		XSetWindowBackgroundPixmap(_dpy, window, pixmap);
1429 	}
1430 }
1431 
1432 void
clearWindow(Window window)1433 X11::clearWindow(Window window)
1434 {
1435 	if (_dpy) {
1436 		XClearWindow(_dpy, window);
1437 	}
1438 }
1439 
1440 void
clearArea(Window window,int x,int y,uint width,uint height)1441 X11::clearArea(Window window, int x, int y, uint width, uint height)
1442 {
1443 	if (_dpy) {
1444 		XClearArea(_dpy, window, x, y, width, height, False);
1445 	}
1446 }
1447 
1448 #ifdef PEKWM_HAVE_SHAPE
1449 void
shapeSelectInput(Window window,ulong mask)1450 X11::shapeSelectInput(Window window, ulong mask)
1451 {
1452 	if (_dpy) {
1453 		XShapeSelectInput(_dpy, window, mask);
1454 	}
1455 }
1456 
1457 void
shapeQuery(Window dst,int * bshaped)1458 X11::shapeQuery(Window dst, int *bshaped)
1459 {
1460 	int foo; unsigned bar;
1461 	XShapeQueryExtents(_dpy, dst, bshaped, &foo, &foo, &bar, &bar,
1462 			   &foo, &foo, &foo, &bar, &bar);
1463 }
1464 
1465 void
shapeCombine(Window dst,int kind,int x,int y,Window src,int op)1466 X11::shapeCombine(Window dst, int kind, int x, int y,
1467                   Window src, int op)
1468 {
1469 	XShapeCombineShape(_dpy, dst, kind, x, y, src, kind, op);
1470 }
1471 
1472 void
shapeSetRect(Window dst,XRectangle * rect)1473 X11::shapeSetRect(Window dst, XRectangle *rect)
1474 {
1475 	XShapeCombineRectangles(_dpy, dst, ShapeBounding, 0, 0, rect, 1,
1476 				ShapeSet, YXBanded);
1477 }
1478 
1479 void
shapeIntersectRect(Window dst,XRectangle * rect)1480 X11::shapeIntersectRect(Window dst, XRectangle *rect)
1481 {
1482 	XShapeCombineRectangles(_dpy, dst, ShapeBounding, 0, 0, rect, 1,
1483 				ShapeIntersect, YXBanded);
1484 }
1485 
1486 void
shapeSetMask(Window dst,int kind,Pixmap pix)1487 X11::shapeSetMask(Window dst, int kind, Pixmap pix)
1488 {
1489 	XShapeCombineMask(_dpy, dst, kind, 0, 0, pix, ShapeSet);
1490 }
1491 
1492 XRectangle*
shapeGetRects(Window win,int kind,int * num)1493 X11::shapeGetRects(Window win, int kind, int *num)
1494 {
1495 	int ordering;
1496 	return XShapeGetRectangles(_dpy, win, kind, num, &ordering);
1497 }
1498 #else // ! PEKWM_HAVE_SHAPE
1499 void
shapeSelectInput(Window window,ulong mask)1500 X11::shapeSelectInput(Window window, ulong mask)
1501 {
1502 }
1503 
1504 void
shapeQuery(Window dst,int * bshaped)1505 X11::shapeQuery(Window dst, int *bshaped)
1506 {
1507 	*bshaped = 0;
1508 }
1509 
1510 void
shapeCombine(Window dst,int kind,int x,int y,Window src,int op)1511 X11::shapeCombine(Window dst, int kind, int x, int y,
1512                   Window src, int op)
1513 {
1514 }
1515 
1516 void
shapeSetRect(Window dst,XRectangle * rect)1517 X11::shapeSetRect(Window dst, XRectangle *rect)
1518 {
1519 }
1520 
1521 void
shapeIntersectRect(Window dst,XRectangle * rect)1522 X11::shapeIntersectRect(Window dst, XRectangle *rect)
1523 {
1524 }
1525 
1526 void
shapeSetMask(Window dst,int kind,Pixmap pix)1527 X11::shapeSetMask(Window dst, int kind, Pixmap pix)
1528 {
1529 }
1530 
1531 XRectangle*
shapeGetRects(Window win,int kind,int * num)1532 X11::shapeGetRects(Window win, int kind, int *num)
1533 {
1534 	num = 0;
1535 	return nullptr;
1536 }
1537 #endif // PEKWM_HAVE_SHAPE
1538 
1539 //! @brief Initialize head information
1540 void
initHeads(void)1541 X11::initHeads(void)
1542 {
1543 	_heads.clear();
1544 
1545 	// Read head information, randr has priority over xinerama then
1546 	// comes ordinary X11 information.
1547 
1548 	initHeadsRandr();
1549 	if (! _heads.size()) {
1550 		initHeadsXinerama();
1551 
1552 		if (! _heads.size()) {
1553 			addHead(Head(0, 0, _screen_gm.width, _screen_gm.height));
1554 		}
1555 	}
1556 }
1557 
1558 //! @brief Initialize head information from Xinerama
1559 void
initHeadsXinerama(void)1560 X11::initHeadsXinerama(void)
1561 {
1562 #ifdef PEKWM_HAVE_XINERAMA
1563 	// Check if there are heads already initialized from example Randr
1564 	if (! XineramaIsActive(_dpy)) {
1565 		return;
1566 	}
1567 
1568 	int num_heads = 0;
1569 	XineramaScreenInfo *infos = XineramaQueryScreens(_dpy, &num_heads);
1570 
1571 	for (int i = 0; i < num_heads; ++i) {
1572 		addHead(Head(infos[i].x_org, infos[i].y_org,
1573 			     infos[i].width, infos[i].height));
1574 	}
1575 
1576 	X11::free(infos);
1577 #endif // PEKWM_HAVE_XINERAMA
1578 }
1579 
1580 //! @brief Initialize head information from RandR
1581 void
initHeadsRandr(void)1582 X11::initHeadsRandr(void)
1583 {
1584 #ifdef PEKWM_HAVE_XRANDR
1585 	if (! _honour_randr || ! _has_extension_xrandr) {
1586 		return;
1587 	}
1588 
1589 	XRRScreenResources *resources = XRRGetScreenResources(_dpy, _root);
1590 	if (! resources) {
1591 		return;
1592 	}
1593 
1594 	for (int i = 0; i < resources->noutput; ++i) {
1595 		XRROutputInfo* output =
1596 			XRRGetOutputInfo(_dpy, resources, resources->outputs[i]);
1597 		if (output->crtc) {
1598 			XRRCrtcInfo* crtc = XRRGetCrtcInfo(_dpy, resources, output->crtc);
1599 			addHead(Head(crtc->x, crtc->y, crtc->width, crtc->height));
1600 			XRRFreeCrtcInfo (crtc);
1601 		}
1602 		XRRFreeOutputInfo (output);
1603 	}
1604 
1605 	XRRFreeScreenResources (resources);
1606 #endif // PEKWM_HAVE_XRANDR
1607 }
1608 
1609 // gets the squared distance between 2 points
1610 uint
calcDistance(int x1,int y1,int x2,int y2)1611 X11::calcDistance(int x1, int y1, int x2, int y2)
1612 {
1613 	return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
1614 }
1615 
1616 // gets the squared distance between 2 points with either x or y the same
1617 uint
calcDistance(int p1,int p2)1618 X11::calcDistance(int p1, int p2)
1619 {
1620 	return (p1 - p2) * (p1 - p2);
1621 }
1622 
1623 /**
1624  * Lookup mask from keycode.
1625  *
1626  * @param keycode KeyCode to lookup.
1627  * @return Mask for keycode, 0 if something fails.
1628  */
1629 uint
getMaskFromKeycode(KeyCode keycode)1630 X11::getMaskFromKeycode(KeyCode keycode)
1631 {
1632 	// Make sure modifier mappings were looked up ok
1633 	if (! _modifier_map || _modifier_map->max_keypermod < 1) {
1634 		return 0;
1635 	}
1636 
1637 	// .h files states that modifiermap is an 8 * max_keypermod array.
1638 	int max_info = _modifier_map->max_keypermod * 8;
1639 	for (int i = 0; i < max_info; ++i) {
1640 		if (_modifier_map->modifiermap[i] == keycode) {
1641 			return MODIFIER_TO_MASK[i / _modifier_map->max_keypermod];
1642 		}
1643 	}
1644 
1645 	return 0;
1646 }
1647 
1648 /**
1649  * Figure out what key you can press to generate mask
1650  *
1651  * @param mask Modifier mask to get keycode for.
1652  * @return KeyCode for mask, 0 if failing.
1653  */
1654 KeyCode
getKeycodeFromMask(uint mask)1655 X11::getKeycodeFromMask(uint mask)
1656 {
1657 	// Make sure modifier mappings were looked up ok
1658 	if (! _modifier_map || _modifier_map->max_keypermod < 1) {
1659 		return 0;
1660 	}
1661 
1662 	for (int i = 0; i < 8; ++i) {
1663 		if (MODIFIER_TO_MASK[i] == mask) {
1664 			// FIXME: Is iteration over the range required?
1665 			return _modifier_map->modifiermap[i * _modifier_map->max_keypermod];
1666 		}
1667 	}
1668 
1669 	return 0;
1670 }
1671 
1672 /**
1673  * Wrapper for XKeycodeToKeysym and XkbKeycodeToKeysym depending on
1674  * which one is available.
1675  */
1676 #ifdef __GNUG__
1677 #ifdef PEKWM_HAVE_GCC_DIAGNOSTICS_PUSH
1678 #pragma GCC diagnostic push
1679 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1680 #endif // PEKWM_HAVE_GCC_DIAGNOSTICS_PUSH
1681 #else // ! __GNUG__
1682 #ifdef __SUNPRO_CC
1683 #pragma error_messages (off,symdeprecated)
1684 #endif // __SUNPRO_CC
1685 #endif // __GNUG__
1686 KeySym
getKeysymFromKeycode(KeyCode keycode)1687 X11::getKeysymFromKeycode(KeyCode keycode)
1688 {
1689 #ifdef PEKWM_HAVE_X11_XKBLIB_H
1690 	if (_has_extension_xkb)
1691 		return XkbKeycodeToKeysym(_dpy, keycode, 0, 0);
1692 	else
1693 #endif
1694 		return XKeycodeToKeysym(_dpy, keycode, 0);
1695 }
1696 #ifdef __GNUG__
1697 #ifdef PEKWM_HAVE_GCC_DIAGNOSTICS_PUSH
1698 #pragma GCC diagnostic pop
1699 #endif // PEKWM_HAVE_GCC_DIAGNOSTICS_PUSH
1700 #endif // __GNUG__
1701 
1702 void
removeMotionEvents(void)1703 X11::removeMotionEvents(void)
1704 {
1705 	XEvent xev;
1706 	while (XCheckMaskEvent(_dpy, PointerMotionMask, &xev))
1707 		;
1708 }
1709 
1710 /**
1711  * Parse string and set on geometry, same format as XParseGeometry
1712  * however both size and position can be given in percent.
1713  *
1714  * Format: [=][<width>{xX}<height>][{+-}<xoffset>{+-}<yoffset>]
1715  */
1716 int
parseGeometry(const std::string & str,Geometry & gm)1717 X11::parseGeometry(const std::string& str, Geometry& gm)
1718 {
1719 	int mask = 0;
1720 	if (str.size() < 3) {
1721 		// no valid geometry can fit in less than 3 characters.
1722 		return mask;
1723 	}
1724 
1725 	// skip initial = if given
1726 	const char *cstr = str.c_str();
1727 	std::string::size_type s_start = str[0] == '=' ? 1 : 0;
1728 	std::string::size_type s_end = str.find_first_of("+-", s_start);
1729 
1730 	int ret;
1731 	if (s_end == std::string::npos) {
1732 		s_end = str.size();
1733 	} else {
1734 		// position
1735 		std::string::size_type y_start = str.find_first_of("+-", s_end + 1);
1736 		if ((ret = parseGeometryVal(cstr + s_end + 1,
1737 					    cstr + y_start, gm.x)) > 0) {
1738 			mask |= X_VALUE;
1739 			if (str[s_end] == '-') {
1740 				mask |= X_NEGATIVE;
1741 			}
1742 			if (ret == 2) {
1743 				mask |= X_PERCENT;
1744 			}
1745 		}
1746 		if ((ret = parseGeometryVal(cstr + y_start + 1,
1747 					    cstr + str.size(), gm.y)) > 0) {
1748 			mask |= Y_VALUE;
1749 			if (str[y_start] == '-') {
1750 				mask |= Y_NEGATIVE;
1751 			}
1752 			if (ret == 2) {
1753 				mask |= Y_PERCENT;
1754 			}
1755 		}
1756 	}
1757 
1758 	if (s_end > s_start) {
1759 		// size
1760 		int width, height;
1761 		std::string::size_type h_start = str.find_first_of("xX", s_start);
1762 		if ((ret = parseGeometryVal(cstr + s_start, cstr + h_start, width)) > 0
1763 		    && width > 0) {
1764 			gm.width = width;
1765 			mask |= WIDTH_VALUE;
1766 			if (ret == 2) {
1767 				mask |= WIDTH_PERCENT;
1768 			}
1769 		}
1770 		if ((ret = parseGeometryVal(cstr + h_start + 1,
1771 					    cstr + s_end, height)) > 0
1772 		    && height > 0) {
1773 			gm.height = height;
1774 			mask |= HEIGHT_VALUE;
1775 			if (ret == 2) {
1776 				mask |= HEIGHT_PERCENT;
1777 			}
1778 		}
1779 	}
1780 
1781 	return mask;
1782 }
1783 
1784 int
parseGeometryVal(const char * cstr,const char * e_end,int & val)1785 X11::parseGeometryVal(const char *cstr, const char *e_end, int &val)
1786 {
1787 	char *end = 0;
1788 	val = strtoll(cstr, &end, 10);
1789 	if (*end == '%') {
1790 		if (val < 0 || val > 100) {
1791 			return 0;
1792 		}
1793 		return 2;
1794 	}
1795 	return end == e_end ? 1 : 0;
1796 }
1797 
1798 void
keepVisible(Geometry & gm)1799 X11::keepVisible(Geometry &gm)
1800 {
1801 	if (gm.x > static_cast<int>(getWidth()) - 3) {
1802 		gm.x = getWidth() - 3;
1803 	}
1804 	if (gm.x + static_cast<int>(gm.width) < 3) {
1805 		gm.x = 3 - gm.width;
1806 	}
1807 	if (gm.y > static_cast<int>(getHeight()) - 3) {
1808 		gm.y = getHeight() - 3;
1809 	}
1810 	if (gm.y + static_cast<int>(gm.height) < 3) {
1811 		gm.y = 3 - gm.height;
1812 	}
1813 }
1814 
1815 Window
createWindow(Window parent,int x,int y,uint width,uint height,uint border_width,uint depth,uint _class,Visual * visual,ulong valuemask,XSetWindowAttributes * attrs)1816 X11::createWindow(Window parent,
1817                   int x, int y, uint width, uint height,
1818                   uint border_width, uint depth, uint _class,
1819                   Visual* visual, ulong valuemask,
1820                   XSetWindowAttributes* attrs)
1821 {
1822 	if (_dpy) {
1823 		return XCreateWindow(_dpy, parent,
1824 				     x, y, width, height, border_width,
1825 				     depth, _class, visual, valuemask, attrs);
1826 	}
1827 	return None;
1828 }
1829 
1830 Window
createSimpleWindow(Window parent,int x,int y,uint width,uint height,uint border_width,ulong border,ulong background)1831 X11::createSimpleWindow(Window parent,
1832                         int x, int y, uint width, uint height,
1833                         uint border_width,
1834                         ulong border, ulong background)
1835 {
1836 	if (_dpy) {
1837 		return XCreateSimpleWindow(_dpy, parent, x, y, width, height,
1838 					   border_width, border, background);
1839 	}
1840 	return None;
1841 }
1842 
1843 void
destroyWindow(Window win)1844 X11::destroyWindow(Window win)
1845 {
1846 	if (_dpy) {
1847 		XDestroyWindow(_dpy, win);
1848 	}
1849 }
1850 
1851 void
changeWindowAttributes(Window win,ulong mask,XSetWindowAttributes & attrs)1852 X11::changeWindowAttributes(Window win, ulong mask,
1853                             XSetWindowAttributes &attrs)
1854 {
1855 	if (_dpy) {
1856 		XChangeWindowAttributes(_dpy, win, mask, &attrs);
1857 	}
1858 }
1859 
1860 void
grabButton(unsigned b,unsigned int mod,Window win,unsigned mask,int mode)1861 X11::grabButton(unsigned b, unsigned int mod, Window win,
1862                 unsigned mask, int mode)
1863 {
1864 	XGrabButton(_dpy, b, mod, win, true, mask, mode,
1865 		    GrabModeAsync, None, None);
1866 }
1867 
1868 void
mapWindow(Window w)1869 X11::mapWindow(Window w)
1870 {
1871 	if (_dpy) {
1872 		XMapWindow(_dpy, w);
1873 	}
1874 }
1875 
1876 void
mapRaised(Window w)1877 X11::mapRaised(Window w)
1878 {
1879 	if (_dpy) {
1880 		XMapRaised(_dpy, w);
1881 	}
1882 }
1883 
1884 void
unmapWindow(Window w)1885 X11::unmapWindow(Window w)
1886 {
1887 	if (_dpy) {
1888 		XUnmapWindow(_dpy, w);
1889 	}
1890 }
1891 
1892 void
reparentWindow(Window w,Window parent,int x,int y)1893 X11::reparentWindow(Window w, Window parent, int x, int y)
1894 {
1895 	if (_dpy) {
1896 		XReparentWindow(_dpy, w, parent, x, y);
1897 	}
1898 }
1899 
1900 void
raiseWindow(Window w)1901 X11::raiseWindow(Window w)
1902 {
1903 	if (_dpy) {
1904 		XRaiseWindow(_dpy, w);
1905 	}
1906 }
1907 
1908 void
lowerWindow(Window w)1909 X11::lowerWindow(Window w)
1910 {
1911 	if (_dpy) {
1912 		XLowerWindow(_dpy, w);
1913 	}
1914 }
1915 
1916 void
ungrabButton(uint button,uint modifiers,Window win)1917 X11::ungrabButton(uint button, uint modifiers, Window win)
1918 {
1919 	XUngrabButton(_dpy, button, modifiers, win);
1920 }
1921 
1922 /**
1923  * Wrapper for XRestackWindows, windows go in top-to-bottom order.
1924  */
1925 void
stackWindows(Window * wins,unsigned len)1926 X11::stackWindows(Window *wins, unsigned len)
1927 {
1928 	if (len > 1) {
1929 		XRestackWindows(_dpy, wins, len);
1930 	}
1931 }
1932 
1933 bool
checkTypedEvent(int type,XEvent * ev)1934 X11::checkTypedEvent(int type, XEvent *ev)
1935 {
1936 	return XCheckTypedEvent(_dpy, type, ev);
1937 }
1938 
1939 void
sync(Bool discard)1940 X11::sync(Bool discard)
1941 {
1942 	if (_dpy) {
1943 		XSync(X11::getDpy(), discard);
1944 	}
1945 }
1946 
1947 int
selectInput(Window w,long mask)1948 X11::selectInput(Window w, long mask)
1949 {
1950 	if (_dpy) {
1951 		return XSelectInput(_dpy, w, mask);
1952 	}
1953 	return 0;
1954 }
1955 
1956 void
setInputFocus(Window w)1957 X11::setInputFocus(Window w)
1958 {
1959 	XSetInputFocus(_dpy, w, RevertToPointerRoot, CurrentTime);
1960 }
1961 
1962 Display *X11::_dpy;
1963 bool X11::_honour_randr = false;
1964 int X11::_fd = -1;
1965 int X11::_screen = -1;
1966 int X11::_depth = -1;
1967 Geometry X11::_screen_gm;
1968 Window X11::_root = None;
1969 Visual *X11::_visual = 0;
1970 GC X11::_gc = None;
1971 Colormap X11::_colormap = None;
1972 XModifierKeymap *X11::_modifier_map;
1973 bool X11::_has_extension_shape = false;
1974 int X11::_event_shape = -1;
1975 bool X11::_has_extension_xkb = false;
1976 bool X11::_has_extension_xinerama = false;
1977 bool X11::_has_extension_xrandr = false;
1978 int X11::_event_xrandr = -1;
1979 uint X11::_num_lock;
1980 uint X11::_scroll_lock;
1981 std::vector<Head> X11::_heads;
1982 uint X11::_server_grabs;
1983 Time X11::_last_event_time;
1984 Window X11::_last_click_id = None;
1985 Time X11::_last_click_time[BUTTON_NO - 1];
1986 std::vector<X11::ColorEntry*> X11::_colors;
1987 XColor X11::_xc_default;
1988 Cursor X11::_cursor_map[CURSOR_NONE];
1989