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) <faure@kde.org>
bb10
David Faure 2013-04-15 23:52:32 +02:00 committed by The Qt Project
parent 71a1ff39bc
commit 1ea1abeb91
8 changed files with 85 additions and 1 deletions

View File

@ -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();
}

View File

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

View File

@ -95,6 +95,8 @@ public:
QStringList themeNames() const;
QPlatformTheme *createPlatformTheme(const QString &name) const;
QXcbConnection *defaultConnection() const { return m_connections.first(); }
private:
QList<QXcbConnection *> m_connections;

View File

@ -42,6 +42,7 @@
#include "qxcbnativeinterface.h"
#include "qxcbscreen.h"
#include "qxcbintegration.h"
#include <private/qguiapplication_p.h>
#include <QtCore/QMap>
@ -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<void *>(quintptr(screen->connection()->netWmUserTime()));
}
void *QXcbNativeInterface::startupId()
{
QXcbIntegration* integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration());
QXcbConnection *defaultConnection = integration->defaultConnection();
if (defaultConnection)
return reinterpret_cast<void *>(const_cast<char *>(defaultConnection->startupId().constData()));
return 0;
}
void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time)
{
static_cast<QXcbScreen *>(screen->handle())->connection()->setTime(time);

View File

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

View File

@ -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<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);

View File

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

View File

@ -46,6 +46,7 @@
#include <QtGui/QIcon>
#include <QtGui/QRegion>
#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();