1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 2012, 2013 Martin Gräßlin <mgraesslin@kde.org>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program. If not, see <http://www.gnu.org/licenses/>.
19*********************************************************************/
20#ifndef KWIN_XCB_UTILS_H
21#define KWIN_XCB_UTILS_H
22
23#include <kwinglobals.h>
24#include "utils.h"
25
26#include <QRect>
27#include <QRegion>
28#include <QVector>
29
30#include <xcb/xcb.h>
31#include <xcb/composite.h>
32
33namespace KWin {
34
35namespace Xcb {
36
37typedef xcb_window_t WindowId;
38
39// forward declaration of methods
40static void defineCursor(xcb_window_t window, xcb_cursor_t cursor);
41static void setInputFocus(xcb_window_t window, uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime());
42static void moveWindow(xcb_window_t window, const QPoint &pos);
43static void moveWindow(xcb_window_t window, uint32_t x, uint32_t y);
44
45template <typename Reply,
46 typename Cookie,
47 Reply *(*replyFunc)(xcb_connection_t*, Cookie, xcb_generic_error_t**),
48 Cookie (*requestFunc)(xcb_connection_t*, xcb_window_t)>
49class Wrapper
50{
51public:
52 Wrapper()
53 : m_retrieved(false)
54 , m_window(XCB_WINDOW_NONE)
55 , m_reply(NULL)
56 {
57 m_cookie.sequence = 0;
58 }
59 explicit Wrapper(WindowId window)
60 : m_retrieved(false)
61 , m_cookie(requestFunc(connection(), window))
62 , m_window(window)
63 , m_reply(NULL)
64 {
65 }
66 explicit Wrapper(const Wrapper &other)
67 : m_retrieved(other.m_retrieved)
68 , m_cookie(other.m_cookie)
69 , m_window(other.m_window)
70 , m_reply(NULL)
71 {
72 takeFromOther(const_cast<Wrapper&>(other));
73 }
74 virtual ~Wrapper() {
75 cleanup();
76 }
77 inline Wrapper &operator=(const Wrapper &other) {
78 if (this != &other) {
79 // if we had managed a reply, free it
80 cleanup();
81 // copy members
82 m_retrieved = other.m_retrieved;
83 m_cookie = other.m_cookie;
84 m_window = other.m_window;
85 m_reply = other.m_reply;
86 // take over the responsibility for the reply pointer
87 takeFromOther(const_cast<Wrapper&>(other));
88 }
89 return *this;
90 }
91
92 inline const Reply *operator->() {
93 getReply();
94 return m_reply;
95 }
96 inline bool isNull() {
97 getReply();
98 return m_reply == NULL;
99 }
100 inline operator bool() {
101 return !isNull();
102 }
103 inline const Reply *data() {
104 getReply();
105 return m_reply;
106 }
107 inline WindowId window() const {
108 return m_window;
109 }
110 inline bool isRetrieved() const {
111 return m_retrieved;
112 }
113 /**
114 * Returns the value of the reply pointer referenced by this object. The reply pointer of
115 * this object will be reset to null. Calling any method which requires the reply to be valid
116 * will crash.
117 *
118 * Callers of this function take ownership of the pointer.
119 **/
120 inline Reply *take() {
121 getReply();
122 Reply *ret = m_reply;
123 m_reply = NULL;
124 m_window = XCB_WINDOW_NONE;
125 return ret;
126 }
127
128protected:
129 void getReply() {
130 if (m_retrieved || !m_cookie.sequence) {
131 return;
132 }
133 m_reply = replyFunc(connection(), m_cookie, NULL);
134 m_retrieved = true;
135 }
136
137private:
138 inline void cleanup() {
139 if (!m_retrieved && m_cookie.sequence) {
140 xcb_discard_reply(connection(), m_cookie.sequence);
141 } else if (m_reply) {
142 free(m_reply);
143 }
144 }
145 inline void takeFromOther(Wrapper &other) {
146 if (m_retrieved) {
147 m_reply = other.take();
148 } else {
149 //ensure that other object doesn't try to get the reply or discards it in the dtor
150 other.m_retrieved = true;
151 other.m_window = XCB_WINDOW_NONE;
152 }
153 }
154 bool m_retrieved;
155 Cookie m_cookie;
156 WindowId m_window;
157 Reply *m_reply;
158};
159
160typedef Wrapper<xcb_get_window_attributes_reply_t, xcb_get_window_attributes_cookie_t, &xcb_get_window_attributes_reply, &xcb_get_window_attributes_unchecked> WindowAttributes;
161typedef Wrapper<xcb_composite_get_overlay_window_reply_t, xcb_composite_get_overlay_window_cookie_t, &xcb_composite_get_overlay_window_reply, &xcb_composite_get_overlay_window_unchecked> OverlayWindow;
162
163
164class WindowGeometry : public Wrapper<xcb_get_geometry_reply_t, xcb_get_geometry_cookie_t, &xcb_get_geometry_reply, &xcb_get_geometry_unchecked>
165{
166public:
167 WindowGeometry() : Wrapper<xcb_get_geometry_reply_t, xcb_get_geometry_cookie_t, &xcb_get_geometry_reply, &xcb_get_geometry_unchecked>() {}
168 explicit WindowGeometry(xcb_window_t window) : Wrapper<xcb_get_geometry_reply_t, xcb_get_geometry_cookie_t, &xcb_get_geometry_reply, &xcb_get_geometry_unchecked>(window) {}
169
170 inline QRect rect() {
171 const xcb_get_geometry_reply_t *geometry = data();
172 if (!geometry) {
173 return QRect();
174 }
175 return QRect(geometry->x, geometry->y, geometry->width, geometry->height);
176 }
177};
178
179class Tree : public Wrapper<xcb_query_tree_reply_t, xcb_query_tree_cookie_t, &xcb_query_tree_reply, &xcb_query_tree_unchecked>
180{
181public:
182 explicit Tree(WindowId window) : Wrapper<xcb_query_tree_reply_t, xcb_query_tree_cookie_t, &xcb_query_tree_reply, &xcb_query_tree_unchecked>(window) {}
183
184 inline WindowId *children() {
185 return xcb_query_tree_children(data());
186 }
187 inline xcb_window_t parent() {
188 if (isNull())
189 return XCB_WINDOW_NONE;
190 return (*this)->parent;
191 }
192};
193
194inline xcb_get_input_focus_cookie_t get_input_focus(xcb_connection_t *c, xcb_window_t) {
195 return xcb_get_input_focus(c);
196}
197class CurrentInput : public Wrapper<xcb_get_input_focus_reply_t, xcb_get_input_focus_cookie_t, &xcb_get_input_focus_reply, &get_input_focus>
198{
199public:
200 CurrentInput() : Wrapper<xcb_get_input_focus_reply_t, xcb_get_input_focus_cookie_t, &xcb_get_input_focus_reply, &get_input_focus>() {}
201
202 inline xcb_window_t window() {
203 if (isNull())
204 return XCB_WINDOW_NONE;
205 return (*this)->focus;
206 }
207};
208
209class ExtensionData
210{
211public:
212 ExtensionData();
213 int version;
214 int eventBase;
215 int errorBase;
216 int majorOpcode;
217 bool present;
218 QByteArray name;
219};
220
221class Extensions
222{
223public:
224 bool isShapeAvailable() const {
225 return m_shape.version > 0;
226 }
227 bool isShapeInputAvailable() const;
228 int shapeNotifyEvent() const;
229 bool hasShape(xcb_window_t w) const;
230 bool isRandrAvailable() const {
231 return m_randr.present;
232 }
233 int randrNotifyEvent() const;
234 bool isDamageAvailable() const {
235 return m_damage.present;
236 }
237 int damageNotifyEvent() const;
238 bool isCompositeAvailable() const {
239 return m_composite.version > 0;
240 }
241 bool isCompositeOverlayAvailable() const;
242 bool isRenderAvailable() const {
243 return m_render.version > 0;
244 }
245 bool isFixesAvailable() const {
246 return m_fixes.version > 0;
247 }
248 bool isFixesRegionAvailable() const;
249 bool isSyncAvailable() const {
250 return m_sync.present;
251 }
252 int syncAlarmNotifyEvent() const;
253 QVector<ExtensionData> extensions() const;
254
255 static Extensions *self();
256 static void destroy();
257private:
258 Extensions();
259 ~Extensions();
260 void init();
261 template <typename reply, typename T, typename F>
262 void initVersion(T cookie, F f, ExtensionData *dataToFill);
263 void extensionQueryReply(const xcb_query_extension_reply_t *extension, ExtensionData *dataToFill);
264
265 ExtensionData m_shape;
266 ExtensionData m_randr;
267 ExtensionData m_damage;
268 ExtensionData m_composite;
269 ExtensionData m_render;
270 ExtensionData m_fixes;
271 ExtensionData m_sync;
272
273 static Extensions *s_self;
274};
275
276/**
277 * This class is an RAII wrapper for an xcb_window_t. An xcb_window_t hold by an instance of this class
278 * will be freed when the instance gets destroyed.
279 *
280 * Furthermore the class provides wrappers around some xcb methods operating on an xcb_window_t.
281 **/
282class Window
283{
284public:
285 /**
286 * Takes over responsibility of @p window. If @p window is not provided an invalid Window is
287 * created. Use @link create to set an xcb_window_t later on.
288 * @param window The window to manage.
289 **/
290 Window(xcb_window_t window = XCB_WINDOW_NONE);
291 /**
292 * Creates an xcb_window_t and manages it. It's a convenient method to create a window with
293 * depth, class and visual being copied from parent and border being @c 0.
294 * @param geometry The geometry for the window to be created
295 * @param mask The mask for the values
296 * @param values The values to be passed to xcb_create_window
297 * @param parent The parent window
298 **/
299 Window(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow());
300 /**
301 * Creates an xcb_window_t and manages it. It's a convenient method to create a window with
302 * depth and visual being copied from parent and border being @c 0.
303 * @param geometry The geometry for the window to be created
304 * @param class The window class
305 * @param mask The mask for the values
306 * @param values The values to be passed to xcb_create_window
307 * @param parent The parent window
308 **/
309 Window(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow());
310 ~Window();
311
312 /**
313 * Creates a new window for which the responsibility is taken over. If a window had been managed
314 * before it is freed.
315 *
316 * Depth, class and visual are being copied from parent and border is @c 0.
317 * @param geometry The geometry for the window to be created
318 * @param mask The mask for the values
319 * @param values The values to be passed to xcb_create_window
320 * @param parent The parent window
321 **/
322 void create(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow());
323 /**
324 * Creates a new window for which the responsibility is taken over. If a window had been managed
325 * before it is freed.
326 *
327 * Depth and visual are being copied from parent and border is @c 0.
328 * @param geometry The geometry for the window to be created
329 * @param class The window class
330 * @param mask The mask for the values
331 * @param values The values to be passed to xcb_create_window
332 * @param parent The parent window
333 **/
334 void create(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow());
335 /**
336 * Frees the existing window and starts to manage the new @p window.
337 **/
338 void reset(xcb_window_t window = XCB_WINDOW_NONE);
339 /**
340 * @returns @c true if a window is managed, @c false otherwise.
341 **/
342 bool isValid() const;
343 /**
344 * Configures the window with a new geometry.
345 * @param geometry The new window geometry to be used
346 **/
347 void setGeometry(const QRect &geometry);
348 void setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height);
349 void move(const QPoint &pos);
350 void move(uint32_t x, uint32_t y);
351 void resize(const QSize &size);
352 void resize(uint32_t width, uint32_t height);
353 void raise();
354 void map();
355 void unmap();
356 /**
357 * Clears the window area. Same as xcb_clear_area with x, y, width, height being @c 0.
358 **/
359 void clear();
360 void setBackgroundPixmap(xcb_pixmap_t pixmap);
361 void defineCursor(xcb_cursor_t cursor);
362 void focus(uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime());
363 operator xcb_window_t() const;
364private:
365 Window(const Window &other);
366 xcb_window_t doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow());
367 void destroy();
368 xcb_window_t m_window;
369};
370
371inline
372Window::Window(xcb_window_t window)
373 : m_window(window)
374{
375}
376
377inline
378Window::Window(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent)
379 : m_window(doCreate(geometry, XCB_COPY_FROM_PARENT, mask, values, parent))
380{
381}
382
383inline
384Window::Window(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent)
385 : m_window(doCreate(geometry, windowClass, mask, values, parent))
386{
387}
388
389inline
390Window::~Window()
391{
392 destroy();
393}
394
395inline
396void Window::destroy()
397{
398 if (!isValid()) {
399 return;
400 }
401 xcb_destroy_window(connection(), m_window);
402 m_window = XCB_WINDOW_NONE;
403}
404
405inline
406bool Window::isValid() const
407{
408 return m_window != XCB_WINDOW_NONE;
409}
410
411inline
412Window::operator xcb_window_t() const
413{
414 return m_window;
415}
416
417inline
418void Window::create(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent)
419{
420 destroy();
421 m_window = doCreate(geometry, windowClass, mask, values, parent);
422}
423
424inline
425void Window::create(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent)
426{
427 create(geometry, XCB_COPY_FROM_PARENT, mask, values, parent);
428}
429
430inline
431xcb_window_t Window::doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent)
432{
433 xcb_window_t w = xcb_generate_id(connection());
434 xcb_create_window(connection(), XCB_COPY_FROM_PARENT, w, parent,
435 geometry.x(), geometry.y(), geometry.width(), geometry.height(),
436 0, windowClass, XCB_COPY_FROM_PARENT, mask, values);
437 return w;
438}
439
440inline
441void Window::reset(xcb_window_t window)
442{
443 destroy();
444 m_window = window;
445}
446
447inline
448void Window::setGeometry(const QRect &geometry)
449{
450 setGeometry(geometry.x(), geometry.y(), geometry.width(), geometry.height());
451}
452
453inline
454void Window::setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
455{
456 if (!isValid()) {
457 return;
458 }
459 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
460 const uint32_t values[] = { x, y, width, height };
461 xcb_configure_window(connection(), m_window, mask, values);
462}
463
464inline
465void Window::move(const QPoint &pos)
466{
467 move(pos.x(), pos.y());
468}
469
470inline
471void Window::move(uint32_t x, uint32_t y)
472{
473 if (!isValid()) {
474 return;
475 }
476 moveWindow(m_window, x, y);
477}
478
479inline
480void Window::resize(const QSize &size)
481{
482 resize(size.width(), size.height());
483}
484
485inline
486void Window::resize(uint32_t width, uint32_t height)
487{
488 if (!isValid()) {
489 return;
490 }
491 const uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
492 const uint32_t values[] = { width, height };
493 xcb_configure_window(connection(), m_window, mask, values);
494}
495
496inline
497void Window::raise()
498{
499 const uint32_t values[] = { XCB_STACK_MODE_ABOVE };
500 xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_STACK_MODE, values);
501}
502
503inline
504void Window::map()
505{
506 if (!isValid()) {
507 return;
508 }
509 xcb_map_window(connection(), m_window);
510}
511
512inline
513void Window::unmap()
514{
515 if (!isValid()) {
516 return;
517 }
518 xcb_unmap_window(connection(), m_window);
519}
520
521inline
522void Window::clear()
523{
524 if (!isValid()) {
525 return;
526 }
527 xcb_clear_area(connection(), false, m_window, 0, 0, 0, 0);
528}
529
530inline
531void Window::setBackgroundPixmap(xcb_pixmap_t pixmap)
532{
533 if (!isValid()) {
534 return;
535 }
536 const uint32_t values[] = {pixmap};
537 xcb_change_window_attributes(connection(), m_window, XCB_CW_BACK_PIXMAP, values);
538}
539
540inline
541void Window::defineCursor(xcb_cursor_t cursor)
542{
543 Xcb::defineCursor(m_window, cursor);
544}
545
546inline
547void Window::focus(uint8_t revertTo, xcb_timestamp_t time)
548{
549 setInputFocus(m_window, revertTo, time);
550}
551
552// helper functions
553static inline void moveResizeWindow(WindowId window, const QRect &geometry)
554{
555 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
556 const uint32_t values[] = {
557 static_cast<uint32_t>(geometry.x()),
558 static_cast<uint32_t>(geometry.y()),
559 static_cast<uint32_t>(geometry.width()),
560 static_cast<uint32_t>(geometry.height())
561 };
562 xcb_configure_window(connection(), window, mask, values);
563}
564
565static inline void moveWindow(xcb_window_t window, const QPoint& pos)
566{
567 moveWindow(window, pos.x(), pos.y());
568}
569
570static inline void moveWindow(xcb_window_t window, uint32_t x, uint32_t y)
571{
572 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
573 const uint32_t values[] = { x, y };
574 xcb_configure_window(connection(), window, mask, values);
575}
576
577static inline WindowId createInputWindow(const QRect &geometry, uint32_t mask, const uint32_t *values)
578{
579 WindowId window = xcb_generate_id(connection());
580 xcb_create_window(connection(), 0, window, rootWindow(),
581 geometry.x(), geometry.y(), geometry.width(), geometry.height(),
582 0, XCB_WINDOW_CLASS_INPUT_ONLY,
583 XCB_COPY_FROM_PARENT, mask, values);
584 return window;
585}
586
587static inline void restackWindows(const QVector<xcb_window_t> &windows)
588{
589 if (windows.count() < 2) {
590 // only one window, nothing to do
591 return;
592 }
593 for (int i=1; i<windows.count(); ++i) {
594 const uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
595 const uint32_t stackingValues[] = {
596 windows.at(i-1),
597 XCB_STACK_MODE_BELOW
598 };
599 xcb_configure_window(connection(), windows.at(i), mask, stackingValues);
600 }
601}
602
603static inline void restackWindowsWithRaise(const QVector<xcb_window_t> &windows)
604{
605 if (windows.isEmpty()) {
606 return;
607 }
608 const uint32_t values[] = { XCB_STACK_MODE_ABOVE };
609 xcb_configure_window(connection(), windows.first(), XCB_CONFIG_WINDOW_STACK_MODE, values);
610 restackWindows(windows);
611}
612
613static inline int defaultDepth()
614{
615 static int depth = 0;
616 if (depth != 0) {
617 return depth;
618 }
619 int screen = QX11Info::appScreen();
620 for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(connection()));
621 it.rem;
622 --screen, xcb_screen_next(&it)) {
623 if (screen == 0) {
624 depth = it.data->root_depth;
625 break;
626 }
627 }
628 return depth;
629}
630
631static inline xcb_rectangle_t fromQt(const QRect &rect)
632{
633 xcb_rectangle_t rectangle;
634 rectangle.x = rect.x();
635 rectangle.y = rect.y();
636 rectangle.width = rect.width();
637 rectangle.height = rect.height();
638 return rectangle;
639}
640
641static inline QVector<xcb_rectangle_t> regionToRects(const QRegion &region)
642{
643 const QVector<QRect> regionRects = region.rects();
644 QVector<xcb_rectangle_t> rects(regionRects.count());
645 for (int i=0; i<regionRects.count(); ++i) {
646 rects[i] = Xcb::fromQt(regionRects.at(i));
647 }
648 return rects;
649}
650
651static inline void defineCursor(xcb_window_t window, xcb_cursor_t cursor)
652{
653 xcb_change_window_attributes(connection(), window, XCB_CW_CURSOR, &cursor);
654}
655
656static inline void setInputFocus(xcb_window_t window, uint8_t revertTo, xcb_timestamp_t time)
657{
658 xcb_set_input_focus(connection(), revertTo, window, time);
659}
660
661} // namespace X11
662
663} // namespace KWin
664#endif // KWIN_X11_UTILS_H
665