add WM support class
authorLars Knoll <lars.knoll@nokia.com>
Tue, 7 Jun 2011 08:33:23 +0000 (10:33 +0200)
committerLars Knoll <lars.knoll@nokia.com>
Tue, 7 Jun 2011 13:50:53 +0000 (15:50 +0200)
Add a QXcbWMSupport class to better integrate with
NET_WM compliant window managers.

Suppport NET_WM_USER_TIME on windows.

Reviewed-by: Samuel
src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h
src/plugins/platforms/xcb/qxcbkeyboard.cpp
src/plugins/platforms/xcb/qxcbkeyboard.h
src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/qxcbwindow.h
src/plugins/platforms/xcb/qxcbwmsupport.cpp [new file with mode: 0644]
src/plugins/platforms/xcb/qxcbwmsupport.h [new file with mode: 0644]
src/plugins/platforms/xcb/xcb.pro

index fa59521..94c30a9 100644 (file)
@@ -45,6 +45,7 @@
 #include "qxcbwindow.h"
 #include "qxcbclipboard.h"
 #include "qxcbdrag.h"
+#include "qxcbwmsupport.h"
 
 #include <QtAlgorithms>
 #include <QSocketNotifier>
@@ -119,6 +120,7 @@ QXcbConnection::QXcbConnection(const char *displayName)
         xcb_screen_next(&it);
     }
 
+    m_wmSupport = new QXcbWMSupport(this);
     m_keyboard = new QXcbKeyboard(this);
     m_clipboard = new QXcbClipboard(this);
     m_drag = new QXcbDrag(this);
@@ -178,7 +180,7 @@ break;
 { \
     event_t *e = (event_t *)event; \
     if (QXcbWindow *platformWindow = platformWindowFromId(e->event)) \
-        m_keyboard->handler(platformWindow->window(), e); \
+        m_keyboard->handler(platformWindow, e); \
 } \
 break;
 
@@ -515,6 +517,7 @@ void QXcbConnection::processXcbEvents()
         xcb_generic_event_t *event = eventqueue.at(i);
         if (!event)
             continue;
+        eventqueue[i] = 0;
 
         uint response_type = event->response_type & ~0x80;
 
@@ -788,8 +791,15 @@ xcb_atom_t QXcbConnection::internAtom(const char *name)
 
 QByteArray QXcbConnection::atomName(xcb_atom_t atom)
 {
-    xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name_unchecked(xcb_connection(), atom));
-    xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, 0);
+    if (!atom)
+        return QByteArray();
+
+    xcb_generic_error_t *error = 0;
+    xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom));
+    xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error);
+    if (error) {
+        qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
+    }
     if (reply) {
         QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
         free(reply);
index 401d446..25a92f9 100644 (file)
@@ -56,6 +56,7 @@ class QXcbWindow;
 class QXcbDrag;
 class QXcbKeyboard;
 class QXcbClipboard;
+class QXcbWMSupport;
 
 typedef QHash<xcb_window_t, QXcbWindow *> WindowMapper;
 
@@ -67,6 +68,7 @@ namespace QXcbAtom {
     static const xcb_atom_t XA_BITMAP = 5;
     static const xcb_atom_t XA_STRING = 32;
     static const xcb_atom_t XA_WINDOW = 33;
+    static const xcb_atom_t XA_CARDINAL = 6;
 
     enum Atom {
         // window-manager <-> client protocols
@@ -256,6 +258,8 @@ public:
     QXcbClipboard *clipboard() const { return m_clipboard; }
     QXcbDrag *drag() const { return m_drag; }
 
+    QXcbWMSupport *wmSupport() const { return m_wmSupport; }
+
 #ifdef XCB_USE_XLIB
     void *xlib_display() const { return m_xlib_display; }
 #endif
@@ -285,8 +289,6 @@ public:
     template<typename T>
     inline xcb_generic_event_t *checkEvent(const T &checker);
 
-    QXcbWindow *platformWindowFromId(xcb_window_t id);
-
     typedef bool (*PeekFunc)(xcb_generic_event_t *);
     void addPeekFunc(PeekFunc f);
 
@@ -319,6 +321,7 @@ private:
     QXcbKeyboard *m_keyboard;
     QXcbClipboard *m_clipboard;
     QXcbDrag *m_drag;
+    QXcbWMSupport *m_wmSupport;
 
 #if defined(XCB_USE_XLIB)
     void *m_xlib_display;
index 2b9f790..9cc21b7 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 
 #include "qxcbkeyboard.h"
+#include "qxcbwindow.h"
 
 #include <xcb/xcb_keysyms.h>
 
@@ -1030,14 +1031,15 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod
     QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, code, 0, state, string.left(count));
 }
 
-void QXcbKeyboard::handleKeyPressEvent(QWindow *window, const xcb_key_press_event_t *event)
+void QXcbKeyboard::handleKeyPressEvent(QXcbWindow *window, const xcb_key_press_event_t *event)
 {
-    handleKeyEvent(window, QEvent::KeyPress, event->detail, event->state, event->time);
+    window->updateNetWmUserTime(event->time);
+    handleKeyEvent(window->window(), QEvent::KeyPress, event->detail, event->state, event->time);
 }
 
-void QXcbKeyboard::handleKeyReleaseEvent(QWindow *window, const xcb_key_release_event_t *event)
+void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindow *window, const xcb_key_release_event_t *event)
 {
-    handleKeyEvent(window, QEvent::KeyRelease, event->detail, event->state, event->time);
+    handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time);
 }
 
 void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event)
