feet(plugin): StatusNotifierItem system tray protocol support

Change-Id: I3b879b052e46f2eda758085dfb4701d23923d445
This commit is contained in:
listenerri 2018-08-16 10:05:16 +08:00
parent 62f381d6ba
commit 68a4332ad1
Notes: gerrit 2018-10-16 11:41:57 +08:00
Verified+1: <jenkins@deepin.com>
Code-Review+2: listenerri <listenerri@gmail.com>
Submitted-by: listenerri <listenerri@gmail.com>
Submitted-at: Tue, 16 Oct 2018 11:41:56 +0800
Reviewed-on: https://cr.deepin.io/37795
Project: dde/dde-dock
Branch: refs/heads/master
14 changed files with 1080 additions and 31 deletions

8
debian/control vendored
View File

@ -10,11 +10,9 @@ Build-Depends: debhelper (>= 8.0.0), pkg-config,
libxcb-icccm4-dev, libxtst-dev,
libdtkwidget-dev, libdtkcore-dev | libdtkutil-dev,
qttools5-dev-tools, libxcb-icccm4-dev,
qtbase5-private-dev,
libdframeworkdbus-dev,
libgsettings-qt-dev,
cmake,
libdde-network-utils-dev
qtbase5-private-dev, libdframeworkdbus-dev,
libgsettings-qt-dev, cmake,
libdde-network-utils-dev, libdbusmenu-qt5-dev
Standards-Version: 3.9.8
Homepage: http://www.deepin.org/

View File

@ -4,7 +4,8 @@ set(PLUGIN_NAME "system-tray")
project(${PLUGIN_NAME})
# Sources files
file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../../widgets/*.h" "../../widgets/*.cpp")
file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../../widgets/*.h" "../../widgets/*.cpp"
"../../frame/util/themeappicon.h" "../../frame/util/themeappicon.cpp")
find_package(PkgConfig REQUIRED)
find_package(Qt5Widgets REQUIRED)
@ -13,7 +14,7 @@ find_package(Qt5DBus REQUIRED)
find_package(Qt5X11Extras REQUIRED)
find_package(DtkWidget REQUIRED)
pkg_check_modules(XCB_LIBS REQUIRED xcb-ewmh xcb xcb-image xcb-composite xtst xcb-icccm)
pkg_check_modules(XCB_LIBS REQUIRED xcb-ewmh xcb xcb-image xcb-composite xtst xcb-icccm dbusmenu-qt5)
add_definitions("${QT_DEFINITIONS} -DQT_PLUGIN")
add_library(${PLUGIN_NAME} SHARED ${SRCS} system-tray.qrc)

View File

@ -0,0 +1,66 @@
/*
* Copyright 2011 Canonical Ltd.
* Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>.
*/
#include <QtDBus/qdbusmetatype.h>
#include "dbusstructures.h"
QDBusArgument &operator<<(QDBusArgument &argument, const DBusImage &image)
{
argument.beginStructure();
argument << image.width << image.height << image.pixels;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusImage &image)
{
argument.beginStructure();
argument >> image.width >> image.height >> image.pixels;
argument.endStructure();
return argument;
}
QDBusArgument &operator<<(QDBusArgument &argument, const DBusToolTip &tip)
{
argument.beginStructure();
argument << tip.iconName << tip.iconPixmap << tip.title << tip.description;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusToolTip &tip)
{
argument.beginStructure();
argument >> tip.iconName >> tip.iconPixmap >> tip.title >> tip.description;
argument.endStructure();
return argument;
}
void registerMetaTypes()
{
static bool registered = false;
if (registered) {
return;
}
qDBusRegisterMetaType<DBusImage>();
qDBusRegisterMetaType<DBusImageList>();
qDBusRegisterMetaType<DBusToolTip>();
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2011 Canonical Ltd.
* Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>.
*/
#ifndef DBUSSTRUCTURES_H
#define DBUSSTRUCTURES_H
#include <QtCore/qbytearray.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
#include <QtGui/qicon.h>
#include <QtGui/qpixmap.h>
#include <QtDBus/qdbusargument.h>
struct DBusImage
{
int width;
int height;
QByteArray pixels;
};
Q_DECLARE_METATYPE(DBusImage)
typedef QList<DBusImage> DBusImageList;
Q_DECLARE_METATYPE(DBusImageList)
struct DBusToolTip
{
QString iconName;
DBusImageList iconPixmap;
QString title;
QString description;
};
Q_DECLARE_METATYPE(DBusToolTip)
QDBusArgument &operator<<(QDBusArgument&, const DBusImage&);
const QDBusArgument &operator>>(const QDBusArgument&, DBusImage&);
QDBusArgument &operator<<(QDBusArgument&, const DBusToolTip&);
const QDBusArgument &operator>>(const QDBusArgument&, DBusToolTip&);
void registerMetaTypes();
#endif /* DBUSSTRUCTURES_H */

View File

@ -0,0 +1,23 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp item-inter.xml -a statusnotifieritemadapter -p statusnotifieritemproxy
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#include "statusnotifieritem_interface.h"
StatusNotifierItemInterface::StatusNotifierItemInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
registerMetaTypes();
}
StatusNotifierItemInterface::~StatusNotifierItemInterface()
{
}

