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 <mardy@users.sourceforge.net>
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Friedemann Kleint 2013-06-28 11:51:29 +02:00 committed by The Qt Project
parent 983e921c54
commit d8090022f6
11 changed files with 404 additions and 100 deletions

View File

@ -51,6 +51,7 @@
#include "qxcbwmsupport.h"
#include "qxcbnativeinterface.h"
#include "qxcbintegration.h"
#include "qxcbsystemtraytracker.h"
#include <QtAlgorithms>
#include <QSocketNotifier>
@ -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)
{

View File

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

View File

@ -42,7 +42,9 @@
#include "qxcbnativeinterface.h"
#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "qxcbintegration.h"
#include "qxcbsystemtraytracker.h"
#include <private/qguiapplication_p.h>
#include <QtCore/QMap>
@ -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<const QXcbScreen *>(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<const QXcbWindow *>(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<const QXcbWindow *>(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<void *>(xcbScreen->hintStyle() + 1);
break;
case TrayWindow:
if (QXcbSystemTrayTracker *s = systemTrayTracker(screen))
result = (void *)quintptr(s->trayWindow());
break;
default:
break;
}

View File

@ -45,6 +45,8 @@
#include <qpa/qplatformnativeinterface.h>
#include <xcb/xcb.h>
#include <QtCore/QRect>
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;

View File

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

View File

@ -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 <QtCore/QDebug>
#include <QtCore/QRect>
#include <QtGui/QScreen>
#include <qpa/qplatformnativeinterface.h>
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

View File

@ -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 <xcb/xcb.h>
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

View File

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

View File

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

View File

@ -57,84 +57,19 @@
#include <qpa/qplatformnativeinterface.h>
#include <qdebug.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#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
static inline unsigned long locateSystemTray()
{
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<Display *>(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;
}
return (unsigned long)QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("traywindow"), QGuiApplication::primaryScreen());
}
Window QX11SystemTrayContext::locateSystemTray() const
{
if (isValid())
return XGetSelectionOwner(m_display, m_systemTraySelection);
return 0;
}
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,48 +99,60 @@ 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<long>(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;
}
void QSystemTrayIconSys::systemTrayWindowChanged(QScreen *)
{
if (locateSystemTray()) {
addToTray();
} else {
QBalloonTip::hideBalloon();
hide(); // still no luck
destroy();
}
}
QRect QSystemTrayIconSys::globalGeometry() const
{
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));
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)
{
QPoint globalPos = ev->globalPos();
@ -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

View File

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