index dcdbf01..2616a08 100644 (file)
@@ -56,8 +56,8 @@ public:
     QXcbKeyboard(QXcbConnection *connection);
     ~QXcbKeyboard();
 
-    void handleKeyPressEvent(QWindow *window, const xcb_key_press_event_t *event);
-    void handleKeyReleaseEvent(QWindow *window, const xcb_key_release_event_t *event);
+    void handleKeyPressEvent(QXcbWindow *window, const xcb_key_press_event_t *event);
+    void handleKeyReleaseEvent(QXcbWindow *window, const xcb_key_release_event_t *event);
 
     void handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event);
 
index 2aaa2c4..8e53852 100644 (file)
@@ -46,7 +46,7 @@
 #include "qxcbconnection.h"
 #include "qxcbscreen.h"
 #include "qxcbdrag.h"
-
+#include "qxcbwmsupport.h"
 
 #ifdef XCB_USE_DRI2
 #include "qdri2context.h"
@@ -98,6 +98,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
     , m_context(0)
     , m_syncCounter(0)
     , m_mapped(false)
+    , m_netWmUserTimeWindow(XCB_NONE)
 {
     m_screen = static_cast<QXcbScreen *>(QGuiApplicationPrivate::platformIntegration()->screens().at(0));
 
@@ -812,6 +813,43 @@ void QXcbWindow::updateNetWmStateBeforeMap()
     setNetWmState(netWmState);
 }
 
