Merge remote-tracking branch 'origin/master' into api_changes
[profile/ivi/qtbase.git] / src / plugins / platforms / xcb / qxcbwindow.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qxcbwindow.h"
43
44 #include <QtDebug>
45 #include <QScreen>
46
47 #include "qxcbconnection.h"
48 #include "qxcbscreen.h"
49 #include "qxcbdrag.h"
50 #include "qxcbkeyboard.h"
51 #include "qxcbwmsupport.h"
52
53 #include <qplatformintegration_qpa.h>
54
55 #ifdef XCB_USE_DRI2
56 #include "qdri2context.h"
57 #endif
58
59 // FIXME This workaround can be removed for xcb-icccm > 3.8
60 #define class class_name
61 #include <xcb/xcb_icccm.h>
62 #undef class
63 #include <xcb/xfixes.h>
64
65 // xcb-icccm 3.8 support
66 #ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
67 #define xcb_get_wm_hints_reply xcb_icccm_get_wm_hints_reply
68 #define xcb_get_wm_hints xcb_icccm_get_wm_hints
69 #define xcb_get_wm_hints_unchecked xcb_icccm_get_wm_hints_unchecked
70 #define xcb_set_wm_hints xcb_icccm_set_wm_hints
71 #define xcb_set_wm_normal_hints xcb_icccm_set_wm_normal_hints
72 #define xcb_size_hints_set_base_size xcb_icccm_size_hints_set_base_size
73 #define xcb_size_hints_set_max_size xcb_icccm_size_hints_set_max_size
74 #define xcb_size_hints_set_min_size xcb_icccm_size_hints_set_min_size
75 #define xcb_size_hints_set_position xcb_icccm_size_hints_set_position
76 #define xcb_size_hints_set_resize_inc xcb_icccm_size_hints_set_resize_inc
77 #define xcb_size_hints_set_size xcb_icccm_size_hints_set_size
78 #define xcb_size_hints_set_win_gravity xcb_icccm_size_hints_set_win_gravity
79 #define xcb_wm_hints_set_iconic xcb_icccm_wm_hints_set_iconic
80 #define xcb_wm_hints_set_normal xcb_icccm_wm_hints_set_normal
81 #define xcb_wm_hints_set_input xcb_icccm_wm_hints_set_input
82 #define xcb_wm_hints_t xcb_icccm_wm_hints_t
83 #define XCB_WM_STATE_ICONIC XCB_ICCCM_WM_STATE_ICONIC
84 #define XCB_WM_STATE_WITHDRAWN XCB_ICCCM_WM_STATE_WITHDRAWN
85 #endif
86
87 #include <private/qguiapplication_p.h>
88 #include <private/qwindow_p.h>
89
90 #include <QtGui/QPlatformBackingStore>
91 #include <QtGui/QWindowSystemInterface>
92
93 #include <stdio.h>
94
95 #ifdef XCB_USE_XLIB
96 #include <X11/Xlib.h>
97 #include <X11/Xutil.h>
98 #endif
99
100 #ifdef XCB_USE_XINPUT2_MAEMO
101 #include <X11/extensions/XInput2.h>
102 #endif
103
104 #if defined(XCB_USE_GLX)
105 #include "qglxintegration.h"
106 #include <QtPlatformSupport/private/qglxconvenience_p.h>
107 #elif defined(XCB_USE_EGL)
108 #include "qxcbeglsurface.h"
109 #include <QtPlatformSupport/private/qeglconvenience_p.h>
110 #include <QtPlatformSupport/private/qxlibeglintegration_p.h>
111 #endif
112
113 #define XCOORD_MAX 16383
114
115 //#ifdef NET_WM_STATE_DEBUG
116
117 QT_BEGIN_NAMESPACE
118
119 // Returns true if we should set WM_TRANSIENT_FOR on \a w
120 static inline bool isTransient(const QWindow *w)
121 {
122     return w->windowType() == Qt::Dialog
123            || w->windowType() == Qt::Sheet
124            || w->windowType() == Qt::Tool
125            || w->windowType() == Qt::SplashScreen
126            || w->windowType() == Qt::ToolTip
127            || w->windowType() == Qt::Drawer
128            || w->windowType() == Qt::Popup;
129 }
130
131 static inline QImage::Format imageFormatForDepth(int depth)
132 {
133     switch (depth) {
134         case 32: return QImage::Format_ARGB32_Premultiplied;
135         case 24: return QImage::Format_RGB32;
136         case 16: return QImage::Format_RGB16;
137         default: return QImage::Format_Invalid;
138     }
139 }
140
141 QXcbWindow::QXcbWindow(QWindow *window)
142     : QPlatformWindow(window)
143     , m_window(0)
144     , m_syncCounter(0)
145     , m_mapped(false)
146     , m_transparent(false)
147     , m_deferredActivation(false)
148     , m_netWmUserTimeWindow(XCB_NONE)
149 #if defined(XCB_USE_EGL)
150     , m_eglSurface(0)
151 #endif
152     , m_lastWindowStateEvent(-1)
153 {
154     m_screen = static_cast<QXcbScreen *>(window->screen()->handle());
155
156     setConnection(m_screen->connection());
157
158     create();
159 }
160
161 void QXcbWindow::create()
162 {
163     destroy();
164
165     m_deferredExpose = false;
166     m_configureNotifyPending = true;
167     m_windowState = Qt::WindowNoState;
168
169     Qt::WindowType type = window()->windowType();
170
171     if (type == Qt::Desktop) {
172         m_window = m_screen->root();
173         m_depth = m_screen->screen()->root_depth;
174         m_imageFormat = imageFormatForDepth(m_depth);
175         connection()->addWindow(m_window, this);
176         return;
177     }
178
179     const quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
180     const quint32 values[] = {
181         // XCB_CW_BACK_PIXMAP
182         XCB_NONE,
183         // XCB_CW_OVERRIDE_REDIRECT
184         type == Qt::Popup || type == Qt::ToolTip,
185         // XCB_CW_SAVE_UNDER
186         type == Qt::Popup || type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer,
187         // XCB_CW_EVENT_MASK
188         XCB_EVENT_MASK_EXPOSURE
189         | XCB_EVENT_MASK_STRUCTURE_NOTIFY
190         | XCB_EVENT_MASK_KEY_PRESS
191         | XCB_EVENT_MASK_KEY_RELEASE
192         | XCB_EVENT_MASK_BUTTON_PRESS
193         | XCB_EVENT_MASK_BUTTON_RELEASE
194         | XCB_EVENT_MASK_BUTTON_MOTION
195         | XCB_EVENT_MASK_ENTER_WINDOW
196         | XCB_EVENT_MASK_LEAVE_WINDOW
197         | XCB_EVENT_MASK_POINTER_MOTION
198         | XCB_EVENT_MASK_PROPERTY_CHANGE
199         | XCB_EVENT_MASK_FOCUS_CHANGE
200     };
201
202     QRect rect = window()->geometry();
203     QPlatformWindow::setGeometry(rect);
204
205     rect.setWidth(qBound(1, rect.width(), XCOORD_MAX));
206     rect.setHeight(qBound(1, rect.height(), XCOORD_MAX));
207
208     xcb_window_t xcb_parent_id = m_screen->root();
209     if (parent())
210         xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
211
212     m_format = window()->requestedFormat();
213
214 #if (defined(XCB_USE_GLX) || defined(XCB_USE_EGL)) && defined(XCB_USE_XLIB)
215     if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)
216         || m_format.hasAlpha())
217     {
218 #if defined(XCB_USE_GLX)
219         XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), &m_format);
220         if (!visualInfo)
221             qFatal("Could not initialize GLX");
222 #elif defined(XCB_USE_EGL)
223         EGLDisplay eglDisplay = connection()->egl_display();
224         EGLConfig eglConfig = q_configFromGLFormat(eglDisplay, m_format, true);
225         m_format = q_glFormatFromConfig(eglDisplay, eglConfig);
226
227         VisualID id = QXlibEglIntegration::getCompatibleVisualId(DISPLAY_FROM_XCB(this), eglDisplay, eglConfig);
228
229         XVisualInfo visualInfoTemplate;
230         memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
231         visualInfoTemplate.visualid = id;
232
233         XVisualInfo *visualInfo;
234         int matchingCount = 0;
235         visualInfo = XGetVisualInfo(DISPLAY_FROM_XCB(this), VisualIDMask, &visualInfoTemplate, &matchingCount);
236         if (!visualInfo)
237             qFatal("Could not initialize EGL");
238 #endif //XCB_USE_GLX
239         m_depth = visualInfo->depth;
240         m_imageFormat = imageFormatForDepth(m_depth);
241         Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(this), xcb_parent_id, visualInfo->visual, AllocNone);
242
243         XSetWindowAttributes a;
244         a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
245         a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(this), m_screen->screenNumber());
246         a.colormap = cmap;
247
248         m_visualId = visualInfo->visualid;
249
250         m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(),
251                                   0, visualInfo->depth, InputOutput, visualInfo->visual,
252                                   CWBackPixel|CWBorderPixel|CWColormap, &a);
253
254         XFree(visualInfo);
255     } else
256 #endif //defined(XCB_USE_GLX) || defined(XCB_USE_EGL)
257     {
258         m_window = xcb_generate_id(xcb_connection());
259         m_depth = m_screen->screen()->root_depth;
260         m_imageFormat = imageFormatForDepth(m_depth);
261         m_visualId = m_screen->screen()->root_visual;
262
263         Q_XCB_CALL(xcb_create_window(xcb_connection(),
264                                      XCB_COPY_FROM_PARENT,            // depth -- same as root
265                                      m_window,                        // window id
266                                      xcb_parent_id,                   // parent window id
267                                      rect.x(),
268                                      rect.y(),
269                                      rect.width(),
270                                      rect.height(),
271                                      0,                               // border width
272                                      XCB_WINDOW_CLASS_INPUT_OUTPUT,   // window class
273                                      m_visualId,                      // visual
274                                      0,                               // value mask
275                                      0));                             // value list
276     }
277
278     connection()->addWindow(m_window, this);
279
280     Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values));
281
282     propagateSizeHints();
283
284     xcb_atom_t properties[4];
285     int propertyCount = 0;
286     properties[propertyCount++] = atom(QXcbAtom::WM_DELETE_WINDOW);
287     properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS);
288     properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING);
289
290     if (m_screen->syncRequestSupported())
291         properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST);
292
293     if (window()->windowFlags() & Qt::WindowContextHelpButtonHint)
294         properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP);
295
296     Q_XCB_CALL(xcb_change_property(xcb_connection(),
297                                    XCB_PROP_MODE_REPLACE,
298                                    m_window,
299                                    atom(QXcbAtom::WM_PROTOCOLS),
300                                    XCB_ATOM_ATOM,
301                                    32,
302                                    propertyCount,
303                                    properties));
304     m_syncValue.hi = 0;
305     m_syncValue.lo = 0;
306
307     if (m_screen->syncRequestSupported()) {
308         m_syncCounter = xcb_generate_id(xcb_connection());
309         Q_XCB_CALL(xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue));
310
311         Q_XCB_CALL(xcb_change_property(xcb_connection(),
312                                        XCB_PROP_MODE_REPLACE,
313                                        m_window,
314                                        atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
315                                        XCB_ATOM_CARDINAL,
316                                        32,
317                                        1,
318                                        &m_syncCounter));
319     }
320
321     // set the PID to let the WM kill the application if unresponsive
322     long pid = getpid();
323     Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
324                                    atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
325                                    1, &pid));
326
327     xcb_wm_hints_t hints;
328     memset(&hints, 0, sizeof(hints));
329     xcb_wm_hints_set_normal(&hints);
330
331     xcb_wm_hints_set_input(&hints, !(window()->windowFlags() & Qt::WindowDoesNotAcceptFocus));
332
333     xcb_set_wm_hints(xcb_connection(), m_window, &hints);
334
335     xcb_window_t leader = m_screen->clientLeader();
336     Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
337                                    atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
338                                    1, &leader));
339
340 #ifdef XCB_USE_XINPUT2_MAEMO
341     if (connection()->isUsingXInput2()) {
342         XIEventMask xieventmask;
343         uchar bitmask[2] = { 0, 0 };
344
345         xieventmask.deviceid = XIAllMasterDevices;
346         xieventmask.mask = bitmask;
347         xieventmask.mask_len = sizeof(bitmask);
348
349         XISetMask(bitmask, XI_ButtonPress);
350         XISetMask(bitmask, XI_ButtonRelease);
351         XISetMask(bitmask, XI_Motion);
352
353         XISelectEvents(DISPLAY_FROM_XCB(this), m_window, &xieventmask, 1);
354     }
355 #endif
356
357     setWindowFlags(window()->windowFlags());
358     setWindowTitle(window()->windowTitle());
359     setWindowState(window()->windowState());
360
361     if (window()->windowFlags() & Qt::WindowTransparentForInput)
362         setTransparentForMouseEvents(true);
363
364     connection()->drag()->dndEnable(this, true);
365 }
366
367 QXcbWindow::~QXcbWindow()
368 {
369     destroy();
370 }
371
372 void QXcbWindow::destroy()
373 {
374     if (m_syncCounter && m_screen->syncRequestSupported())
375         Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter));
376     if (m_window) {
377         if (m_netWmUserTimeWindow) {
378             xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
379             // Some window managers, like metacity, do XSelectInput on the _NET_WM_USER_TIME_WINDOW window,
380             // without trapping BadWindow (which crashes when the user time window is destroyed).
381             connection()->sync();
382             xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow);
383             m_netWmUserTimeWindow = XCB_NONE;
384         }
385         connection()->removeWindow(m_window);
386         Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window));
387     }
388     m_mapped = false;
389
390 #if defined(XCB_USE_EGL)
391     delete m_eglSurface;
392     m_eglSurface = 0;
393 #endif
394 }
395
396 void QXcbWindow::setGeometry(const QRect &rect)
397 {
398     QPlatformWindow::setGeometry(rect);
399
400     propagateSizeHints();
401
402     const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
403     const qint32 values[] = {
404         qBound<qint32>(-XCOORD_MAX, rect.x(),      XCOORD_MAX),
405         qBound<qint32>(-XCOORD_MAX, rect.y(),      XCOORD_MAX),
406         qBound<qint32>(1,           rect.width(),  XCOORD_MAX),
407         qBound<qint32>(1,           rect.height(), XCOORD_MAX),
408     };
409
410     Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
411
412     xcb_flush(xcb_connection());
413 }
414
415 QMargins QXcbWindow::frameMargins() const
416 {
417     if (m_dirtyFrameMargins) {
418         xcb_window_t window = m_window;
419         xcb_window_t parent = m_window;
420
421         bool foundRoot = false;
422
423         const QVector<xcb_window_t> &virtualRoots =
424             connection()->wmSupport()->virtualRoots();
425
426         while (!foundRoot) {
427             xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked(xcb_connection(), parent);
428
429             xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, NULL);
430             if (reply) {
431                 if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1) {
432                     foundRoot = true;
433                 } else {
434                     window = parent;
435                     parent = reply->parent;
436                 }
437
438                 free(reply);
439             } else {
440                 m_dirtyFrameMargins = false;
441                 m_frameMargins = QMargins();
442                 return m_frameMargins;
443             }
444         }
445
446         QPoint offset;
447
448         xcb_translate_coordinates_reply_t *reply =
449             xcb_translate_coordinates_reply(
450                 xcb_connection(),
451                 xcb_translate_coordinates(xcb_connection(), window, parent, 0, 0),
452                 NULL);
453
454         if (reply) {
455             offset = QPoint(reply->dst_x, reply->dst_y);
456             free(reply);
457         }
458
459         xcb_get_geometry_reply_t *geom =
460             xcb_get_geometry_reply(
461                 xcb_connection(),
462                 xcb_get_geometry(xcb_connection(), parent),
463                 NULL);
464
465         if (geom) {
466             // --
467             // add the border_width for the window managers frame... some window managers
468             // do not use a border_width of zero for their frames, and if we the left and
469             // top strut, we ensure that pos() is absolutely correct.  frameGeometry()
470             // will still be incorrect though... perhaps i should have foffset as well, to
471             // indicate the frame offset (equal to the border_width on X).
472             // - Brad
473             // -- copied from qwidget_x11.cpp
474
475             int left = offset.x() + geom->border_width;
476             int top = offset.y() + geom->border_width;
477             int right = geom->width + geom->border_width - geometry().width() - offset.x();
478             int bottom = geom->height + geom->border_width - geometry().height() - offset.y();
479
480             m_frameMargins = QMargins(left, top, right, bottom);
481
482             free(geom);
483         }
484
485         m_dirtyFrameMargins = false;
486     }
487
488     return m_frameMargins;
489 }
490
491 void QXcbWindow::setVisible(bool visible)
492 {
493     if (visible)
494         show();
495     else
496         hide();
497 }
498
499 void QXcbWindow::show()
500 {
501     if (window()->isTopLevel()) {
502         xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
503
504         xcb_wm_hints_t hints;
505         xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL);
506
507         if (window()->windowState() & Qt::WindowMinimized)
508             xcb_wm_hints_set_iconic(&hints);
509         else
510             xcb_wm_hints_set_normal(&hints);
511
512         xcb_wm_hints_set_input(&hints, !(window()->windowFlags() & Qt::WindowDoesNotAcceptFocus));
513
514         xcb_set_wm_hints(xcb_connection(), m_window, &hints);
515
516         // update WM_NORMAL_HINTS
517         propagateSizeHints();
518
519         // update WM_TRANSIENT_FOR
520         if (window()->transientParent() && isTransient(window())) {
521             QXcbWindow *transientXcbParent = static_cast<QXcbWindow *>(window()->transientParent()->handle());
522             if (transientXcbParent) {
523                 // ICCCM 4.1.2.6
524                 xcb_window_t parentWindow = transientXcbParent->xcb_window();
525
526                 // todo: set transient for group (wm_client_leader) if no parent, a la qwidget_x11.cpp
527                 Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
528                                                XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
529                                                1, &parentWindow));
530             }
531         }
532
533         // update _MOTIF_WM_HINTS
534         updateMotifWmHintsBeforeMap();
535
536         // update _NET_WM_STATE
537         updateNetWmStateBeforeMap();
538     }
539
540     if (connection()->time() != XCB_TIME_CURRENT_TIME)
541         updateNetWmUserTime(connection()->time());
542
543     Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
544     xcb_flush(xcb_connection());
545
546     connection()->sync();
547 }
548
549 void QXcbWindow::hide()
550 {
551     Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window));
552
553     // send synthetic UnmapNotify event according to icccm 4.1.4
554     xcb_unmap_notify_event_t event;
555     event.response_type = XCB_UNMAP_NOTIFY;
556     event.event = m_screen->root();
557     event.window = m_window;
558     event.from_configure = false;
559     Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_screen->root(),
560                               XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
561
562     xcb_flush(xcb_connection());
563
564     m_mapped = false;
565 }
566
567 struct QtMotifWmHints {
568     quint32 flags, functions, decorations;
569     qint32 input_mode;
570     quint32 status;
571 };
572
573 enum {
574     MWM_HINTS_FUNCTIONS   = (1L << 0),
575
576     MWM_FUNC_ALL      = (1L << 0),
577     MWM_FUNC_RESIZE   = (1L << 1),
578     MWM_FUNC_MOVE     = (1L << 2),
579     MWM_FUNC_MINIMIZE = (1L << 3),
580     MWM_FUNC_MAXIMIZE = (1L << 4),
581     MWM_FUNC_CLOSE    = (1L << 5),
582
583     MWM_HINTS_DECORATIONS = (1L << 1),
584
585     MWM_DECOR_ALL      = (1L << 0),
586     MWM_DECOR_BORDER   = (1L << 1),
587     MWM_DECOR_RESIZEH  = (1L << 2),
588     MWM_DECOR_TITLE    = (1L << 3),
589     MWM_DECOR_MENU     = (1L << 4),
590     MWM_DECOR_MINIMIZE = (1L << 5),
591     MWM_DECOR_MAXIMIZE = (1L << 6),
592
593     MWM_HINTS_INPUT_MODE = (1L << 2),
594
595     MWM_INPUT_MODELESS                  = 0L,
596     MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L,
597     MWM_INPUT_FULL_APPLICATION_MODAL    = 3L
598 };
599
600 static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
601 {
602     QtMotifWmHints hints;
603
604     xcb_get_property_cookie_t get_cookie =
605         xcb_get_property_unchecked(c->xcb_connection(), 0, window, c->atom(QXcbAtom::_MOTIF_WM_HINTS),
606                          c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
607
608     xcb_get_property_reply_t *reply =
609         xcb_get_property_reply(c->xcb_connection(), get_cookie, NULL);
610
611     if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) {
612         hints = *((QtMotifWmHints *)xcb_get_property_value(reply));
613     } else {
614         hints.flags = 0L;
615         hints.functions = MWM_FUNC_ALL;
616         hints.decorations = MWM_DECOR_ALL;
617         hints.input_mode = 0L;
618         hints.status = 0L;
619     }
620
621     free(reply);
622
623     return hints;
624 }
625
626 static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints)
627 {
628     if (hints.flags != 0l) {
629         Q_XCB_CALL2(xcb_change_property(c->xcb_connection(),
630                                        XCB_PROP_MODE_REPLACE,
631                                        window,
632                                        c->atom(QXcbAtom::_MOTIF_WM_HINTS),
633                                        c->atom(QXcbAtom::_MOTIF_WM_HINTS),
634                                        32,
635                                        5,
636                                        &hints), c);
637     } else {
638         Q_XCB_CALL2(xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS)), c);
639     }
640 }
641
642 QXcbWindow::NetWmStates QXcbWindow::netWmStates()
643 {
644     NetWmStates result(0);
645
646     xcb_get_property_cookie_t get_cookie =
647         xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
648                          XCB_ATOM_ATOM, 0, 1024);
649
650     xcb_get_property_reply_t *reply =
651         xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
652
653     if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
654         const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
655         const xcb_atom_t *statesEnd = states + reply->length;
656         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
657             result |= NetWmStateAbove;
658         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW)))
659             result |= NetWmStateBelow;
660         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
661             result |= NetWmStateFullScreen;
662         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
663             result |= NetWmStateMaximizedHorz;
664         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
665             result |= NetWmStateMaximizedVert;
666         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL)))
667             result |= NetWmStateModal;
668         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
669             result |= NetWmStateStaysOnTop;
670         if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
671             result |= NetWmStateDemandsAttention;
672         free(reply);
673     } else {
674 #ifdef NET_WM_STATE_DEBUG
675         printf("getting net wm state (%x), empty\n", m_window);
676 #endif
677     }
678
679     return result;
680 }
681
682 void QXcbWindow::setNetWmStates(NetWmStates states)
683 {
684     QVector<xcb_atom_t> atoms;
685     if (states & NetWmStateAbove)
686         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
687     if (states & NetWmStateBelow)
688         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW));
689     if (states & NetWmStateFullScreen)
690         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
691     if (states & NetWmStateMaximizedHorz)
692         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
693     if (states & NetWmStateMaximizedVert)
694         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
695     if (states & NetWmStateModal)
696         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL));
697     if (states & NetWmStateStaysOnTop)
698         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
699     if (states & NetWmStateDemandsAttention)
700         atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
701
702     if (atoms.isEmpty()) {
703         Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE)));
704     } else {
705         Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
706                                        atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
707                                        atoms.count(), atoms.constData()));
708     }
709     xcb_flush(xcb_connection());
710 }
711
712 Qt::WindowFlags QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
713 {
714     Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
715
716     if (type == Qt::ToolTip)
717         flags |= Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint;
718     if (type == Qt::Popup)
719         flags |= Qt::X11BypassWindowManagerHint;
720
721     if (flags & Qt::WindowTransparentForInput) {
722         uint32_t mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_VISIBILITY_CHANGE
723                  | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_RESIZE_REDIRECT
724                 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
725                 | XCB_EVENT_MASK_FOCUS_CHANGE  | XCB_EVENT_MASK_PROPERTY_CHANGE
726                 | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON;
727         xcb_change_window_attributes(xcb_connection(), xcb_window(), XCB_CW_EVENT_MASK, &mask);
728     }
729
730     setNetWmWindowFlags(flags);
731     setMotifWindowFlags(flags);
732
733     setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
734     updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
735
736     return flags;
737 }
738
739 void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags)
740 {
741     Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
742
743     QtMotifWmHints mwmhints;
744     mwmhints.flags = 0L;
745     mwmhints.functions = 0L;
746     mwmhints.decorations = 0;
747     mwmhints.input_mode = 0L;
748     mwmhints.status = 0L;
749
750     if (type != Qt::SplashScreen) {
751         mwmhints.flags |= MWM_HINTS_DECORATIONS;
752
753         bool customize = flags & Qt::CustomizeWindowHint;
754         if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) {
755             mwmhints.decorations |= MWM_DECOR_BORDER;
756             mwmhints.decorations |= MWM_DECOR_RESIZEH;
757
758             if (flags & Qt::WindowTitleHint)
759                 mwmhints.decorations |= MWM_DECOR_TITLE;
760
761             if (flags & Qt::WindowSystemMenuHint)
762                 mwmhints.decorations |= MWM_DECOR_MENU;
763
764             if (flags & Qt::WindowMinimizeButtonHint) {
765                 mwmhints.decorations |= MWM_DECOR_MINIMIZE;
766                 mwmhints.functions |= MWM_FUNC_MINIMIZE;
767             }
768
769             if (flags & Qt::WindowMaximizeButtonHint) {
770                 mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
771                 mwmhints.functions |= MWM_FUNC_MAXIMIZE;
772             }
773
774             if (flags & Qt::WindowCloseButtonHint)
775                 mwmhints.functions |= MWM_FUNC_CLOSE;
776         }
777     } else {
778         // if type == Qt::SplashScreen
779         mwmhints.decorations = MWM_DECOR_ALL;
780     }
781
782     if (mwmhints.functions != 0) {
783         mwmhints.flags |= MWM_HINTS_FUNCTIONS;
784         mwmhints.functions |= MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
785     } else {
786         mwmhints.functions = MWM_FUNC_ALL;
787     }
788
789     if (!(flags & Qt::FramelessWindowHint)
790         && flags & Qt::CustomizeWindowHint
791         && flags & Qt::WindowTitleHint
792         && !(flags &
793              (Qt::WindowMinimizeButtonHint
794               | Qt::WindowMaximizeButtonHint
795               | Qt::WindowCloseButtonHint)))
796     {
797         // a special case - only the titlebar without any button
798         mwmhints.flags = MWM_HINTS_FUNCTIONS;
799         mwmhints.functions = MWM_FUNC_MOVE | MWM_FUNC_RESIZE;
800         mwmhints.decorations = 0;
801     }
802
803     setMotifWmHints(connection(), m_window, mwmhints);
804 }
805
806 void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
807 {
808     xcb_client_message_event_t event;
809
810     event.response_type = XCB_CLIENT_MESSAGE;
811     event.format = 32;
812     event.window = m_window;
813     event.type = atom(QXcbAtom::_NET_WM_STATE);
814     event.data.data32[0] = set ? 1 : 0;
815     event.data.data32[1] = one;
816     event.data.data32[2] = two;
817     event.data.data32[3] = 0;
818     event.data.data32[4] = 0;
819
820     Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
821 }
822
823 Qt::WindowState QXcbWindow::setWindowState(Qt::WindowState state)
824 {
825     if (state == m_windowState)
826         return state;
827
828     // unset old state
829     switch (m_windowState) {
830     case Qt::WindowMinimized:
831         Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
832         break;
833     case Qt::WindowMaximized:
834         changeNetWmState(false,
835                          atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
836                          atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
837         break;
838     case Qt::WindowFullScreen:
839         changeNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
840         break;
841     default:
842         break;
843     }
844
845     // set new state
846     switch (state) {
847     case Qt::WindowMinimized:
848         {
849             xcb_client_message_event_t event;
850
851             event.response_type = XCB_CLIENT_MESSAGE;
852             event.format = 32;
853             event.window = m_window;
854             event.type = atom(QXcbAtom::WM_CHANGE_STATE);
855             event.data.data32[0] = XCB_WM_STATE_ICONIC;
856             event.data.data32[1] = 0;
857             event.data.data32[2] = 0;
858             event.data.data32[3] = 0;
859             event.data.data32[4] = 0;
860
861             Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
862         }
863         break;
864     case Qt::WindowMaximized:
865         changeNetWmState(true,
866                          atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
867                          atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
868         break;
869     case Qt::WindowFullScreen:
870         changeNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
871         break;
872     case Qt::WindowNoState:
873         break;
874     default:
875         break;
876     }
877
878     connection()->sync();
879
880     m_windowState = state;
881     return m_windowState;
882 }
883
884 void QXcbWindow::setNetWmWindowFlags(Qt::WindowFlags flags)
885 {
886     // in order of decreasing priority
887     QVector<uint> windowTypes;
888
889     Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
890
891     switch (type) {
892     case Qt::Dialog:
893     case Qt::Sheet:
894         windowTypes.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG));
895         break;
896     case Qt::Tool:
897     case Qt::Drawer:
898         windowTypes.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY));
899         break;
900     case Qt::ToolTip:
901         windowTypes.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP));
902         break;
903     case Qt::SplashScreen:
904         windowTypes.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH));
905         break;
906     default:
907         break;
908     }
909
910     if (flags & Qt::FramelessWindowHint)
911         windowTypes.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
912
913     windowTypes.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL));
914
915     Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
916                                    atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
917                                    windowTypes.count(), windowTypes.constData()));
918 }
919
920 void QXcbWindow::updateMotifWmHintsBeforeMap()
921 {
922     QtMotifWmHints mwmhints = getMotifWmHints(connection(), m_window);
923
924     if (window()->windowModality() != Qt::NonModal) {
925         switch (window()->windowModality()) {
926         case Qt::WindowModal:
927             mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL;
928             break;
929         case Qt::ApplicationModal:
930         default:
931             mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL;
932             break;
933         }
934         mwmhints.flags |= MWM_HINTS_INPUT_MODE;
935     } else {
936         mwmhints.input_mode = MWM_INPUT_MODELESS;
937         mwmhints.flags &= ~MWM_HINTS_INPUT_MODE;
938     }
939
940     if (window()->minimumSize() == window()->maximumSize()) {
941         // fixed size, remove the resize handle (since mwm/dtwm
942         // isn't smart enough to do it itself)
943         mwmhints.flags |= MWM_HINTS_FUNCTIONS;
944         if (mwmhints.functions == MWM_FUNC_ALL) {
945             mwmhints.functions = MWM_FUNC_MOVE;
946         } else {
947             mwmhints.functions &= ~MWM_FUNC_RESIZE;
948         }
949
950         if (mwmhints.decorations == MWM_DECOR_ALL) {
951             mwmhints.flags |= MWM_HINTS_DECORATIONS;
952             mwmhints.decorations = (MWM_DECOR_BORDER
953                                     | MWM_DECOR_TITLE
954                                     | MWM_DECOR_MENU);
955         } else {
956             mwmhints.decorations &= ~MWM_DECOR_RESIZEH;
957         }
958     }
959
960     if (window()->windowFlags() & Qt::WindowMinimizeButtonHint) {
961         mwmhints.flags |= MWM_HINTS_DECORATIONS;
962         mwmhints.decorations |= MWM_DECOR_MINIMIZE;
963         mwmhints.functions |= MWM_FUNC_MINIMIZE;
964     }
965     if (window()->windowFlags() & Qt::WindowMaximizeButtonHint) {
966         mwmhints.flags |= MWM_HINTS_DECORATIONS;
967         mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
968         mwmhints.functions |= MWM_FUNC_MAXIMIZE;
969     }
970     if (window()->windowFlags() & Qt::WindowCloseButtonHint)
971         mwmhints.functions |= MWM_FUNC_CLOSE;
972
973     setMotifWmHints(connection(), m_window, mwmhints);
974 }
975
976 void QXcbWindow::updateNetWmStateBeforeMap()
977 {
978     NetWmStates states(0);
979
980     const Qt::WindowFlags flags = window()->windowFlags();
981     if (flags & Qt::WindowStaysOnTopHint) {
982         states |= NetWmStateAbove;
983         states |= NetWmStateStaysOnTop;
984     } else if (flags & Qt::WindowStaysOnBottomHint) {
985         states |= NetWmStateBelow;
986     }
987
988     if (window()->windowState() & Qt::WindowFullScreen)
989         states |= NetWmStateFullScreen;
990
991     if (window()->windowState() & Qt::WindowMaximized) {
992         states |= NetWmStateMaximizedHorz;
993         states |= NetWmStateMaximizedVert;
994     }
995
996     if (window()->windowModality() != Qt::NonModal)
997         states |= NetWmStateModal;
998
999     setNetWmStates(states);
1000 }
1001
1002 void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
1003 {
1004     xcb_window_t wid = m_window;
1005
1006     const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
1007     if (m_netWmUserTimeWindow || isSupportedByWM) {
1008         if (!m_netWmUserTimeWindow) {
1009             m_netWmUserTimeWindow = xcb_generate_id(xcb_connection());
1010             Q_XCB_CALL(xcb_create_window(xcb_connection(),
1011                                          XCB_COPY_FROM_PARENT,            // depth -- same as root
1012                                          m_netWmUserTimeWindow,                        // window id
1013                                          m_window,                   // parent window id
1014                                          -1, -1, 1, 1,
1015                                          0,                               // border width
1016                                          XCB_WINDOW_CLASS_INPUT_OUTPUT,   // window class
1017                                          m_visualId,                      // visual
1018                                          0,                               // value mask
1019                                          0));                             // value list
1020             wid = m_netWmUserTimeWindow;
1021             xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
1022                                 XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow);
1023             xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
1024         } else if (!isSupportedByWM) {
1025             // WM no longer supports it, then we should remove the
1026             // _NET_WM_USER_TIME_WINDOW atom.
1027             xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
1028             xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow);
1029             m_netWmUserTimeWindow = XCB_NONE;
1030         } else {
1031             wid = m_netWmUserTimeWindow;
1032         }
1033     }
1034     xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::_NET_WM_USER_TIME),
1035                         XCB_ATOM_CARDINAL, 32, 1, &timestamp);
1036 }
1037
1038 void QXcbWindow::setTransparentForMouseEvents(bool transparent)
1039 {
1040     if (transparent == m_transparent)
1041         return;
1042
1043     xcb_rectangle_t rectangle;
1044
1045     xcb_rectangle_t *rect = 0;
1046     int nrect = 0;
1047
1048     if (!transparent) {
1049         rectangle.x = 0;
1050         rectangle.y = 0;
1051         rectangle.width = geometry().width();
1052         rectangle.height = geometry().height();
1053         rect = &rectangle;
1054         nrect = 1;
1055     }
1056
1057     xcb_xfixes_region_t region = xcb_generate_id(xcb_connection());
1058     xcb_xfixes_create_region(xcb_connection(), region, nrect, rect);
1059     xcb_xfixes_set_window_shape_region_checked(xcb_connection(), m_window, XCB_SHAPE_SK_INPUT, 0, 0, region);
1060     xcb_xfixes_destroy_region(xcb_connection(), region);
1061
1062     m_transparent = transparent;
1063 }
1064
1065 void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
1066 {
1067     xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
1068
1069     xcb_wm_hints_t hints;
1070     if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL)) {
1071         return;
1072     }
1073
1074     xcb_wm_hints_set_input(&hints, !doesNotAcceptFocus);
1075     xcb_set_wm_hints(xcb_connection(), m_window, &hints);
1076 }
1077
1078 WId QXcbWindow::winId() const
1079 {
1080     return m_window;
1081 }
1082
1083 void QXcbWindow::setParent(const QPlatformWindow *parent)
1084 {
1085     // re-create for compatibility
1086     create();
1087
1088     QPoint topLeft = geometry().topLeft();
1089
1090     xcb_window_t xcb_parent_id = parent ? static_cast<const QXcbWindow *>(parent)->xcb_window() : m_screen->root();
1091     Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y()));
1092 }
1093
1094 void QXcbWindow::setWindowTitle(const QString &title)
1095 {
1096     QByteArray ba = title.toUtf8();
1097     Q_XCB_CALL(xcb_change_property(xcb_connection(),
1098                                    XCB_PROP_MODE_REPLACE,
1099                                    m_window,
1100                                    atom(QXcbAtom::_NET_WM_NAME),
1101                                    atom(QXcbAtom::UTF8_STRING),
1102                                    8,
1103                                    ba.length(),
1104                                    ba.constData()));
1105 }
1106
1107 void QXcbWindow::raise()
1108 {
1109     const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1110     const quint32 values[] = { XCB_STACK_MODE_ABOVE };
1111     Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
1112 }
1113
1114 void QXcbWindow::lower()
1115 {
1116     const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
1117     const quint32 values[] = { XCB_STACK_MODE_BELOW };
1118     Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
1119 }
1120
1121 void QXcbWindow::propagateSizeHints()
1122 {
1123     // update WM_NORMAL_HINTS
1124     xcb_size_hints_t hints;
1125     memset(&hints, 0, sizeof(hints));
1126
1127     QRect rect = geometry();
1128
1129     QWindow *win = window();
1130
1131     xcb_size_hints_set_position(&hints, true, rect.x(), rect.y());
1132     xcb_size_hints_set_size(&hints, true, rect.width(), rect.height());
1133     xcb_size_hints_set_win_gravity(&hints, qt_window_private(win)->positionPolicy == QWindowPrivate::WindowFrameInclusive
1134                                            ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC);
1135
1136     QSize minimumSize = win->minimumSize();
1137     QSize maximumSize = win->maximumSize();
1138     QSize baseSize = win->baseSize();
1139     QSize sizeIncrement = win->sizeIncrement();
1140
1141     if (minimumSize.width() > 0 || minimumSize.height() > 0)
1142         xcb_size_hints_set_min_size(&hints, minimumSize.width(), minimumSize.height());
1143
1144     if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX)
1145         xcb_size_hints_set_max_size(&hints,
1146                                     qMin(XCOORD_MAX, maximumSize.width()),
1147                                     qMin(XCOORD_MAX, maximumSize.height()));
1148
1149     if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) {
1150         xcb_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height());
1151         xcb_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height());
1152     }
1153
1154     xcb_set_wm_normal_hints(xcb_connection(), m_window, &hints);
1155 }
1156
1157 void QXcbWindow::requestActivateWindow()
1158 {
1159     if (!m_mapped) {
1160         m_deferredActivation = true;
1161         return;
1162     }
1163     m_deferredActivation = false;
1164
1165     updateNetWmUserTime(connection()->time());
1166
1167     if (window()->isTopLevel()
1168         && connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_ACTIVE_WINDOW))) {
1169         xcb_client_message_event_t event;
1170
1171         event.response_type = XCB_CLIENT_MESSAGE;
1172         event.format = 32;
1173         event.window = m_window;
1174         event.type = atom(QXcbAtom::_NET_ACTIVE_WINDOW);
1175         event.data.data32[0] = 1;
1176         event.data.data32[1] = connection()->time();
1177         QWindow *focusWindow = QGuiApplication::focusWindow();
1178         event.data.data32[2] = focusWindow ? focusWindow->winId() : XCB_NONE;
1179         event.data.data32[3] = 0;
1180         event.data.data32[4] = 0;
1181
1182         Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
1183     } else {
1184         Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time()));
1185     }
1186
1187     connection()->sync();
1188 }
1189
1190 #if XCB_USE_MAEMO_WINDOW_PROPERTIES
1191 void QXcbWindow::setOrientation(Qt::ScreenOrientation orientation)
1192 {
1193     int angle = 0;
1194     switch (orientation) {
1195         case Qt::PortraitOrientation: angle = 270; break;
1196         case Qt::LandscapeOrientation: angle = 0; break;
1197         case Qt::InvertedPortraitOrientation: angle = 90; break;
1198         case Qt::InvertedLandscapeOrientation: angle = 180; break;
1199         case Qt::PrimaryOrientation: break;
1200     }
1201     Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
1202                                    atom(QXcbAtom::MeegoTouchOrientationAngle), XCB_ATOM_CARDINAL, 32,
1203                                    1, &angle));
1204 }
1205 #endif
1206
1207 QSurfaceFormat QXcbWindow::format() const
1208 {
1209     // ### return actual format
1210     return m_format;
1211 }
1212
1213 #if defined(XCB_USE_EGL)
1214 QXcbEGLSurface *QXcbWindow::eglSurface() const
1215 {
1216     if (!m_eglSurface) {
1217         EGLDisplay display = connection()->egl_display();
1218         EGLConfig config = q_configFromGLFormat(display, window()->requestedFormat(), true);
1219         EGLSurface surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)m_window, 0);
1220
1221         m_eglSurface = new QXcbEGLSurface(display, surface);
1222     }
1223
1224     return m_eglSurface;
1225 }
1226 #endif
1227
1228 void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
1229 {
1230     QRect rect(event->x, event->y, event->width, event->height);
1231
1232     if (m_exposeRegion.isEmpty())
1233         m_exposeRegion = rect;
1234     else
1235         m_exposeRegion |= rect;
1236
1237     // if count is non-zero there are more expose events pending
1238     if (event->count == 0) {
1239         QWindowSystemInterface::handleExposeEvent(window(), m_exposeRegion);
1240         m_exposeRegion = QRegion();
1241     }
1242 }
1243
1244 void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event)
1245 {
1246     if (event->format != 32)
1247         return;
1248
1249     if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
1250         if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) {
1251             QWindowSystemInterface::handleCloseEvent(window());
1252         } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
1253             connection()->setTime(event->data.data32[1]);
1254         } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
1255             xcb_client_message_event_t reply = *event;
1256
1257             reply.response_type = XCB_CLIENT_MESSAGE;
1258             reply.window = m_screen->root();
1259
1260             xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply);
1261             xcb_flush(xcb_connection());
1262         } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
1263             connection()->setTime(event->data.data32[1]);
1264             m_syncValue.lo = event->data.data32[2];
1265             m_syncValue.hi = event->data.data32[3];
1266         } else {
1267             qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
1268         }
1269     } else if (event->type == atom(QXcbAtom::XdndEnter)) {
1270         connection()->drag()->handleEnter(window(), event);
1271     } else if (event->type == atom(QXcbAtom::XdndPosition)) {
1272         connection()->drag()->handlePosition(window(), event);
1273     } else if (event->type == atom(QXcbAtom::XdndLeave)) {
1274         connection()->drag()->handleLeave(window(), event);
1275     } else if (event->type == atom(QXcbAtom::XdndDrop)) {
1276         connection()->drag()->handleDrop(window(), event);
1277     } else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon
1278     } else {
1279         qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
1280     }
1281 }
1282
1283 void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event)
1284 {
1285     bool fromSendEvent = (event->response_type & 0x80);
1286     QPoint pos(event->x, event->y);
1287     if (!parent() && !fromSendEvent) {
1288         // Do not trust the position, query it instead.
1289         xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(),
1290                                                                               m_screen->root(), 0, 0);
1291         xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
1292         if (reply) {
1293             pos.setX(reply->dst_x);
1294             pos.setY(reply->dst_y);
1295             free(reply);
1296         }
1297     }
1298
1299     QRect rect(pos, QSize(event->width, event->height));
1300
1301     QPlatformWindow::setGeometry(rect);
1302     QWindowSystemInterface::handleGeometryChange(window(), rect);
1303
1304     m_configureNotifyPending = false;
1305
1306     if (m_deferredExpose) {
1307         m_deferredExpose = false;
1308         QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
1309     }
1310
1311     m_dirtyFrameMargins = true;
1312
1313 #if XCB_USE_DRI2
1314     if (m_context)
1315         static_cast<QDri2Context *>(m_context)->resize(rect.size());
1316 #endif
1317 }
1318
1319 bool QXcbWindow::isExposed() const
1320 {
1321     return m_mapped;
1322 }
1323
1324 void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
1325 {
1326     if (event->window == m_window) {
1327         m_mapped = true;
1328         if (m_deferredActivation)
1329             requestActivateWindow();
1330         if (m_configureNotifyPending)
1331             m_deferredExpose = true;
1332         else
1333             QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
1334     }
1335 }
1336
1337 void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
1338 {
1339     if (event->window == m_window) {
1340         m_mapped = false;
1341         QWindowSystemInterface::handleExposeEvent(window(), QRegion());
1342     }
1343 }
1344
1345 static Qt::MouseButtons translateMouseButtons(int s)
1346 {
1347     Qt::MouseButtons ret = 0;
1348     if (s & XCB_BUTTON_MASK_1)
1349         ret |= Qt::LeftButton;
1350     if (s & XCB_BUTTON_MASK_2)
1351         ret |= Qt::MidButton;
1352     if (s & XCB_BUTTON_MASK_3)
1353         ret |= Qt::RightButton;
1354     return ret;
1355 }
1356
1357 static Qt::MouseButton translateMouseButton(xcb_button_t s)
1358 {
1359     switch (s) {
1360     case 1: return Qt::LeftButton;
1361     case 2: return Qt::MidButton;
1362     case 3: return Qt::RightButton;
1363     // Button values 4-7 were already handled as Wheel events, and won't occur here.
1364     case 8: return Qt::BackButton;      // Also known as Qt::ExtraButton1
1365     case 9: return Qt::ForwardButton;   // Also known as Qt::ExtraButton2
1366     case 10: return Qt::ExtraButton3;
1367     case 11: return Qt::ExtraButton4;
1368     case 12: return Qt::ExtraButton5;
1369     case 13: return Qt::ExtraButton6;
1370     case 14: return Qt::ExtraButton7;
1371     case 15: return Qt::ExtraButton8;
1372     case 16: return Qt::ExtraButton9;
1373     case 17: return Qt::ExtraButton10;
1374     case 18: return Qt::ExtraButton11;
1375     case 19: return Qt::ExtraButton12;
1376     case 20: return Qt::ExtraButton13;
1377     case 21: return Qt::ExtraButton14;
1378     case 22: return Qt::ExtraButton15;
1379     case 23: return Qt::ExtraButton16;
1380     case 24: return Qt::ExtraButton17;
1381     case 25: return Qt::ExtraButton18;
1382     case 26: return Qt::ExtraButton19;
1383     case 27: return Qt::ExtraButton20;
1384     case 28: return Qt::ExtraButton21;
1385     case 29: return Qt::ExtraButton22;
1386     case 30: return Qt::ExtraButton23;
1387     case 31: return Qt::ExtraButton24;
1388     default: return Qt::NoButton;
1389     }
1390 }
1391
1392 void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
1393 {
1394     updateNetWmUserTime(event->time);
1395
1396     QPoint local(event->event_x, event->event_y);
1397     QPoint global(event->root_x, event->root_y);
1398
1399     Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
1400
1401     if (event->detail >= 4 && event->detail <= 7) {
1402         // Logic borrowed from qapplication_x11.cpp
1403         int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1);
1404         bool hor = (((event->detail == 4 || event->detail == 5)
1405                      && (modifiers & Qt::AltModifier))
1406                     || (event->detail == 6 || event->detail == 7));
1407
1408         QWindowSystemInterface::handleWheelEvent(window(), event->time,
1409                                                  local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
1410         return;
1411     }
1412
1413     handleMouseEvent(event->detail, event->state, event->time, local, global, modifiers);
1414 }
1415
1416 void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
1417 {
1418     QPoint local(event->event_x, event->event_y);
1419     QPoint global(event->root_x, event->root_y);
1420     Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
1421
1422     handleMouseEvent(event->detail, event->state, event->time, local, global, modifiers);
1423 }
1424
1425 void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
1426 {
1427     QPoint local(event->event_x, event->event_y);
1428     QPoint global(event->root_x, event->root_y);
1429     Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
1430
1431     handleMouseEvent(event->detail, event->state, event->time, local, global, modifiers);
1432 }
1433
1434 void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers)
1435 {
1436     connection()->setTime(time);
1437
1438     Qt::MouseButtons buttons = translateMouseButtons(state);
1439     Qt::MouseButton button = translateMouseButton(detail);
1440
1441     buttons ^= button; // X event uses state *before*, Qt uses state *after*
1442
1443     QWindowSystemInterface::handleMouseEvent(window(), time, local, global, buttons, modifiers);
1444 }
1445
1446 void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
1447 {
1448     connection()->setTime(event->time);
1449
1450     if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
1451         || event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
1452         || event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL)
1453     {
1454         return;
1455     }
1456
1457     QWindowSystemInterface::handleEnterEvent(window());
1458 }
1459
1460 void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
1461 {
1462     connection()->setTime(event->time);
1463
1464     if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
1465         || event->detail == XCB_NOTIFY_DETAIL_INFERIOR)
1466     {
1467         return;
1468     }
1469
1470     QWindowSystemInterface::handleLeaveEvent(window());
1471 }
1472
1473 void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
1474 {
1475     connection()->setTime(event->time);
1476
1477     const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
1478     const xcb_atom_t netWmStateAtom = atom(QXcbAtom::_NET_WM_STATE);
1479     const xcb_atom_t wmStateAtom = atom(QXcbAtom::WM_STATE);
1480
1481     if (event->atom == netWmStateAtom || event->atom == wmStateAtom) {
1482         if (propertyDeleted)
1483             return;
1484
1485         Qt::WindowState newState = Qt::WindowNoState;
1486         if (event->atom == wmStateAtom) { // WM_STATE: Quick check for 'Minimize'.
1487             const xcb_get_property_cookie_t get_cookie =
1488                 xcb_get_property(xcb_connection(), 0, m_window, wmStateAtom,
1489                                  XCB_ATOM_ANY, 0, 1024);
1490
1491             xcb_get_property_reply_t *reply =
1492                 xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
1493
1494             if (reply && reply->format == 32 && reply->type == wmStateAtom) {
1495                 const long *data = (const long *)xcb_get_property_value(reply);
1496                 if (reply->length != 0 && XCB_WM_STATE_ICONIC == data[0])
1497                     newState = Qt::WindowMinimized;
1498                 free(reply);
1499             }
1500         } // WM_STATE: Quick check for 'Minimize'.
1501         if (newState != Qt::WindowMinimized) { // Something else changed, get _NET_WM_STATE.
1502             const NetWmStates states = netWmStates();
1503             if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
1504                 newState = Qt::WindowMaximized;
1505             else if (states & NetWmStateFullScreen)
1506                 newState = Qt::WindowFullScreen;
1507         }
1508         // Send Window state, compress events in case other flags (modality, etc) are changed.
1509         if (m_lastWindowStateEvent != newState) {
1510             QWindowSystemInterface::handleWindowStateChanged(window(), newState);
1511             m_lastWindowStateEvent = newState;
1512         }
1513     }
1514 }
1515
1516 void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *)
1517 {
1518     QWindowSystemInterface::handleWindowActivated(window());
1519 }
1520
1521 static bool focusInPeeker(xcb_generic_event_t *event)
1522 {
1523     if (!event) {
1524         // FocusIn event is not in the queue, proceed with FocusOut normally.
1525         QWindowSystemInterface::handleWindowActivated(0);
1526         return true;
1527     }
1528     uint response_type = event->response_type & ~0x80;
1529     return response_type == XCB_FOCUS_IN;
1530 }
1531
1532 void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *)
1533 {
1534     // Do not set the active window to 0 if there is a FocusIn coming.
1535     // There is however no equivalent for XPutBackEvent so register a
1536     // callback for QXcbConnection instead.
1537     connection()->addPeekFunc(focusInPeeker);
1538 }
1539
1540 void QXcbWindow::updateSyncRequestCounter()
1541 {
1542     if (m_screen->syncRequestSupported() && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
1543         Q_XCB_CALL(xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue));
1544         xcb_flush(xcb_connection());
1545         connection()->sync();
1546
1547         m_syncValue.lo = 0;
1548         m_syncValue.hi = 0;
1549     }
1550 }
1551
1552 bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
1553 {
1554     if (!grab) {
1555         xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME);
1556         return true;
1557     }
1558     xcb_grab_keyboard_cookie_t cookie = xcb_grab_keyboard(xcb_connection(), false,
1559                                                           m_window, XCB_TIME_CURRENT_TIME,
1560                                                           XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
1561     xcb_grab_keyboard_reply_t *reply = xcb_grab_keyboard_reply(xcb_connection(), cookie, NULL);
1562     bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
1563     free(reply);
1564     return result;
1565 }
1566
1567 bool QXcbWindow::setMouseGrabEnabled(bool grab)
1568 {
1569     if (!grab) {
1570         xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME);
1571         return true;
1572     }
1573     xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer(xcb_connection(), false, m_window,
1574                                                         (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
1575                                                          | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
1576                                                          | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
1577                                                         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
1578                                                         XCB_WINDOW_NONE, XCB_CURSOR_NONE,
1579                                                         XCB_TIME_CURRENT_TIME);
1580     xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, NULL);
1581     bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
1582     free(reply);
1583     return result;
1584 }
1585
1586 void QXcbWindow::setCursor(xcb_cursor_t cursor)
1587 {
1588     xcb_change_window_attributes(xcb_connection(), m_window, XCB_CW_CURSOR, &cursor);
1589     xcb_flush(xcb_connection());
1590 }
1591
1592 QT_END_NAMESPACE