From d8090022f66cc6cff6af5ed2ae702212fd172ff7 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 28 Jun 2013 11:51:29 +0200 Subject: Move the X11 system tray code from widgets into XCB-plugin. - Add system tray tracker class to XCB plugin and provide functionality via invokable slots of the native interface. - Remove XLib-dependency of widgets/utils. - Reintroduce tracking of tray window destruction and recreation, which was removed in the XLib-code when porting it from Qt 4 to Qt 5. This paves the way for implementing the tray icon completely in terms of QPlatformSystemTrayIcon at some point later. Change-Id: Ia04268b0e2919c05874a3e9548930535332897c7 Reviewed-by: Alberto Mardegan Reviewed-by: Shawn Rutledge --- src/plugins/platforms/xcb/qxcbconnection.cpp | 19 +++ src/plugins/platforms/xcb/qxcbconnection.h | 8 + src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 38 +++++ src/plugins/platforms/xcb/qxcbnativeinterface.h | 11 +- src/plugins/platforms/xcb/qxcbscreen.cpp | 1 + .../platforms/xcb/qxcbsystemtraytracker.cpp | 180 +++++++++++++++++++++ src/plugins/platforms/xcb/qxcbsystemtraytracker.h | 87 ++++++++++ src/plugins/platforms/xcb/qxcbwindow.cpp | 3 + src/plugins/platforms/xcb/xcb-plugin.pro | 6 +- src/widgets/util/qsystemtrayicon_x11.cpp | 150 +++++++---------- src/widgets/util/util.pri | 1 - 11 files changed, 404 insertions(+), 100 deletions(-) create mode 100644 src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp create mode 100644 src/plugins/platforms/xcb/qxcbsystemtraytracker.h diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index c4dd58667d..63bc1a6fee 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -51,6 +51,7 @@ #include "qxcbwmsupport.h" #include "qxcbnativeinterface.h" #include "qxcbintegration.h" +#include "qxcbsystemtraytracker.h" #include #include @@ -262,6 +263,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , has_xkb(false) , m_buttons(0) , m_focusWindow(0) + , m_systemTrayTracker(0) { #ifdef XCB_USE_XLIB Display *dpy = XOpenDisplay(m_displayName.constData()); @@ -813,6 +815,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent); case XCB_UNMAP_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent); + case XCB_DESTROY_NOTIFY: + HANDLE_PLATFORM_WINDOW_EVENT(xcb_destroy_notify_event_t, event, handleDestroyNotifyEvent); case XCB_CLIENT_MESSAGE: handleClientMessageEvent((xcb_client_message_event_t *)event); break; @@ -1193,6 +1197,8 @@ void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t * drag()->handleFinished(event); } #endif + if (m_systemTrayTracker && event->type == atom(QXcbAtom::MANAGER)) + m_systemTrayTracker->notifyManagerClientMessageEvent(event); QXcbWindow *window = platformWindowFromId(event->window); if (!window) @@ -1228,6 +1234,8 @@ static const char * xcb_atomnames = { "_NET_WM_CONTEXT_HELP\0" "_NET_WM_SYNC_REQUEST\0" "_NET_WM_SYNC_REQUEST_COUNTER\0" + "MANAGER\0" + "_NET_SYSTEM_TRAY_OPCODE\0" // ICCCM window state "WM_STATE\0" @@ -1728,6 +1736,17 @@ bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int o } #endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO) +QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() +{ + if (!m_systemTrayTracker) { + if ( (m_systemTrayTracker = QXcbSystemTrayTracker::create(this)) ) { + connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)), + QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*))); + } + } + return m_systemTrayTracker; +} + QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection) :m_connection(connection) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 8f568c6c25..aa0e070061 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -87,6 +87,7 @@ class QXcbKeyboard; class QXcbClipboard; class QXcbWMSupport; class QXcbNativeInterface; +class QXcbSystemTrayTracker; namespace QXcbAtom { enum Atom { @@ -98,6 +99,8 @@ namespace QXcbAtom { _NET_WM_CONTEXT_HELP, _NET_WM_SYNC_REQUEST, _NET_WM_SYNC_REQUEST_COUNTER, + MANAGER, // System tray notification + _NET_SYSTEM_TRAY_OPCODE, // System tray operation // ICCCM window state WM_STATE, @@ -321,6 +324,7 @@ public: virtual void handleConfigureNotifyEvent(const xcb_configure_notify_event_t *) {} virtual void handleMapNotifyEvent(const xcb_map_notify_event_t *) {} virtual void handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *) {} + virtual void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) {} virtual void handleButtonPressEvent(const xcb_button_press_event_t *) {} virtual void handleButtonReleaseEvent(const xcb_button_release_event_t *) {} virtual void handleMotionNotifyEvent(const xcb_motion_notify_event_t *) {} @@ -433,6 +437,9 @@ public: void ungrabServer(); QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; } + + QXcbSystemTrayTracker *systemTrayTracker(); + private slots: void processXcbEvents(); @@ -566,6 +573,7 @@ private: QXcbWindow *m_focusWindow; QByteArray m_startupId; + QXcbSystemTrayTracker *m_systemTrayTracker; }; #define DISPLAY_FROM_XCB(object) ((Display *)(object->connection()->xlib_display())) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 9e9fd2914f..1c9903e234 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -42,7 +42,9 @@ #include "qxcbnativeinterface.h" #include "qxcbscreen.h" +#include "qxcbwindow.h" #include "qxcbintegration.h" +#include "qxcbsystemtraytracker.h" #include #include @@ -82,6 +84,7 @@ public: insert("appusertime",QXcbNativeInterface::AppUserTime); insert("hintstyle", QXcbNativeInterface::ScreenHintStyle); insert("startupid", QXcbNativeInterface::StartupId); + insert(QByteArrayLiteral("traywindow"), QXcbNativeInterface::TrayWindow); } }; @@ -100,6 +103,36 @@ void QXcbNativeInterface::beep() // For QApplication::beep() xcb_bell(connection, 0); } +static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s) +{ + return static_cast(s->handle())->connection()->systemTrayTracker(); +} + +bool QXcbNativeInterface::systemTrayAvailable(const QScreen *screen) const +{ + return systemTrayTracker(screen); +} + +bool QXcbNativeInterface::requestSystemTrayWindowDock(const QWindow *window) +{ + const QPlatformWindow *platformWindow = window->handle(); + if (!platformWindow) + return false; + QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen()); + if (!trayTracker) + return false; + trayTracker->requestSystemTrayWindowDock(static_cast(platformWindow)->xcb_window()); + return true; +} + +QRect QXcbNativeInterface::systemTrayWindowGlobalGeometry(const QWindow *window) +{ + if (const QPlatformWindow *platformWindow = window->handle()) + if (const QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen())) + return trayTracker->systemTrayWindowGlobalGeometry(static_cast(platformWindow)->xcb_window()); + return QRect(); +} + void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString) { QByteArray lowerCaseResource = resourceString.toLower(); @@ -162,6 +195,11 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resource, Q break; case ScreenHintStyle: result = reinterpret_cast(xcbScreen->hintStyle() + 1); + break; + case TrayWindow: + if (QXcbSystemTrayTracker *s = systemTrayTracker(screen)) + result = (void *)quintptr(s->trayWindow()); + break; default: break; } diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index e27bfa5a46..c671d417e9 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -45,6 +45,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QWidget; @@ -66,7 +68,8 @@ public: AppTime, AppUserTime, ScreenHintStyle, - StartupId + StartupId, + TrayWindow }; QXcbNativeInterface(); @@ -95,6 +98,12 @@ public: static void *glxContextForContext(QOpenGLContext *context); Q_INVOKABLE void beep(); + Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const; + Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window); + Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window); + +signals: + void systemTrayWindowChanged(QScreen *screen); private: const QByteArray m_genericEventFilterType; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 37c6c97bc4..0971b6ca8e 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -112,6 +112,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE + | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification). }; xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values); diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp new file mode 100644 index 0000000000..24d2feb106 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbsystemtraytracker.h" +#include "qxcbconnection.h" +#include "qxcbscreen.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +enum { + SystemTrayRequestDock = 0, + SystemTrayBeginMessage = 1, + SystemTrayCancelMessage = 2 +}; + +// QXcbSystemTrayTracker provides API for accessing the tray window and tracks +// its lifecyle by listening for its destruction and recreation. +// See http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html + +QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection) +{ + // Selection, tray atoms for GNOME, NET WM Specification + const xcb_atom_t trayAtom = connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_OPCODE); + if (!trayAtom) + return 0; + const QByteArray netSysTray = QByteArrayLiteral("_NET_SYSTEM_TRAY_S") + QByteArray::number(connection->primaryScreen()); + const xcb_atom_t selection = connection->internAtom(netSysTray.constData()); + if (!selection) + return 0; + return new QXcbSystemTrayTracker(connection, trayAtom, selection, connection); +} + +QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection, + xcb_atom_t trayAtom, + xcb_atom_t selection, + QObject *parent) + : QObject(parent) + , m_selection(selection) + , m_trayAtom(trayAtom) + , m_connection(connection) + , m_trayWindow(0) +{ +} + +xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection) +{ + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(connection->xcb_connection(), selection); + xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(connection->xcb_connection(), cookie, 0); + if (!reply) + return 0; + const xcb_window_t result = reply->owner; + free(reply); + return result; +} + +// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Request a window +// to be docked on the tray. +void QXcbSystemTrayTracker::requestSystemTrayWindowDock(xcb_window_t window) const +{ + xcb_client_message_event_t trayRequest; + memset(&trayRequest, 0, sizeof(trayRequest)); + trayRequest.response_type = XCB_CLIENT_MESSAGE; + trayRequest.format = 32; + trayRequest.window = m_trayWindow; + trayRequest.type = m_trayAtom; + trayRequest.data.data32[0] = XCB_CURRENT_TIME; + trayRequest.data.data32[1] = SystemTrayRequestDock; + trayRequest.data.data32[2] = window; + xcb_send_event(m_connection->xcb_connection(), 0, m_trayWindow, XCB_EVENT_MASK_NO_EVENT, (const char *)&trayRequest); +} + +// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Return tray window. +xcb_window_t QXcbSystemTrayTracker::trayWindow() +{ + if (!m_trayWindow) { + m_trayWindow = QXcbSystemTrayTracker::locateTrayWindow(m_connection, m_selection); + if (m_trayWindow) { // Listen for DestroyNotify on tray. + m_connection->addWindowEventListener(m_trayWindow, this); + const quint32 mask = XCB_CW_EVENT_MASK; + const quint32 value = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + Q_XCB_CALL(xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value)); + } + } + return m_trayWindow; +} + +// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Return the geometry of a +// a window parented on the tray. Determines the global geometry via XCB since mapToGlobal +// does not work for the QWindow parented on the tray. +QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const +{ + xcb_connection_t *conn = m_connection->xcb_connection(); + xcb_get_geometry_reply_t *geomReply = + xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0); + if (!geomReply) + return QRect(); + + xcb_translate_coordinates_reply_t *translateReply = + xcb_translate_coordinates_reply(conn, xcb_translate_coordinates(conn, window, m_connection->rootWindow(), 0, 0), 0); + if (!translateReply) { + free(geomReply); + return QRect(); + } + + const QRect result(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height)); + free(translateReply); + return result; +} + +inline void QXcbSystemTrayTracker::emitSystemTrayWindowChanged() +{ + const int screen = m_connection->primaryScreen(); + if (screen >= 0 && screen < m_connection->screens().size()) { + const QPlatformScreen *ps = m_connection->screens().at(screen); + emit systemTrayWindowChanged(ps->screen()); + } +} + +// Client messages with the "MANAGER" atom on the root window indicate creation of a new tray. +void QXcbSystemTrayTracker::notifyManagerClientMessageEvent(const xcb_client_message_event_t *t) +{ + if (t->data.data32[1] == m_selection) + emitSystemTrayWindowChanged(); +} + +// Listen for destruction of the tray. +void QXcbSystemTrayTracker::handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *event) +{ + if (event->window == m_trayWindow) { + m_connection->removeWindowEventListener(m_trayWindow); + m_trayWindow = 0; + emitSystemTrayWindowChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h new file mode 100644 index 0000000000..c6b0a0659e --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBSYSTEMTRAYTRACKER_H +#define QXCBSYSTEMTRAYTRACKER_H + +#include "qxcbconnection.h" + +#include + +QT_BEGIN_NAMESPACE + +class QXcbConnection; +class QScreen; + +class QXcbSystemTrayTracker : public QObject, public QXcbWindowEventListener +{ + Q_OBJECT +public: + static QXcbSystemTrayTracker *create(QXcbConnection *connection); + + xcb_window_t trayWindow(); + void requestSystemTrayWindowDock(xcb_window_t window) const; + QRect systemTrayWindowGlobalGeometry(xcb_window_t window) const; + + void notifyManagerClientMessageEvent(const xcb_client_message_event_t *); + + void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *); + +signals: + void systemTrayWindowChanged(QScreen *screen); + +private: + explicit QXcbSystemTrayTracker(QXcbConnection *connection, + xcb_atom_t trayAtom, + xcb_atom_t selection, + QObject *parent = 0); + static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection); + void emitSystemTrayWindowChanged(); + + const xcb_atom_t m_selection; + const xcb_atom_t m_trayAtom; + QXcbConnection *m_connection; + xcb_window_t m_trayWindow; +}; + +QT_END_NAMESPACE + +#endif // QXCBSYSTEMTRAYTRACKER_H diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 0325338a13..7f1d5ddf66 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1530,6 +1530,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even #endif } else if (event->type == atom(QXcbAtom::_XEMBED)) { handleXEmbedMessage(event); + } else if (event->type == atom(QXcbAtom::MANAGER) || event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) { + // Ignore _NET_ACTIVE_WINDOW which is received when the user clicks on a system tray icon and + // MANAGER which indicates the creation of a system tray. } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index 82995286c4..b198ab1717 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -21,7 +21,8 @@ SOURCES = \ qxcbnativeinterface.cpp \ qxcbcursor.cpp \ qxcbimage.cpp \ - qxcbxsettings.cpp + qxcbxsettings.cpp \ + qxcbsystemtraytracker.cpp HEADERS = \ qxcbclipboard.h \ @@ -38,7 +39,8 @@ HEADERS = \ qxcbnativeinterface.h \ qxcbcursor.h \ qxcbimage.h \ - qxcbxsettings.h + qxcbxsettings.h \ + qxcbsystemtraytracker.h LIBS += -ldl diff --git a/src/widgets/util/qsystemtrayicon_x11.cpp b/src/widgets/util/qsystemtrayicon_x11.cpp index 5204b85a9a..347e5701c0 100644 --- a/src/widgets/util/qsystemtrayicon_x11.cpp +++ b/src/widgets/util/qsystemtrayicon_x11.cpp @@ -57,84 +57,19 @@ #include #include -#include -#include -#include -#include - #ifndef QT_NO_SYSTEMTRAYICON QT_BEGIN_NAMESPACE -enum { - SYSTEM_TRAY_REQUEST_DOCK = 0, - SYSTEM_TRAY_BEGIN_MESSAGE = 1, - SYSTEM_TRAY_CANCEL_MESSAGE =2 -}; - -// ### fixme (15.3.2012): The following issues need to be resolved: -// - Tracking of the actual tray window for DestroyNotify and re-creation -// of the icons on the new window should it change (see Qt 4.X). - -// Global context for the X11 system tray containing a display for the primary -// screen and a selection atom from which the tray window can be determined. -class QX11SystemTrayContext -{ -public: - QX11SystemTrayContext(); - ~QX11SystemTrayContext(); - - bool isValid() const { return m_systemTraySelection != 0; } - - inline Display *display() const { return m_display; } - inline int screenNumber() const { return m_screenNumber; } - Window locateSystemTray() const; - -private: - Display *m_display; - int m_screenNumber; - Atom m_systemTraySelection; -}; - -QX11SystemTrayContext::QX11SystemTrayContext() : m_display(0), m_screenNumber(0), m_systemTraySelection(0) -{ - QScreen *screen = QGuiApplication::primaryScreen(); - if (!screen) { - qWarning("%s: No screen.", Q_FUNC_INFO); - return; - } - void *displayV = QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("display"), screen); - if (!displayV) { - qWarning("%s: Unable to obtain X11 display of primary screen.", Q_FUNC_INFO); - return; - } - - m_display = static_cast(displayV); - - const QByteArray netSysTray = "_NET_SYSTEM_TRAY_S" + QByteArray::number(m_screenNumber); - m_systemTraySelection = XInternAtom(m_display, netSysTray.constData(), False); - if (!m_systemTraySelection) { - qWarning("%s: Unable to retrieve atom '%s'.", Q_FUNC_INFO, netSysTray.constData()); - return; - } -} - -Window QX11SystemTrayContext::locateSystemTray() const +static inline unsigned long locateSystemTray() { - if (isValid()) - return XGetSelectionOwner(m_display, m_systemTraySelection); - return 0; + return (unsigned long)QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("traywindow"), QGuiApplication::primaryScreen()); } -QX11SystemTrayContext::~QX11SystemTrayContext() -{ -} - -Q_GLOBAL_STATIC(QX11SystemTrayContext, qX11SystemTrayContext) - // System tray widget. Could be replaced by a QWindow with // a backing store if it did not need tooltip handling. class QSystemTrayIconSys : public QWidget { + Q_OBJECT public: explicit QSystemTrayIconSys(QSystemTrayIcon *q); @@ -149,7 +84,12 @@ protected: virtual bool event(QEvent *); virtual void paintEvent(QPaintEvent *); +private slots: + void systemTrayWindowChanged(QScreen *screen); + private: + bool addToTray(); + QSystemTrayIcon *q; }; @@ -159,47 +99,59 @@ QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *qIn) { setObjectName(QStringLiteral("QSystemTrayIconSys")); setToolTip(q->toolTip()); - QX11SystemTrayContext *context = qX11SystemTrayContext(); - Q_ASSERT(context->isValid()); setAttribute(Qt::WA_AlwaysShowToolTips, true); setAttribute(Qt::WA_TranslucentBackground, true); setAttribute(Qt::WA_QuitOnClose, false); const QSize size(22, 22); // Gnome, standard size setGeometry(QRect(QPoint(0, 0), size)); setMinimumSize(size); + addToTray(); +} + +bool QSystemTrayIconSys::addToTray() +{ + if (!locateSystemTray()) + return false; + createWinId(); setMouseTracking(true); - Display *display = context->display(); - - // Request to be a tray window according to GNOME, NET WM Specification - static Atom netwm_tray_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False); - long l[5] = { CurrentTime, SYSTEM_TRAY_REQUEST_DOCK, static_cast(winId()), 0, 0 }; - XEvent ev; - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = context->locateSystemTray(); - ev.xclient.message_type = netwm_tray_atom; - ev.xclient.format = 32; - memcpy((char *)&ev.xclient.data, (const char *) l, sizeof(l)); - XSendEvent(display, ev.xclient.window, False, 0, &ev); + bool requestResult = false; + if (!QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), + "requestSystemTrayWindowDock", Qt::DirectConnection, + Q_RETURN_ARG(bool, requestResult), + Q_ARG(const QWindow *, windowHandle())) + || !requestResult) { + qWarning("requestSystemTrayWindowDock failed."); + return false; + } show(); + return true; } -QRect QSystemTrayIconSys::globalGeometry() const +void QSystemTrayIconSys::systemTrayWindowChanged(QScreen *) { - QX11SystemTrayContext *context = qX11SystemTrayContext(); - ::Window dummy; - int x, y, rootX, rootY; - unsigned int width, height, border, depth; - // Use X11 API since we are parented on the tray, about which the QWindow does not know. - XGetGeometry(context->display(), winId(), &dummy, &x, &y, &width, &height, &border, &depth); - XTranslateCoordinates(context->display(), winId(), - XRootWindow(context->display(), context->screenNumber()), - x, y, &rootX, &rootY, &dummy); - return QRect(QPoint(rootX, rootY), QSize(width, height)); + if (locateSystemTray()) { + addToTray(); + } else { + QBalloonTip::hideBalloon(); + hide(); // still no luck + destroy(); + } } +QRect QSystemTrayIconSys::globalGeometry() const +{ + QRect result; + if (!QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), + "systemTrayWindowGlobalGeometry", Qt::DirectConnection, + Q_RETURN_ARG(QRect, result), + Q_ARG(const QWindow *, windowHandle())) + || !result.isValid()) { + qWarning("systemTrayWindowGlobalGeometry failed."); + } + return result; +} void QSystemTrayIconSys::mousePressEvent(QMouseEvent *ev) { @@ -268,8 +220,11 @@ QSystemTrayIconPrivate::~QSystemTrayIconPrivate() void QSystemTrayIconPrivate::install_sys() { Q_Q(QSystemTrayIcon); - if (!sys && qX11SystemTrayContext()->isValid()) + if (!sys && locateSystemTray()) { sys = new QSystemTrayIconSys(q); + QObject::connect(QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)), + sys, SLOT(systemTrayWindowChanged(QScreen*))); + } } QRect QSystemTrayIconPrivate::geometry_sys() const @@ -313,7 +268,7 @@ bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() { const QString platform = QGuiApplication::platformName(); if (platform.compare(QStringLiteral("xcb"), Qt::CaseInsensitive) == 0) - return qX11SystemTrayContext()->locateSystemTray() != None; + return locateSystemTray(); return false; } @@ -334,4 +289,7 @@ void QSystemTrayIconPrivate::showMessage_sys(const QString &message, const QStri } QT_END_NAMESPACE + +#include "qsystemtrayicon_x11.moc" + #endif //QT_NO_SYSTEMTRAYICON diff --git a/src/widgets/util/util.pri b/src/widgets/util/util.pri index 598a3082c0..072c736f71 100644 --- a/src/widgets/util/util.pri +++ b/src/widgets/util/util.pri @@ -31,7 +31,6 @@ win32:!wince* { SOURCES += util/qsystemtrayicon_win.cpp } else:contains(QT_CONFIG, xcb) { SOURCES += util/qsystemtrayicon_x11.cpp - CONFIG += x11 } else { SOURCES += util/qsystemtrayicon_qpa.cpp } -- cgit v1.2.3