View File

@ -0,0 +1,145 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp item-inter.xml -a statusnotifieritemadapter -p statusnotifieritemproxy
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#ifndef STATUSNOTIFIERITEM_H
#define STATUSNOTIFIERITEM_H
#include "dbusstructures.h"
#include <QtCore/QObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
/*
* Proxy class for interface org.kde.StatusNotifierItem
*/
class StatusNotifierItemInterface: public QDBusAbstractInterface
{
Q_OBJECT
public:
static inline const char *staticInterfaceName()
{ return "org.kde.StatusNotifierItem"; }
public:
StatusNotifierItemInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);
~StatusNotifierItemInterface();
Q_PROPERTY(QString AttentionIconName READ attentionIconName)
inline QString attentionIconName() const
{ return qvariant_cast< QString >(property("AttentionIconName")); }
Q_PROPERTY(DBusImageList AttentionIconPixmap READ attentionIconPixmap)
inline DBusImageList attentionIconPixmap() const
{ return qvariant_cast< DBusImageList >(property("AttentionIconPixmap")); }
Q_PROPERTY(QString AttentionMovieName READ attentionMovieName)
inline QString attentionMovieName() const
{ return qvariant_cast< QString >(property("AttentionMovieName")); }
Q_PROPERTY(QString Category READ category)
inline QString category() const
{ return qvariant_cast< QString >(property("Category")); }
Q_PROPERTY(QString IconName READ iconName)
inline QString iconName() const
{ return qvariant_cast< QString >(property("IconName")); }
Q_PROPERTY(DBusImageList IconPixmap READ iconPixmap)
inline DBusImageList iconPixmap() const
{ return qvariant_cast< DBusImageList >(property("IconPixmap")); }
Q_PROPERTY(QString Id READ id)
inline QString id() const
{ return qvariant_cast< QString >(property("Id")); }
Q_PROPERTY(bool ItemIsMenu READ itemIsMenu)
inline bool itemIsMenu() const
{ return qvariant_cast< bool >(property("ItemIsMenu")); }
Q_PROPERTY(QDBusObjectPath Menu READ menu)
inline QDBusObjectPath menu() const
{ return qvariant_cast< QDBusObjectPath >(property("Menu")); }
Q_PROPERTY(QString OverlayIconName READ overlayIconName)
inline QString overlayIconName() const
{ return qvariant_cast< QString >(property("OverlayIconName")); }
Q_PROPERTY(DBusImageList OverlayIconPixmap READ overlayIconPixmap)
inline DBusImageList overlayIconPixmap() const
{ return qvariant_cast< DBusImageList >(property("OverlayIconPixmap")); }
Q_PROPERTY(QString Status READ status)
inline QString status() const
{ return qvariant_cast< QString >(property("Status")); }
Q_PROPERTY(QString Title READ title)
inline QString title() const
{ return qvariant_cast< QString >(property("Title")); }
Q_PROPERTY(DBusToolTip ToolTip READ toolTip)
inline DBusToolTip toolTip() const
{ return qvariant_cast< DBusToolTip >(property("ToolTip")); }
Q_PROPERTY(int WindowId READ windowId)
inline int windowId() const
{ return qvariant_cast< int >(property("WindowId")); }
public Q_SLOTS: // METHODS
inline QDBusPendingReply<> Activate(int x, int y)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
return asyncCallWithArgumentList(QStringLiteral("Activate"), argumentList);
}
inline QDBusPendingReply<> ContextMenu(int x, int y)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
return asyncCallWithArgumentList(QStringLiteral("ContextMenu"), argumentList);
}
inline QDBusPendingReply<> Scroll(int delta, const QString &orientation)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(delta) << QVariant::fromValue(orientation);
return asyncCallWithArgumentList(QStringLiteral("Scroll"), argumentList);
}
inline QDBusPendingReply<> SecondaryActivate(int x, int y)
{
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
return asyncCallWithArgumentList(QStringLiteral("SecondaryActivate"), argumentList);
}
Q_SIGNALS: // SIGNALS
void NewAttentionIcon();
void NewIcon();
void NewOverlayIcon();
void NewStatus(const QString &status);
void NewTitle();
void NewToolTip();
};
namespace com {
namespace deepin {
namespace dde {
typedef ::StatusNotifierItemInterface StatusNotifierItem;
}
}
}
#endif