+void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
+{
+    xcb_window_t wid = m_window;
+
+    const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
+    if (m_netWmUserTimeWindow || isSupportedByWM) {
+        if (!m_netWmUserTimeWindow) {
+            m_netWmUserTimeWindow = xcb_generate_id(xcb_connection());
+            Q_XCB_CALL(xcb_create_window(xcb_connection(),
+                                         XCB_COPY_FROM_PARENT,            // depth -- same as root
+                                         m_netWmUserTimeWindow,                        // window id
+                                         m_window,                   // parent window id
+                                         -1, -1, 1, 1,
+                                         0,                               // border width
+                                         XCB_WINDOW_CLASS_INPUT_OUTPUT,   // window class
+                                         m_screen->screen()->root_visual, // visual
+                                         0,                               // value mask
+                                         0));                             // value list
+            wid = m_netWmUserTimeWindow;
+            xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
+                                QXcbAtom::XA_WINDOW, 32, 1, &m_netWmUserTimeWindow);
+            xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
+        } else if (!isSupportedByWM) {
+            // WM no longer supports it, then we should remove the
+            // _NET_WM_USER_TIME_WINDOW atom.
+            xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
+            xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow);
+            m_netWmUserTimeWindow = XCB_NONE;
+        } else {
+            wid = m_netWmUserTimeWindow;
+        }
+    }
+    xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::_NET_WM_USER_TIME),
+                        QXcbAtom::XA_CARDINAL, 32, 1, &timestamp);
+}
+
+
 WId QXcbWindow::winId() const
 {
     return m_window;
@@ -892,9 +930,10 @@ void QXcbWindow::propagateSizeHints()
 void QXcbWindow::requestActivateWindow()
 {
     if (m_mapped){
-        Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, XCB_TIME_CURRENT_TIME));
-        connection()->sync();
+        updateNetWmUserTime(connection()->time());
+        Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time()));
     }
+    connection()->sync();
 }
 
 QPlatformGLContext *QXcbWindow::glContext() const
@@ -942,11 +981,12 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
         return;
 
     if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
+        qDebug() << "WM_PROTO" << m_window << connection()->atomName(event->data.data32[0]);
         if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) {
             QWindowSystemInterface::handleCloseEvent(window());
         } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
             connection()->setTime(event->data.data32[1]);
-            // ### handle take focus!
+            requestActivateWindow();
         } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
             xcb_client_message_event_t reply = *event;
 
@@ -963,6 +1003,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
             }
             m_syncValue.lo = event->data.data32[2];
             m_syncValue.hi = event->data.data32[3];
+        } else {
+            qWarning() << "unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
         }
     } else if (event->type == atom(QXcbAtom::XdndEnter)) {
         connection()->drag()->handleEnter(window(), event);
@@ -972,6 +1014,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
         connection()->drag()->handleLeave(window(), event, false);
     } else if (event->type == atom(QXcbAtom::XdndDrop)) {
         connection()->drag()->handleDrop(window(), event, false);
+    } else {
+        qWarning() << "unhandled client message:" << connection()->atomName(event->type);
     }
 }
 
@@ -1043,6 +1087,8 @@ static Qt::MouseButton translateMouseButton(xcb_button_t s)
 
 void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
 {
+    updateNetWmUserTime(event->time);
+
     QPoint local(event->event_x, event->event_y);
     QPoint global(event->root_x, event->root_y);
 
index 23b9d7a..0412f4a 100644 (file)
@@ -100,6 +100,8 @@ public:
     void handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global);
 
     void updateSyncRequestCounter();
+    void updateNetWmUserTime(xcb_timestamp_t timestamp);
+    void netWmUserTime() const;
 
 private:
     void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0);
@@ -113,6 +115,7 @@ private:
     void updateMotifWmHintsBeforeMap();
     void updateNetWmStateBeforeMap();
 
+
     void create();
     void destroy();
 
@@ -134,6 +137,7 @@ private:
     Qt::WindowState m_windowState;
 
     bool m_mapped;
+    xcb_window_t m_netWmUserTimeWindow;
 };
 
 #endif
diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp
new file mode 100644 (file)
index 0000000..8e55bf8
--- /dev/null
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbwmsupport.h"
+#include "qxcbscreen.h"
+
+#include <qdebug.h>
+
+QXcbWMSupport::QXcbWMSupport(QXcbConnection *c)
+    : QXcbObject(c)
+{
+    updateNetWMAtoms();
+    updateVirtualRoots();
+}
+
+bool QXcbWMSupport::isSupportedByWM(xcb_atom_t atom) const
+{
+    return net_wm_atoms.contains(atom);
+}
+
+
+
+void QXcbWMSupport::updateNetWMAtoms()
+{
+    net_wm_atoms.clear();
+
+    xcb_window_t root = connection()->screens().at(connection()->primaryScreen())->root();
+    int offset = 0;
+    int remaining = 0;
+    do {
+        xcb_generic_error_t *error = 0;
+        xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), QXcbAtom::XA_ATOM, offset, 1024);
+        xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, &error);
+        if (!reply || error)
+            break;
+
+        remaining = 0;
+
+        if (reply->type == QXcbAtom::XA_ATOM && reply->format == 32) {
+            int len = xcb_get_property_value_length(reply)/4;
+            xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+            int s = net_wm_atoms.size();
+            net_wm_atoms.resize(s + len);
+            memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t));
+
+            remaining = reply->bytes_after;
+            offset += len;
+        }
+
+        free(reply);
+    } while (remaining > 0);
+
+//    qDebug() << "======== updateNetWMAtoms";
+//    for (int i = 0; i < net_wm_atoms.size(); ++i)
+//        qDebug() << atomName(net_wm_atoms.at(i));
+//    qDebug() << "======== updateNetWMAtoms";
+}
+
+// update the virtual roots array
+void QXcbWMSupport::updateVirtualRoots()
+{
+    net_virtual_roots.clear();
+
+    if (!isSupportedByWM(atom(QXcbAtom::_NET_VIRTUAL_ROOTS)))
+        return;
+
+    xcb_window_t root = connection()->screens().at(connection()->primaryScreen())->root();
+    int offset = 0;
+    int remaining = 0;
+    do {
+        xcb_generic_error_t *error = 0;
+        xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), QXcbAtom::XA_ATOM, offset, 1024);
+        xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, &error);
+        if (!reply || error)
+            break;
+
+        remaining = 0;
+
+        if (reply->type == QXcbAtom::XA_ATOM && reply->format == 32) {
+            int len = xcb_get_property_value_length(reply)/4;
+            xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+            int s = net_wm_atoms.size();
+            net_wm_atoms.resize(s + len);
+            memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t));
+
+            remaining = reply->bytes_after;
+            offset += len;
+        }
+
+        free(reply);
+    } while (remaining > 0);
+
+    qDebug() << "======== updateVirtualRoots";
+    for (int i = 0; i < net_virtual_roots.size(); ++i)
+        qDebug() << connection()->atomName(net_virtual_roots.at(i));
+    qDebug() << "======== updateVirtualRoots";
+}
+
diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.h b/src/plugins/platforms/xcb/qxcbwmsupport.h
new file mode 100644 (file)
index 0000000..3b02ef3
--- /dev/null
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QXCBWMSUPPORT_H
+#define QXCBWMSUPPORT_H
+
+#include "qxcbobject.h"
+#include "qxcbconnection.h"
+#include <qvector.h>
+
+class QXcbWMSupport : public QXcbObject
+{
+public:
+    QXcbWMSupport(QXcbConnection *c);
+
+
+    bool isSupportedByWM(xcb_atom_t atom) const;
+    const QVector<xcb_window_t> &virtualRoots() const { return net_virtual_roots; }
+
+private:
+    friend class QXcbConnection;
+    void updateNetWMAtoms();
+    void updateVirtualRoots();
+
+    QVector<xcb_atom_t> net_wm_atoms;
+    QVector<xcb_window_t> net_virtual_roots;
+};
+
+
+#endif
index e7fee04..2f70b65 100644 (file)
@@ -15,6 +15,7 @@ SOURCES = \
         qxcbscreen.cpp \
         qxcbwindow.cpp \
         qxcbwindowsurface.cpp \
+        qxcbwmsupport.cpp \
         main.cpp \
         qxcbnativeinterface.cpp
 
@@ -29,6 +30,7 @@ HEADERS = \
         qxcbscreen.h \
         qxcbwindow.h \
         qxcbwindowsurface.h \
+        qxcbwmsupport.h \
         qxcbnativeinterface.h
 
 QT += gui-private core-private