From ff53b1dcec761861f76c7528aafd42c9ce843d56 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 7 Jun 2011 10:33:23 +0200 Subject: [PATCH] add WM support class 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 | 16 +++- src/plugins/platforms/xcb/qxcbconnection.h | 7 +- src/plugins/platforms/xcb/qxcbkeyboard.cpp | 10 +- src/plugins/platforms/xcb/qxcbkeyboard.h | 4 +- src/plugins/platforms/xcb/qxcbwindow.cpp | 54 ++++++++++- src/plugins/platforms/xcb/qxcbwindow.h | 4 + src/plugins/platforms/xcb/qxcbwmsupport.cpp | 136 +++++++++++++++++++++++++++ src/plugins/platforms/xcb/qxcbwmsupport.h | 67 +++++++++++++ src/plugins/platforms/xcb/xcb.pro | 2 + 9 files changed, 285 insertions(+), 15 deletions(-) create mode 100644 src/plugins/platforms/xcb/qxcbwmsupport.cpp create mode 100644 src/plugins/platforms/xcb/qxcbwmsupport.h diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index fa59521..94c30a9 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -45,6 +45,7 @@ #include "qxcbwindow.h" #include "qxcbclipboard.h" #include "qxcbdrag.h" +#include "qxcbwmsupport.h" #include #include @@ -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); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 401d446..25a92f9 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -56,6 +56,7 @@ class QXcbWindow; class QXcbDrag; class QXcbKeyboard; class QXcbClipboard; +class QXcbWMSupport; typedef QHash 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 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; diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 2b9f790..9cc21b7 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qxcbkeyboard.h" +#include "qxcbwindow.h" #include @@ -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) diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index dcdbf01..2616a08 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -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); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 2aaa2c4..8e53852 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -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(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, ×tamp); +} + + 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); diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 23b9d7a..0412f4a 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -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 index 0000000..8e55bf8 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp @@ -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 + +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 index 0000000..3b02ef3 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbwmsupport.h @@ -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 + +class QXcbWMSupport : public QXcbObject +{ +public: + QXcbWMSupport(QXcbConnection *c); + + + bool isSupportedByWM(xcb_atom_t atom) const; + const QVector &virtualRoots() const { return net_virtual_roots; } + +private: + friend class QXcbConnection; + void updateNetWMAtoms(); + void updateVirtualRoots(); + + QVector net_wm_atoms; + QVector net_virtual_roots; +}; + + +#endif diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro index e7fee04..2f70b65 100644 --- a/src/plugins/platforms/xcb/xcb.pro +++ b/src/plugins/platforms/xcb/xcb.pro @@ -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 -- 2.7.4