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