~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/plugins/platforms/xcb/qxcbwindow.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

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