View File

@ -0,0 +1,65 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp watcher-inter.xml -a statusnotifierwatcheradapter -p statusnotifierwatcherproxy
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "statusnotifierwatcheradapter.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class StatusNotifierWatcherAdaptor
*/
StatusNotifierWatcherAdaptor::StatusNotifierWatcherAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
StatusNotifierWatcherAdaptor::~StatusNotifierWatcherAdaptor()
{
// destructor
}
bool StatusNotifierWatcherAdaptor::isStatusNotifierHostRegistered() const
{
// get the value of property IsStatusNotifierHostRegistered
return qvariant_cast< bool >(parent()->property("IsStatusNotifierHostRegistered"));
}
int StatusNotifierWatcherAdaptor::protocolVersion() const
{
// get the value of property ProtocolVersion
return qvariant_cast< int >(parent()->property("ProtocolVersion"));
}
QStringList StatusNotifierWatcherAdaptor::registeredStatusNotifierItems() const
{
// get the value of property RegisteredStatusNotifierItems
return qvariant_cast< QStringList >(parent()->property("RegisteredStatusNotifierItems"));
}
void StatusNotifierWatcherAdaptor::RegisterStatusNotifierHost(const QString &service)
{
// handle method call org.kde.StatusNotifierWatcher.RegisterStatusNotifierHost
QMetaObject::invokeMethod(parent(), "RegisterStatusNotifierHost", Q_ARG(QString, service));
}
void StatusNotifierWatcherAdaptor::RegisterStatusNotifierItem(const QString &service)
{
// handle method call org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem
QMetaObject::invokeMethod(parent(), "RegisterStatusNotifierItem", Q_ARG(QString, service));
}

View File

@ -0,0 +1,80 @@
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp watcher-inter.xml -a statusnotifierwatcheradapter -p statusnotifierwatcherproxy
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef STATUSNOTIFIERWATCHERADAPTER_H
#define STATUSNOTIFIERWATCHERADAPTER_H
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
QT_BEGIN_NAMESPACE
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
QT_END_NAMESPACE
/*
* Adaptor class for interface org.kde.StatusNotifierWatcher
*/
class StatusNotifierWatcherAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.StatusNotifierWatcher")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.kde.StatusNotifierWatcher\">\n"
" <method name=\"RegisterStatusNotifierItem\">\n"
" <arg direction=\"in\" type=\"s\" name=\"service\"/>\n"
" </method>\n"
" <method name=\"RegisterStatusNotifierHost\">\n"
" <arg direction=\"in\" type=\"s\" name=\"service\"/>\n"
" </method>\n"
" <property access=\"read\" type=\"as\" name=\"RegisteredStatusNotifierItems\">\n"
" <annotation value=\"QStringList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
" </property>\n"
" <property access=\"read\" type=\"b\" name=\"IsStatusNotifierHostRegistered\"/>\n"
" <property access=\"read\" type=\"i\" name=\"ProtocolVersion\"/>\n"
" <signal name=\"StatusNotifierItemRegistered\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"StatusNotifierItemUnregistered\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"StatusNotifierHostRegistered\"/>\n"
" <signal name=\"StatusNotifierHostUnregistered\"/>\n"
" </interface>\n"
"")
public:
StatusNotifierWatcherAdaptor(QObject *parent);
virtual ~StatusNotifierWatcherAdaptor();
public: // PROPERTIES
Q_PROPERTY(bool IsStatusNotifierHostRegistered READ isStatusNotifierHostRegistered)
bool isStatusNotifierHostRegistered() const;
Q_PROPERTY(int ProtocolVersion READ protocolVersion)
int protocolVersion() const;
Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ registeredStatusNotifierItems)
QStringList registeredStatusNotifierItems() const;
public Q_SLOTS: // METHODS
void RegisterStatusNotifierHost(const QString &service);
void RegisterStatusNotifierItem(const QString &service);
Q_SIGNALS: // SIGNALS
void StatusNotifierHostRegistered();
void StatusNotifierHostUnregistered();
void StatusNotifierItemRegistered(const QString &in0);
void StatusNotifierItemUnregistered(const QString &in0);
};
#endif

