From 1ea1abeb91f544b4cf595d229249ee859090bee5 Mon Sep 17 00:00:00 2001 From: David Faure Date: Mon, 15 Apr 2013 23:52:32 +0200 Subject: Implement startup notification spec again. This functionality was in Qt4's qapplication_x11.cpp and was missing from the XCB QPA plugin. Ported the code from xlib to xcb. This code was actually tested (with plasma), unlike the Qt-4.8 code which skipped every other character... for (uint i = 0; i < 20 && i + sent <= length; i++) xevent.xclient.data.b[i] = message[i + sent++]; Provide a QPA native-function for accessing the startup id, for cases where an application doesn't show a window, but starts another app instead, or asks a running app to show the window on its behalf. Change-Id: If392179efddd70a51c45a8fab4fb9d753913094a Reviewed-by: David Faure (KDE) --- src/plugins/platforms/xcb/qxcbconnection.cpp | 4 +++ src/plugins/platforms/xcb/qxcbconnection.h | 5 ++++ src/plugins/platforms/xcb/qxcbintegration.h | 2 ++ src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 30 ++++++++++++++++++++ src/plugins/platforms/xcb/qxcbnativeinterface.h | 5 +++- src/plugins/platforms/xcb/qxcbscreen.cpp | 34 +++++++++++++++++++++++ src/plugins/platforms/xcb/qxcbscreen.h | 2 ++ src/plugins/platforms/xcb/qxcbwindow.cpp | 4 +++ 8 files changed, 85 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 1504bd99d2..cb80fdfbd3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -344,6 +344,10 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char m_drag = new QXcbDrag(this); #endif + m_startupId = qgetenv("DESKTOP_STARTUP_ID"); + if (!m_startupId.isNull()) + qunsetenv("DESKTOP_STARTUP_ID"); + sync(); } diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index f69a8a9f35..8ab65fc2bc 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -388,6 +388,9 @@ public: QXcbWindow *focusWindow() const { return m_focusWindow; } void setFocusWindow(QXcbWindow *); + QByteArray startupId() const { return m_startupId; } + void clearStartupId() { m_startupId.clear(); } + private slots: void processXcbEvents(); @@ -516,6 +519,8 @@ private: Qt::MouseButtons m_buttons; QXcbWindow *m_focusWindow; + + QByteArray m_startupId; }; #define DISPLAY_FROM_XCB(object) ((Display *)(object->connection()->xlib_display())) diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 6db9d82cca..451dc43475 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -95,6 +95,8 @@ public: QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; + QXcbConnection *defaultConnection() const { return m_connections.first(); } + private: QList m_connections; diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 6ac69a8335..da60cfd2bd 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -42,6 +42,7 @@ #include "qxcbnativeinterface.h" #include "qxcbscreen.h" +#include "qxcbintegration.h" #include #include @@ -78,6 +79,7 @@ public: insert("apptime",QXcbNativeInterface::AppTime); insert("appusertime",QXcbNativeInterface::AppUserTime); insert("hintstyle", QXcbNativeInterface::ScreenHintStyle); + insert("startupid", QXcbNativeInterface::StartupId); } }; @@ -99,6 +101,25 @@ void QXcbNativeInterface::beep() // For QApplication::beep() #endif } +void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString) +{ + QByteArray lowerCaseResource = resourceString.toLower(); + if (!qXcbResourceMap()->contains(lowerCaseResource)) + return 0; + + ResourceType resource = qXcbResourceMap()->value(lowerCaseResource); + void *result = 0; + switch (resource) { + case StartupId: + result = startupId(); + break; + default: + break; + } + + return result; +} + void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context) { QByteArray lowerCaseResource = resourceString.toLower(); @@ -196,6 +217,15 @@ void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen) return reinterpret_cast(quintptr(screen->connection()->netWmUserTime())); } +void *QXcbNativeInterface::startupId() +{ + QXcbIntegration* integration = static_cast(QGuiApplicationPrivate::platformIntegration()); + QXcbConnection *defaultConnection = integration->defaultConnection(); + if (defaultConnection) + return reinterpret_cast(const_cast(defaultConnection->startupId().constData())); + return 0; +} + void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time) { static_cast(screen->handle())->connection()->setTime(time); diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index 919a21d00d..e27bfa5a46 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -65,11 +65,13 @@ public: GLXContext, AppTime, AppUserTime, - ScreenHintStyle + ScreenHintStyle, + StartupId }; QXcbNativeInterface(); + void *nativeResourceForIntegration(const QByteArray &resource) Q_DECL_OVERRIDE; void *nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context); void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen); void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window); @@ -86,6 +88,7 @@ public: void *graphicsDeviceForWindow(QWindow *window); void *appTime(const QXcbScreen *screen); void *appUserTime(const QXcbScreen *screen); + void *startupId(); static void setAppTime(QScreen *screen, xcb_timestamp_t time); static void setAppUserTime(QScreen *screen, xcb_timestamp_t time); static void *eglContextForContext(QOpenGLContext *context); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 38b4c873ad..a6ead49a27 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -233,6 +233,40 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const return 0; } +void QXcbScreen::windowShown(QXcbWindow *window) +{ + // Freedesktop.org Startup Notification + if (!connection()->startupId().isEmpty() && window->window()->isTopLevel()) { + sendStartupMessage(QByteArrayLiteral("remove: ID=") + connection()->startupId()); + connection()->clearStartupId(); + } +} + +void QXcbScreen::sendStartupMessage(const QByteArray &message) const +{ + xcb_window_t rootWindow = root(); + + xcb_client_message_event_t ev; + ev.response_type = XCB_CLIENT_MESSAGE; + ev.format = 8; + ev.type = connection()->atom(QXcbAtom::_NET_STARTUP_INFO_BEGIN); + ev.window = rootWindow; + int sent = 0; + int length = message.length() + 1; // include NUL byte + const char *data = message.constData(); + do { + if (sent == 20) + ev.type = connection()->atom(QXcbAtom::_NET_STARTUP_INFO); + + const int start = sent; + const int numBytes = qMin(length - start, 20); + memcpy(ev.data.data8, data + start, numBytes); + xcb_send_event(connection()->xcb_connection(), false, rootWindow, XCB_EVENT_MASK_PROPERTY_CHANGE, (const char *) &ev); + + sent += numBytes; + } while (sent < length); +} + const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const { QMap::const_iterator it = m_visuals.find(visualid); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index c3b13fd1ea..0382be8a29 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -87,6 +87,7 @@ public: xcb_window_t clientLeader() const { return m_clientLeader; } + void windowShown(QXcbWindow *window); QString windowManagerName() const { return m_windowManagerName; } bool syncRequestSupported() const { return m_syncRequestSupported; } @@ -105,6 +106,7 @@ private: static bool xResource(const QByteArray &identifier, const QByteArray &expectedIdentifier, int *value); + void sendStartupMessage(const QByteArray &message) const; xcb_screen_t *m_screen; xcb_randr_crtc_t m_crtc; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index f2ec4ce4b0..4090758d36 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -46,6 +46,7 @@ #include #include +#include "qxcbintegration.h" #include "qxcbconnection.h" #include "qxcbscreen.h" #include "qxcbdrag.h" @@ -653,6 +654,9 @@ void QXcbWindow::show() updateNetWmUserTime(connection()->time()); Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); + + m_screen->windowShown(this); + xcb_flush(xcb_connection()); connection()->sync(); -- cgit v1.2.3