7#ifndef QT_NO_SYSTEMTRAYICON
12#include <QLoggingCategory>
13#include <QStandardPaths>
18#include <QDBusConnectionInterface>
19#include <QDBusArgument>
20#include <QDBusMetaType>
21#include <QDBusServiceWatcher>
23#include <qpa/qplatformmenu.h>
24#include <qpa/qplatformintegration.h>
25#include <qpa/qplatformservices.h>
27#include <private/qdbusmenuconnection_p.h>
28#include <private/qstatusnotifieritemadaptor_p.h>
29#include <private/qdbusmenuadaptor_p.h>
30#include <private/qdbusplatformmenu_p.h>
31#include <private/qxdgnotificationproxy_p.h>
32#include <private/qlockfile_p.h>
33#include <private/qguiapplication_p.h>
40using namespace Qt::StringLiterals;
46 QString tempPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
47 if (!tempPath.isEmpty()) {
48 QString flatpakId = qEnvironmentVariable(
"FLATPAK_ID");
49 if (!flatpakId.isEmpty() && QFileInfo::exists(
"/.flatpak-info"_L1))
50 tempPath +=
"/app/"_L1 + flatpakId;
54 tempPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
56 if (!tempPath.isEmpty()) {
57 QDir tempDir(tempPath);
61 if (tempDir.mkpath(QStringLiteral(
"."))) {
62 const QFile::Permissions permissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
63 if (QFile(tempPath).setPermissions(permissions))
68 return QDir::tempPath();
80 static const QString TempFileTemplate = iconTempPath() +
"/qt-trayicon-XXXXXX.png"_L1;
81 return TempFileTemplate;
86 QImage image = img.ico.pixmap(QSize(64, 64), img.dpr).toImage();
87 const bool hasAlpha = image.hasAlphaChannel();
88 image = image.convertToFormat(hasAlpha ? QImage::Format_RGBA8888 : QImage::Format_RGB888);
90 argument.beginStructure();
91 argument << image.width() << image.height()
92 << image.bytesPerLine() << hasAlpha
93 << 8 << (hasAlpha ? 4 : 3)
94 << QByteArray((
const char *)image.constBits(), image.sizeInBytes());
95 argument.endStructure();
100 Q_ASSERT_X(
false,
__FUNCTION__,
"Not implemented");
105
106
107
110 : m_dbusConnection(
nullptr)
111 , m_adaptor(
new QStatusNotifierItemAdaptor(
this))
112 , m_menuAdaptor(
nullptr)
114 , m_notifier(
nullptr)
115 , m_instanceId(KDEItemFormat.arg(QCoreApplication::applicationPid()).arg(++instanceCount))
116 , m_category(QStringLiteral(
"ApplicationStatus"))
117 , m_defaultStatus(QStringLiteral(
"Active"))
118 , m_status(m_defaultStatus)
119 , m_tempIcon(
nullptr)
120 , m_tempAttentionIcon(
nullptr)
121 , m_registered(
false)
125 QDBusMenuItem::registerDBusTypes();
126 qDBusRegisterMetaType<QXdgDBusImageStruct>();
127 qDBusRegisterMetaType<QXdgDBusImageVector>();
128 qDBusRegisterMetaType<QXdgDBusToolTipStruct>();
130 connect(
this, SIGNAL(statusChanged(QString)), m_adaptor, SIGNAL(NewStatus(QString)));
132 connect(
this, SIGNAL(
iconChanged()), m_adaptor, SIGNAL(NewIcon()));
133 connect(
this, SIGNAL(
attention()), m_adaptor, SIGNAL(NewAttentionIcon()));
134 connect(
this, SIGNAL(
menuChanged()), m_adaptor, SIGNAL(NewMenu()));
135 connect(
this, SIGNAL(
attention()), m_adaptor, SIGNAL(NewTitle()));
136 connect(&m_attentionTimer, SIGNAL(timeout()),
this, SLOT(attentionTimerExpired()));
137 m_attentionTimer.setSingleShot(
true);
146 qCDebug(qLcTray) <<
"registering" << m_instanceId;
147 m_registered = dBusConnection()->registerTrayIcon(
this);
148 QObject::connect(dBusConnection()->dbusWatcher(), &QDBusServiceWatcher::serviceRegistered,
149 this, &QDBusTrayIcon::watcherServiceRegistered);
154 qCDebug(qLcTray) <<
"unregistering" << m_instanceId;
156 dBusConnection()->unregisterTrayIcon(
this);
157 delete m_dbusConnection;
158 m_dbusConnection =
nullptr;
160 m_notifier =
nullptr;
161 m_registered =
false;
164void QDBusTrayIcon::watcherServiceRegistered(
const QString &serviceName)
166 Q_UNUSED(serviceName);
170 dBusConnection()->registerTrayIconWithWatcher(
this);
175 m_messageTitle = QString();
176 m_message = QString();
177 m_attentionIcon = QIcon();
179 emit tooltipChanged();
180 setStatus(m_defaultStatus);
185 qCDebug(qLcTray) << status;
186 if (m_status == status)
189 emit statusChanged(m_status);
196 static bool necessity_checked =
false;
197 static bool necessary =
false;
198 if (!necessity_checked) {
199 QDBusConnection session = QDBusConnection::sessionBus();
200 uint pid = session.interface()->servicePid(KDEWatcherService).value();
201 QString processName = QLockFilePrivate::processNameByPid(pid);
202 necessary = processName.endsWith(
"indicator-application-service"_L1);
204 necessary = session.interface()->isServiceRegistered(
205 QStringLiteral(
"com.canonical.indicator.application"));
208 necessary = session.interface()->isServiceRegistered(
209 QStringLiteral(
"org.ayatana.indicator.application"));
211 if (!necessary && QGuiApplication::desktopSettingsAware()) {
214 const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
216 necessary = services->desktopEnvironment().split(
':').contains(
"UNITY");
218 necessity_checked =
true;
222 QTemporaryFile *ret =
new QTemporaryFile(tempFileTemplate(),
this);
227 icon.pixmap(QSize(22, 22)).save(ret);
234 if (!m_dbusConnection) {
235 m_dbusConnection =
new QDBusMenuConnection(
this, m_instanceId);
236 m_notifier =
new QXdgNotificationInterface(XdgNotificationService,
237 XdgNotificationPath, m_dbusConnection->connection(),
this);
238 connect(m_notifier, SIGNAL(NotificationClosed(uint,uint)),
this, SLOT(notificationClosed(uint,uint)));
239 connect(m_notifier, SIGNAL(ActionInvoked(uint,QString)),
this, SLOT(actionInvoked(uint,QString)));
241 return m_dbusConnection;
246 m_iconName = icon.name();
248 if (m_iconName.isEmpty()) {
251 m_tempIcon = tempIcon(icon);
253 m_iconName = m_tempIcon->fileName();
255 qCDebug(qLcTray) << m_iconName << icon.availableSizes();
261 qCDebug(qLcTray) << tooltip;
263 emit tooltipChanged();
268 return new QDBusPlatformMenu();
273 qCDebug(qLcTray) << menu;
275 if (m_menu != newMenu) {
277 dBusConnection()->unregisterTrayIconMenu(
this);
278 delete m_menuAdaptor;
281 m_menuAdaptor =
new QDBusMenuAdaptor(m_menu);
283 connect(m_menu, SIGNAL(propertiesUpdated(QDBusMenuItemList,QDBusMenuItemKeysList)),
284 m_menuAdaptor, SIGNAL(ItemsPropertiesUpdated(QDBusMenuItemList,QDBusMenuItemKeysList)));
285 connect(m_menu, SIGNAL(updated(uint,
int)),
286 m_menuAdaptor, SIGNAL(LayoutUpdated(uint,
int)));
287 dBusConnection()->registerTrayIconMenu(
this);
293 QPlatformSystemTrayIcon::MessageIcon iconType,
int msecs)
295 m_messageTitle = title;
297 m_attentionIcon = icon;
298 QStringList notificationActions;
301 m_attentionIconName = QStringLiteral(
"dialog-information");
304 m_attentionIconName = QStringLiteral(
"dialog-warning");
307 m_attentionIconName = QStringLiteral(
"dialog-error");
311 notificationActions << DefaultAction << tr(
"OK");
314 m_attentionIconName.clear();
317 if (m_attentionIconName.isEmpty()) {
318 if (m_tempAttentionIcon)
319 delete m_tempAttentionIcon;
320 m_tempAttentionIcon = tempIcon(icon);
321 if (m_tempAttentionIcon)
322 m_attentionIconName = m_tempAttentionIcon->fileName();
324 qCDebug(qLcTray) << title << msg <<
325 QPlatformSystemTrayIcon::metaObject()->enumerator(
326 QPlatformSystemTrayIcon::staticMetaObject.indexOfEnumerator(
"MessageIcon")).valueToKey(iconType)
327 << m_attentionIconName << msecs;
328 setStatus(QStringLiteral(
"NeedsAttention"));
329 m_attentionTimer.start(msecs);
330 emit tooltipChanged();
337 int urgency =
static_cast<
int>(iconType) - 1;
340 hints.insert(
"urgency"_L1, QVariant(urgency));
341 if (m_attentionIconName.isEmpty()) {
344 hints.insert(
"image_data"_L1, QVariant::fromValue(img));
346 m_notifier->notify(QCoreApplication::applicationName(), 0,
347 m_attentionIconName, title, msg, notificationActions, hints, msecs);
350void QDBusTrayIcon::actionInvoked(uint id,
const QString &action)
352 qCDebug(qLcTray) << id << action;
353 emit messageClicked();
358 qCDebug(qLcTray) << id << reason;
363 QDBusMenuConnection * conn =
const_cast<
QDBusTrayIcon *>(
this)->dBusConnection();
367 qCDebug(qLcTray) << conn->isWatcherRegistered();
368 return conn->isWatcherRegistered();
373#include "moc_qdbustrayicon_p.cpp"
void init() override
This method is called to initialize the platform dependent implementation.
void updateMenu(QPlatformMenu *menu) override
This method is called when the system tray menu did change.
void showMessage(const QString &title, const QString &msg, const QIcon &icon, MessageIcon iconType, int msecs) override
Shows a balloon message for the entry with the given title, message msg and icon for the time specifi...
QDBusMenuConnection * dBusConnection()
void cleanup() override
This method is called to cleanup the platform dependent implementation.
void updateIcon(const QIcon &icon) override
This method is called when the icon did change.
void updateToolTip(const QString &tooltip) override
This method is called when the tooltip text did change.
bool isSystemTrayAvailable() const override
Returns true if the system tray is available on the platform.
QPlatformMenu * createMenu() const override
This method allows platforms to use a different QPlatformMenu for system tray menus than what would n...
QDBusArgument & operator<<(QDBusArgument &argument, const QDBusTrayImage &img)
static const QString KDEWatcherService
static const QString DefaultAction
static const QString KDEItemFormat
static QString tempFileTemplate()
const QDBusArgument & operator>>(const QDBusArgument &argument, QDBusTrayImage &)
static const QString XdgNotificationPath
static QString iconTempPath()
static const QString XdgNotificationService
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")