1
/****************************************************************************
3
** Copyright (C) 2016 The Qt Company Ltd.
4
** Contact: https://www.qt.io/licensing/
6
** This file is part of the QtWidgets module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and The Qt Company. For licensing terms
14
** and conditions see https://www.qt.io/terms-conditions. For further
15
** information use the contact form at https://www.qt.io/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 3 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL3 included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 3 requirements
23
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25
** GNU General Public License Usage
26
** Alternatively, this file may be used under the terms of the GNU
27
** General Public License version 2.0 or (at your option) the GNU General
28
** Public license version 3 or any later version approved by the KDE Free
29
** Qt Foundation. The licenses are as published by the Free Software
30
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31
** included in the packaging of this file. Please review the following
32
** information to ensure the GNU General Public License requirements will
33
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34
** https://www.gnu.org/licenses/gpl-3.0.html.
38
****************************************************************************/
45
#include "qapplication.h"
49
#include "qsystemtrayicon_p.h"
50
#include "qpaintengine.h"
52
#include <qguiapplication.h>
54
#include <qbackingstore.h>
55
#include <qpa/qplatformnativeinterface.h>
56
#include <qpa/qplatformsystemtrayicon.h>
57
#include <qpa/qplatformtheme.h>
58
#include <private/qguiapplication_p.h>
61
#include <QtPlatformHeaders/qxcbwindowfunctions.h>
62
#include <QtPlatformHeaders/qxcbintegrationfunctions.h>
63
#ifndef QT_NO_SYSTEMTRAYICON
66
static inline unsigned long locateSystemTray()
68
return (unsigned long)QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("traywindow"), QGuiApplication::primaryScreen());
71
// System tray widget. Could be replaced by a QWindow with
72
// a backing store if it did not need tooltip handling.
73
class QSystemTrayIconSys : public QWidget
77
explicit QSystemTrayIconSys(QSystemTrayIcon *q);
79
inline void updateIcon() { update(); }
80
inline QSystemTrayIcon *systemTrayIcon() const { return q; }
82
QRect globalGeometry() const;
85
virtual void mousePressEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;
86
virtual void mouseDoubleClickEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;
87
virtual bool event(QEvent *) Q_DECL_OVERRIDE;
88
virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
89
virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;
90
virtual void moveEvent(QMoveEvent *) Q_DECL_OVERRIDE;
93
void systemTrayWindowChanged(QScreen *screen);
102
QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *qIn)
103
: QWidget(0, Qt::Window | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint)
106
setObjectName(QStringLiteral("QSystemTrayIconSys"));
107
setToolTip(q->toolTip());
108
setAttribute(Qt::WA_AlwaysShowToolTips, true);
109
setAttribute(Qt::WA_QuitOnClose, false);
110
const QSize size(22, 22); // Gnome, standard size
111
setGeometry(QRect(QPoint(0, 0), size));
112
setMinimumSize(size);
114
// We need two different behaviors depending on whether the X11 visual for the system tray
115
// (a) exists and (b) supports an alpha channel, i.e. is 32 bits.
116
// If we have a visual that has an alpha channel, we can paint this widget with a transparent
117
// background and it will work.
118
// However, if there's no alpha channel visual, in order for transparent tray icons to work,
119
// we do not have a transparent background on the widget, but set the BackPixmap property of our
120
// window to ParentRelative (so that it inherits the background of its X11 parent window), call
121
// xcb_clear_region before painting (so that the inherited background is visible) and then grab
122
// the just-drawn background from the X11 server.
123
bool hasAlphaChannel = QXcbIntegrationFunctions::xEmbedSystemTrayVisualHasAlphaChannel();
124
setAttribute(Qt::WA_TranslucentBackground, hasAlphaChannel);
125
if (!hasAlphaChannel) {
127
QXcbWindowFunctions::setParentRelativeBackPixmap(windowHandle());
129
// XXX: This is actually required, but breaks things ("QWidget::paintEngine: Should no
130
// longer be called"). Why is this needed? When the widget is drawn, we use tricks to grab
131
// the tray icon's background from the server. If the tray icon isn't visible (because
132
// another window is on top of it), the trick fails and instead uses the content of that
133
// other window as the background.
134
// setAttribute(Qt::WA_PaintOnScreen);
140
bool QSystemTrayIconSys::addToTray()
142
if (!locateSystemTray())
146
setMouseTracking(true);
148
if (!QXcbWindowFunctions::requestSystemTrayWindowDock(windowHandle()))
151
if (!background.isNull())
152
background = QPixmap();
157
void QSystemTrayIconSys::systemTrayWindowChanged(QScreen *)
159
if (locateSystemTray()) {
162
QBalloonTip::hideBalloon();
163
hide(); // still no luck
168
QRect QSystemTrayIconSys::globalGeometry() const
170
return QXcbWindowFunctions::systemTrayWindowGlobalGeometry(windowHandle());
173
void QSystemTrayIconSys::mousePressEvent(QMouseEvent *ev)
175
QPoint globalPos = ev->globalPos();
176
#ifndef QT_NO_CONTEXTMENU
177
if (ev->button() == Qt::RightButton && q->contextMenu())
178
q->contextMenu()->popup(globalPos);
181
#endif // QT_NO_CONTEXTMENU
183
if (QBalloonTip::isBalloonVisible()) {
184
emit q->messageClicked();
185
QBalloonTip::hideBalloon();
188
if (ev->button() == Qt::LeftButton)
189
emit q->activated(QSystemTrayIcon::Trigger);
190
else if (ev->button() == Qt::RightButton)
191
emit q->activated(QSystemTrayIcon::Context);
192
else if (ev->button() == Qt::MidButton)
193
emit q->activated(QSystemTrayIcon::MiddleClick);
196
void QSystemTrayIconSys::mouseDoubleClickEvent(QMouseEvent *ev)
198
if (ev->button() == Qt::LeftButton)
199
emit q->activated(QSystemTrayIcon::DoubleClick);
202
bool QSystemTrayIconSys::event(QEvent *e)
205
case QEvent::ToolTip:
206
QApplication::sendEvent(q, e);
208
#ifndef QT_NO_WHEELEVENT
210
return QApplication::sendEvent(q, e);
215
return QWidget::event(e);
218
void QSystemTrayIconSys::paintEvent(QPaintEvent *)
220
const QRect rect(QPoint(0, 0), geometry().size());
221
QPainter painter(this);
223
// If we have Qt::WA_TranslucentBackground set, during widget creation
224
// we detected the systray visual supported an alpha channel
225
if (testAttribute(Qt::WA_TranslucentBackground)) {
226
painter.setCompositionMode(QPainter::CompositionMode_Source);
227
painter.fillRect(rect, Qt::transparent);
229
// clearRegion() was called on XEMBED_EMBEDDED_NOTIFY, so we hope that got done by now.
230
// Grab the tray background pixmap, before rendering the icon for the first time.
231
if (background.isNull()) {
232
background = QGuiApplication::primaryScreen()->grabWindow(winId(),
233
0, 0, rect.size().width(), rect.size().height());
235
// Then paint over the icon area with the background before compositing the icon on top.
236
painter.drawPixmap(QPoint(0, 0), background);
238
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
239
q->icon().paint(&painter, rect);
242
void QSystemTrayIconSys::moveEvent(QMoveEvent *event)
244
QWidget::moveEvent(event);
245
if (QBalloonTip::isBalloonVisible())
246
QBalloonTip::updateBalloonPosition(globalGeometry().center());
249
void QSystemTrayIconSys::resizeEvent(QResizeEvent *event)
252
QWidget::resizeEvent(event);
253
if (QBalloonTip::isBalloonVisible())
254
QBalloonTip::updateBalloonPosition(globalGeometry().center());
256
////////////////////////////////////////////////////////////////////////////
258
QSystemTrayIconPrivate::QSystemTrayIconPrivate()
260
qpa_sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()),
265
QSystemTrayIconPrivate::~QSystemTrayIconPrivate()
270
void QSystemTrayIconPrivate::install_sys()
276
Q_Q(QSystemTrayIcon);
277
if (!sys && locateSystemTray()) {
278
sys = new QSystemTrayIconSys(q);
279
QObject::connect(QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)),
280
sys, SLOT(systemTrayWindowChanged(QScreen*)));
284
QRect QSystemTrayIconPrivate::geometry_sys() const
287
return geometry_sys_qpa();
290
return sys->globalGeometry();
293
void QSystemTrayIconPrivate::remove_sys()
301
QBalloonTip::hideBalloon();
302
sys->hide(); // this should do the trick, but...
303
delete sys; // wm may resize system tray only for DestroyEvents
307
void QSystemTrayIconPrivate::updateIcon_sys()
310
updateIcon_sys_qpa();
317
void QSystemTrayIconPrivate::updateMenu_sys()
320
updateMenu_sys_qpa();
323
void QSystemTrayIconPrivate::updateToolTip_sys()
326
updateToolTip_sys_qpa();
331
#ifndef QT_NO_TOOLTIP
332
sys->setToolTip(toolTip);
336
bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
338
QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon());
339
if (sys && sys->isSystemTrayAvailable())
342
// no QPlatformSystemTrayIcon so fall back to default xcb platform behavior
343
const QString platform = QGuiApplication::platformName();
344
if (platform.compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0)
345
return locateSystemTray();
349
bool QSystemTrayIconPrivate::supportsMessages_sys()
351
QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon());
353
return sys->supportsMessages();
355
// no QPlatformSystemTrayIcon so fall back to default xcb platform behavior
359
void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message,
360
QSystemTrayIcon::MessageIcon icon, int msecs)
363
showMessage_sys_qpa(title, message, icon, msecs);
368
QBalloonTip::showBalloon(icon, title, message, sys->systemTrayIcon(),
369
sys->globalGeometry().center(),
375
#include "qsystemtrayicon_x11.moc"
377
#endif //QT_NO_SYSTEMTRAYICON