View File

@ -0,0 +1,131 @@
/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: listenerri <listenerri@gmail.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "statusnotifierwatcher.h"
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
#include <QDBusMessage>
#include <QDebug>
#include "dbus/sni/statusnotifierwatcheradapter.h"
#include "dbus/sni/statusnotifieritem_interface.h"
StatusNotifierWatcher::StatusNotifierWatcher(QObject *parent)
{
new StatusNotifierWatcherAdaptor(this);
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject(QStringLiteral("/StatusNotifierWatcher"), this);
dbus.registerService(QStringLiteral("org.kde.StatusNotifierWatcher"));
m_serviceWatcher = new QDBusServiceWatcher(this);
m_serviceWatcher->setConnection(dbus);
m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &StatusNotifierWatcher::serviceUnregistered);
}
StatusNotifierWatcher::~StatusNotifierWatcher()
{
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.unregisterService(QStringLiteral("org.kde.StatusNotifierWatcher"));
}
void StatusNotifierWatcher::RegisterStatusNotifierItem(const QString &serviceOrPath)
{
QString service;
QString path;
if (serviceOrPath.startsWith(QLatin1Char('/'))) {
service = message().service();
path = serviceOrPath;
} else {
service = serviceOrPath;
path = QStringLiteral("/StatusNotifierItem");
}
QString notifierItemId = service + path;
if (QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value() &&
!m_registeredServices.contains(notifierItemId)) {
qDebug()<<"Registering" << notifierItemId << "to system tray";
//check if the service has registered a SystemTray object
com::deepin::dde::StatusNotifierItem trayclient(service, path,
QDBusConnection::sessionBus());
if (trayclient.isValid()) {
m_registeredServices.append(notifierItemId);
m_serviceWatcher->addWatchedService(service);
emit StatusNotifierItemRegistered(notifierItemId);
}
}
}
QStringList StatusNotifierWatcher::RegisteredStatusNotifierItems() const
{
return m_registeredServices;
}
void StatusNotifierWatcher::serviceUnregistered(const QString& name)
{
qDebug()<<"Service "<< name << "unregistered";
m_serviceWatcher->removeWatchedService(name);
QString match = name + QLatin1Char('/');
QStringList::Iterator it = m_registeredServices.begin();
while (it != m_registeredServices.end()) {
if (it->startsWith(match)) {
QString name = *it;
it = m_registeredServices.erase(it);
emit StatusNotifierItemUnregistered(name);
} else {
++it;
}
}
if (m_statusNotifierHostServices.contains(name)) {
m_statusNotifierHostServices.remove(name);
emit StatusNotifierHostUnregistered();
}
}
void StatusNotifierWatcher::RegisterStatusNotifierHost(const QString &service)
{
if (service.contains(QStringLiteral("org.kde.StatusNotifierHost-")) &&
QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value() &&
!m_statusNotifierHostServices.contains(service)) {
qDebug()<<"Registering"<<service<<"as system tray";
m_statusNotifierHostServices.insert(service);
m_serviceWatcher->addWatchedService(service);
emit StatusNotifierHostRegistered();
}
}
bool StatusNotifierWatcher::IsStatusNotifierHostRegistered() const
{
return !m_statusNotifierHostServices.isEmpty();
}
int StatusNotifierWatcher::ProtocolVersion() const
{
return 0;
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: listenerri <listenerri@gmail.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STATUSNOTIFIERWATCHER_H
#define STATUSNOTIFIERWATCHER_H
#include <QDBusContext>
#include <QObject>
#include <QStringList>
#include <QSet>
class QDBusServiceWatcher;
class StatusNotifierWatcher : public QObject, protected QDBusContext
{
Q_OBJECT
Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ RegisteredStatusNotifierItems)
Q_PROPERTY(bool IsStatusNotifierHostRegistered READ IsStatusNotifierHostRegistered)
Q_PROPERTY(int ProtocolVersion READ ProtocolVersion)
public:
StatusNotifierWatcher(QObject *parent);
~StatusNotifierWatcher();
QStringList RegisteredStatusNotifierItems() const;
bool IsStatusNotifierHostRegistered() const;
int ProtocolVersion() const;
public Q_SLOTS:
void RegisterStatusNotifierItem(const QString &service);
void RegisterStatusNotifierHost(const QString &service);
protected Q_SLOTS:
void serviceUnregistered(const QString& name);
Q_SIGNALS:
void StatusNotifierItemRegistered(const QString &service);
//TODO: decide if this makes sense, the systray itself could notice the vanishing of items, but looks complete putting it here
void StatusNotifierItemUnregistered(const QString &service);
void StatusNotifierHostRegistered();
void StatusNotifierHostUnregistered();
private:
QDBusServiceWatcher *m_serviceWatcher = nullptr;
QStringList m_registeredServices;
QSet<QString> m_statusNotifierHostServices;
};
#endif

