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
This commit is contained in:
Lars Knoll 2011-06-07 10:33:23 +02:00
parent c3f9de6296
commit ff53b1dcec
9 changed files with 285 additions and 15 deletions

View 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);

View 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;

View 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)

View 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);

View 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);

View 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

View File

@ -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";
}

View File

@ -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

View 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