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:
parent
983e921c54
commit
d8090022f6
@ -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)
|
||||
{
|
||||
|
@ -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()))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
180
src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
Normal file
180
src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
Normal 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
|
87
src/plugins/platforms/xcb/qxcbsystemtraytracker.h
Normal file
87
src/plugins/platforms/xcb/qxcbsystemtraytracker.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user