View File

@ -0,0 +1,274 @@
/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: listenerri <listenerri@gmail.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "snitraywidget.h"
#include "../frame/util/themeappicon.h"
#include <QPainter>
#include <QApplication>
#include <xcb/xproto.h>
#define IconSize 16
const QStringList ItemCategoryList {"ApplicationStatus" , "Communications" , "SystemServices", "Hardware"};
const QStringList ItemStatusList {"ApplicationStatus" , "Communications" , "SystemServices", "Hardware"};
SNITrayWidget::SNITrayWidget(const QString &sniServicePath, QWidget *parent)
: AbstractTrayWidget(parent),
m_dbusMenuImporter(nullptr),
m_menu(nullptr)
{
if (sniServicePath.startsWith("/") || !sniServicePath.contains("/")) {
return;
}
QPair<QString, QString> pair = serviceAndPath(sniServicePath);
const QString &service = pair.first;
const QString &path = pair.second;
m_sniInter = new StatusNotifierItem(service, path, QDBusConnection::sessionBus(), this);
if (!m_sniInter->isValid()) {
return;
}
const QString &menuPath = m_sniInter->menu().path();
m_dbusMenuImporter = new DBusMenuImporter(service, menuPath, SYNCHRONOUS, this);
m_menu = m_dbusMenuImporter->menu();
connect(m_sniInter, &StatusNotifierItem::NewIcon, this, &SNITrayWidget::refreshIcon);
connect(m_sniInter, &StatusNotifierItem::NewOverlayIcon, this, &SNITrayWidget::refreshOverlayIcon);
connect(m_sniInter, &StatusNotifierItem::NewAttentionIcon, this, &SNITrayWidget::refreshAttentionIcon);
QTimer::singleShot(0, this, &SNITrayWidget::refreshIcon);
}
SNITrayWidget::~SNITrayWidget()
{
}
void SNITrayWidget::setActive(const bool active)
{
}
void SNITrayWidget::updateIcon()
{
update();
}
void SNITrayWidget::sendClick(uint8_t mouseButton, int x, int y)
{
switch (mouseButton) {
case XCB_BUTTON_INDEX_1:
m_sniInter->Activate(x, y);
break;
case XCB_BUTTON_INDEX_2:
m_sniInter->SecondaryActivate(x, y);
break;
case XCB_BUTTON_INDEX_3:
// ContextMenu does not work
//m_sniInter->ContextMenu(x, y);
m_menu->popup(QPoint(x, y));
break;
default:
qDebug() << "unknown mouse button key";
break;
}
}
const QImage SNITrayWidget::trayImage()
{
return m_pixmap.toImage();
}
bool SNITrayWidget::isValid()
{
return m_sniInter->isValid();
}
/*
*ItemCategory SNITrayWidget::category()
*{
* const QString &category = m_sniInter->category();
* if (!ItemCategoryList.contains(category)) {
* return UnknownCategory;
* }
*
* return static_cast<ItemCategory>(ItemCategoryList.indexOf(category));
*}
*
*ItemStatus SNITrayWidget::status()
*{
* const QString &status = m_sniInter->status();
* if (!ItemStatusList.contains(status)) {
* return UnknownStatus;
* }
*
* return static_cast<ItemStatus>(ItemStatusList.indexOf(status));
*}
*
*/
void SNITrayWidget::refreshIcon()
{
QPixmap pix = newIconPixmap(Icon);
if (pix.isNull()) {
return;
}
m_pixmap = pix;
update();
Q_EMIT iconChanged();
}
void SNITrayWidget::refreshOverlayIcon()
{
QPixmap pix = newIconPixmap(OverlayIcon);
if (pix.isNull()) {
return;
}
m_overlayPixmap = pix;
update();
Q_EMIT iconChanged();
}
void SNITrayWidget::refreshAttentionIcon()
{
/* TODO: A new approach may be needed to deal with attentionIcon */
QPixmap pix = newIconPixmap(AttentionIcon);
if (pix.isNull()) {
return;
}
m_pixmap = pix;
update();
Q_EMIT iconChanged();
}
QSize SNITrayWidget::sizeHint() const
{
return QSize(26, 26);
}
void SNITrayWidget::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
if (m_pixmap.isNull())
return;
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
#ifdef QT_DEBUG
//painter.fillRect(rect(), Qt::red);
#endif
const QPoint p = rect().center() - m_pixmap.rect().center() / m_pixmap.devicePixelRatioF();
painter.drawPixmap(p, m_pixmap);
if (!m_overlayPixmap.isNull()) {
painter.drawPixmap(p, m_overlayPixmap);
}
painter.end();
}
QPair<QString, QString> SNITrayWidget::serviceAndPath(const QString &servicePath)
{
QStringList list = servicePath.split("/");
QPair<QString, QString> pair;
pair.first = list.takeFirst();
for (auto i : list) {
pair.second.append("/");
pair.second.append(i);
}
return pair;
}
QPixmap SNITrayWidget::newIconPixmap(IconType iconType)
{
QPixmap pixmap;
if (iconType == UnknownIconType) {
return pixmap;
}
QString iconName;
DBusImageList dbusImageList;
switch (iconType) {
case Icon:
iconName = m_sniInter->iconName();
dbusImageList = m_sniInter->iconPixmap();
break;
case OverlayIcon:
iconName = m_sniInter->overlayIconName();
dbusImageList = m_sniInter->overlayIconPixmap();
break;
case AttentionIcon:
iconName = m_sniInter->attentionIconName();
dbusImageList = m_sniInter->attentionIconPixmap();
break;
case AttentionMovieIcon:
iconName = m_sniInter->attentionMovieName();
break;
default:
break;
}
do {
if (!iconName.isEmpty()) {
pixmap = ThemeAppIcon::getIcon(iconName, IconSize);
if (!pixmap.isNull()) {
break;
}
}
const auto ratio = qApp->devicePixelRatio();
if (!dbusImageList.isEmpty()) {
for (DBusImage dbusImage : dbusImageList) {
char *image_data = dbusImage.pixels.data();
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
for (int i = 0; i < dbusImage.pixels.size(); i += 4) {
*(qint32*)(image_data + i) = qFromBigEndian(*(qint32*)(image_data + i));
}
}
QImage image((const uchar*)dbusImage.pixels.constData(), dbusImage.width, dbusImage.height, QImage::Format_ARGB32);
pixmap = QPixmap::fromImage(image.scaled(IconSize, IconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(ratio);
if (!pixmap.isNull()) {
break;
}
}
}
if (pixmap.isNull()) {
qDebug() << "get icon faild!";
}
} while (false);
return pixmap;
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: listenerri <listenerri@gmail.com>
*
* Maintainer: listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SNITRAYWIDGET_H
#define SNITRAYWIDGET_H
#include "abstracttraywidget.h"
#include "dbus/sni/statusnotifieritem_interface.h"
#include <dbusmenu-qt5/dbusmenuimporter.h>
#include <QMenu>
#include <QDBusObjectPath>
using namespace com::deepin::dde;
enum ItemCategory {UnknownCategory = -1, ApplicationStatus, Communications, SystemServices, Hardware};
enum ItemStatus {UnknownStatus = -1, Passive, Active, NeedsAttention};
enum IconType {UnknownIconType = -1, Icon, OverlayIcon, AttentionIcon, AttentionMovieIcon};
class SNITrayWidget : public AbstractTrayWidget
{
Q_OBJECT
public:
SNITrayWidget(const QString &sniServicePath, QWidget *parent = Q_NULLPTR);
virtual ~SNITrayWidget();
void setActive(const bool active) Q_DECL_OVERRIDE;
void updateIcon() Q_DECL_OVERRIDE;
void sendClick(uint8_t mouseButton, int x, int y) Q_DECL_OVERRIDE;
const QImage trayImage() Q_DECL_OVERRIDE;
bool isValid();
static QString toSNIKey(const QString &sniServicePath) { return QString("sni:%1").arg(sniServicePath); }
static bool isSNIKey(const QString &itemKey) { return itemKey.startsWith("sni:"); }
static QString toSNIServicePath(QString itemKey) { return itemKey.remove("sni:"); }
private Q_SLOTS:
void refreshIcon();
void refreshOverlayIcon();
void refreshAttentionIcon();
private:
QSize sizeHint() const Q_DECL_OVERRIDE;
void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE;
QPair<QString, QString> serviceAndPath(const QString &servicePath);
QPixmap newIconPixmap(IconType iconType);
private:
StatusNotifierItem *m_sniInter;
DBusMenuImporter *m_dbusMenuImporter;
QMenu *m_menu;
QPixmap m_pixmap;
QPixmap m_overlayPixmap;
};
#endif /* SNIWIDGET_H */

View File

@ -4,6 +4,7 @@
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
* listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +22,7 @@
#include "systemtrayplugin.h"
#include "fashiontrayitem.h"
#include "snitraywidget.h"
#include <QDir>
#include <QWindow>
@ -62,6 +64,16 @@ void SystemTrayPlugin::init(PluginProxyInterface *proxyInter)
m_proxyInter = proxyInter;
m_sniWatcher = new StatusNotifierWatcher(this);
QDBusConnection dbusConn = QDBusConnection::sessionBus();
const QString &host = QString("org.kde.StatusNotifierHost-") + QString::number(qApp->applicationPid());
dbusConn.registerService(host);
dbusConn.registerObject("/StatusNotifierHost", this);
m_sniWatcher->RegisterStatusNotifierHost(host);
connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemRegistered, this, &SystemTrayPlugin::sniItemsChanged);
connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered, this, &SystemTrayPlugin::sniItemsChanged);
connect(m_trayInter, &DBusTrayManager::TrayIconsChanged, this, &SystemTrayPlugin::trayListChanged);
connect(m_trayInter, &DBusTrayManager::Changed, this, &SystemTrayPlugin::trayChanged);
@ -71,6 +83,7 @@ void SystemTrayPlugin::init(PluginProxyInterface *proxyInter)
QTimer::singleShot(1, this, &SystemTrayPlugin::trayListChanged);
QTimer::singleShot(2, this, &SystemTrayPlugin::loadIndicator);
QTimer::singleShot(3, this, &SystemTrayPlugin::sniItemsChanged);
}
void SystemTrayPlugin::displayModeChanged(const Dock::DisplayMode mode)
@ -192,6 +205,24 @@ const QString SystemTrayPlugin::getWindowClass(quint32 winId)
return ret;
}
void SystemTrayPlugin::sniItemsChanged()
{
const QStringList &itemServicePaths = m_sniWatcher->RegisteredStatusNotifierItems();
QStringList sinTrayKeyList;
for (auto item : itemServicePaths) {
sinTrayKeyList << SNITrayWidget::toSNIKey(item);
}
for (auto itemKey : m_trayList.keys())
if (!sinTrayKeyList.contains(itemKey) && SNITrayWidget::isSNIKey(itemKey)) {
trayRemoved(itemKey);
}
for (auto tray : sinTrayKeyList) {
trayAdded(tray);
}
}
void SystemTrayPlugin::trayListChanged()
{
QList<quint32> winidList = m_trayInter->trayIcons();
@ -211,39 +242,45 @@ void SystemTrayPlugin::trayListChanged()
}
}
void SystemTrayPlugin::addTrayWidget(const QString &itemKey, AbstractTrayWidget * trayWidget)
{
if (!trayWidget) {
return;
}
if (!m_trayList.values().contains(trayWidget)) {
m_trayList.insert(itemKey, trayWidget);
}
m_fashionItem->setMouseEnable(m_trayList.size() == 1);
if (!m_fashionItem->activeTray()) {
m_fashionItem->setActiveTray(trayWidget);
}
if (displayMode() == Dock::Efficient) {
m_proxyInter->itemAdded(this, itemKey);
} else {
m_proxyInter->itemAdded(this, FASHION_MODE_ITEM);
}
}
void SystemTrayPlugin::trayAdded(const QString itemKey)
{
if (m_trayList.contains(itemKey)) {
return;
}
auto addTrayWidget = [ = ](AbstractTrayWidget * trayWidget) {
if (trayWidget) {
if (!m_trayList.values().contains(trayWidget)) {
m_trayList.insert(itemKey, trayWidget);
}
m_fashionItem->setMouseEnable(m_trayList.size() == 1);
if (!m_fashionItem->activeTray()) {
m_fashionItem->setActiveTray(trayWidget);
}
if (displayMode() == Dock::Efficient) {
m_proxyInter->itemAdded(this, itemKey);
} else {
m_proxyInter->itemAdded(this, FASHION_MODE_ITEM);
}
}
};
if (XWindowTrayWidget::isWinIdKey(itemKey)) {
auto winId = XWindowTrayWidget::toWinId(itemKey);
getWindowClass(winId);
AbstractTrayWidget *trayWidget = new XWindowTrayWidget(winId);
addTrayWidget(trayWidget);
}
if (IndicatorTrayWidget::isIndicatorKey(itemKey)) {
addTrayWidget(itemKey, trayWidget);
} else if (SNITrayWidget::isSNIKey(itemKey)) {
const QString &sniServicePath = SNITrayWidget::toSNIServicePath(itemKey);
AbstractTrayWidget *trayWidget = new SNITrayWidget(sniServicePath);
connect(trayWidget, &AbstractTrayWidget::iconChanged, this, &SystemTrayPlugin::sniItemIconChanged);
addTrayWidget(itemKey, trayWidget);
} else if (IndicatorTrayWidget::isIndicatorKey(itemKey)) {
IndicatorTray *trayWidget = nullptr;
QString indicatorKey = IndicatorTrayWidget::toIndicatorId(itemKey);
@ -257,7 +294,7 @@ void SystemTrayPlugin::trayAdded(const QString itemKey)
connect(trayWidget, &IndicatorTray::delayLoaded,
trayWidget, [ = ]() {
addTrayWidget(trayWidget->widget());
addTrayWidget(itemKey, trayWidget->widget());
});
connect(trayWidget, &IndicatorTray::removed, this, [=] {
@ -311,6 +348,20 @@ void SystemTrayPlugin::trayChanged(quint32 winId)
}
}
void SystemTrayPlugin::sniItemIconChanged()
{
AbstractTrayWidget *trayWidget = static_cast<AbstractTrayWidget *>(sender());
if (!m_trayList.values().contains(trayWidget)) {
return;
}
m_fashionItem->setActiveTray(trayWidget);
//if (m_trayApplet->isVisible()) {
// updateTipsContent();
//}
}
void SystemTrayPlugin::switchToMode(const Dock::DisplayMode mode)
{
if (mode == Dock::Fashion) {

View File

@ -4,6 +4,7 @@
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
* listenerri <listenerri@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +25,7 @@
#include "pluginsiteminterface.h"
#include "trayapplet.h"
#include "sni/statusnotifierwatcher.h"
#include "dbus/dbustraymanager.h"
#include "xwindowtraywidget.h"
@ -62,16 +64,20 @@ private:
void loadIndicator();
void updateTipsContent();
const QString getWindowClass(quint32 winId);
void addTrayWidget(const QString &itemKey, AbstractTrayWidget * trayWidget);
private slots:
void sniItemsChanged();
void trayListChanged();
void trayAdded(const QString itemKey);
void trayRemoved(const QString itemKey);
void trayChanged(quint32 winId);
void sniItemIconChanged();
void switchToMode(const Dock::DisplayMode mode);
private:
DBusTrayManager *m_trayInter;
StatusNotifierWatcher *m_sniWatcher;
FashionTrayItem *m_fashionItem;
QMap<QString, AbstractTrayWidget *> m_trayList;
QMap<QString, IndicatorTray*> m_indicatorList;