From 60cfb83ae452e20c1d974eb431b2be551db3432e Mon Sep 17 00:00:00 2001
From: luoqing <luoqing@kylinsec.com.cn>
Date: Thu, 21 Sep 2023 10:21:23 +0800
Subject: [PATCH 1/2] fix(tray):Fixed the tray menu using the X11 platform,
 which caused the missing voice and network icon in the lower right corner,
 and the network icon in the taskbar was formatted incorrectly
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 修复托盘菜单使用X11平台，从而导致的右下角缺少声音和网络图标，以及任务栏网络图标格式不对的问题

Closes #13353,#11856,#14006
---
 CMakeLists.txt                         |   1 +
 include/dbus-tray-monitor.h            | 111 +++++++++++++++++++++++++
 plugins/audio/CMakeLists.txt           |   3 +-
 plugins/audio/src/system-tray/main.cpp |  28 +++----
 plugins/network/CMakeLists.txt         |   1 +
 plugins/network/src/tray/main.cpp      |  29 +++----
 6 files changed, 141 insertions(+), 32 deletions(-)
 create mode 100644 include/dbus-tray-monitor.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bdffa50..210c938 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,7 @@ target_link_libraries(${PROJECT_NAME}
         ${KIRAN_STYLE_LIBRARIES}
         ${GLIB_2_LIBRARIES})
 
+list(FILTER INCLUDE_SRC EXCLUDE REGEX ".*bus-tray-monitor.h$")
 install(FILES "${CMAKE_SOURCE_DIR}/data/kiran-control-panel.svg" DESTINATION ${INSTALL_DATADIR}/icons/hicolor/)
 install(FILES ${INCLUDE_SRC} DESTINATION ${KCP_INSTALL_INCLUDE}/)
 install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.desktop" DESTINATION "${INSTALL_DATADIR}/applications/")
diff --git a/include/dbus-tray-monitor.h b/include/dbus-tray-monitor.h
new file mode 100644
index 0000000..652e7b1
--- /dev/null
+++ b/include/dbus-tray-monitor.h
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2022 KylinSec Co., Ltd.
+ * kiran-control-panel is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ *
+ * Author:     luoqing <luoqing@kylinsec.com.cn>
+ */
+
+#pragma once
+#include <qt5-log-i.h>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusServiceWatcher>
+#include <QDBusArgument>
+#include <QMap>
+#include <QObject>
+
+#define KDE_STATUS_NOTIFIER_WATCHER_SERVICE        "org.kde.StatusNotifierWatcher"
+#define KDE_STATUS_NOTIFIER_WATCHER_PATH           "/StatusNotifierWatcher"
+#define FREEDESKTOP_DBUS_PROPERTIES_INTERFACE      "org.freedesktop.DBus.Properties"
+#define FREEDESKTOP_DBUS_PROPERTIES_CHANGED_METHOD "PropertiesChanged"
+
+#define KDE_STATUS_NOTIFIER_HOST "org.kde.StatusNotifierHost"
+
+namespace KiranControlPanel
+{
+class DBusTrayMonitor : public QObject
+{
+    Q_OBJECT
+public:
+    DBusTrayMonitor(QObject *parent = nullptr)
+        : QObject(parent),
+          m_connection(QDBusConnection::sessionBus()),
+          m_statusNotifierHostRegistered(false)
+    {
+
+        QDBusInterface trayWatcherInterface(KDE_STATUS_NOTIFIER_WATCHER_SERVICE, KDE_STATUS_NOTIFIER_WATCHER_PATH,
+                                            KDE_STATUS_NOTIFIER_WATCHER_SERVICE, QDBusConnection::sessionBus());
+
+        // 确认托盘服务Watcher是否存在，检测Watcher IsStatusNotifierHostRegistered是否为true(满足QDBusTray可用的条件，具体可参考Qt代码)
+        if (trayWatcherInterface.isValid() && trayWatcherInterface.property("IsStatusNotifierHostRegistered").toBool())
+        {
+            m_statusNotifierHostRegistered = true;
+        }
+        else
+        {
+            // 托盘服务Watcher不存在/Host未注册/IsStatusNotifierHostRegistered属性未更新
+            // 连接到DBus Daemon,处理Watcher属性变化信号
+            m_connection.connect(KDE_STATUS_NOTIFIER_WATCHER_SERVICE,
+                                 KDE_STATUS_NOTIFIER_WATCHER_PATH,
+                                 FREEDESKTOP_DBUS_PROPERTIES_INTERFACE,
+                                 FREEDESKTOP_DBUS_PROPERTIES_CHANGED_METHOD, 
+                                 this, SLOT(onWatcherServicePropertyChanged(QDBusMessage)));
+        }
+    }
+
+    ~DBusTrayMonitor() {}
+
+    bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; }
+
+private slots:
+    void onWatcherServicePropertyChanged(QDBusMessage msg)
+    {
+        // 若Host已注册过，无需判断
+        if (m_statusNotifierHostRegistered)
+            return;
+
+        QList<QVariant> args = msg.arguments();
+        QVariantMap changedProps = qdbus_cast<QVariantMap>(args.at(1).value<QDBusArgument>());
+        for (auto iter = changedProps.begin(); iter != changedProps.end(); iter++)
+        {
+            if (iter.key() != "IsStatusNotifierHostRegistered")
+                continue;
+
+            // IsStatusNotifierHostRegistered,属性变为true
+            // 发出DBus托盘服务可用信号(只在托盘初始化时发出)
+            // 后续属性变化不关注
+            if (iter.value().toBool())
+            {
+                m_statusNotifierHostRegistered = true;
+                KLOG_DEBUG() << "notifier host registered,dbus tray available!";
+                emit dbusTrayAvailable();
+            }
+            break;
+        }
+    }
+
+signals:
+    void dbusTrayAvailable();
+
+private:
+    QDBusConnection m_connection;
+    bool m_statusNotifierHostRegistered;
+};
+
+static bool isDBusTrayAvailable()
+{
+    bool dbusTrayAvailable = false;
+        DBusTrayMonitor conn;
+        if (conn.isStatusNotifierHostRegistered())
+            dbusTrayAvailable = true;
+    return dbusTrayAvailable;
+}
+
+}  // namespace KiranControlPanel
diff --git a/plugins/audio/CMakeLists.txt b/plugins/audio/CMakeLists.txt
index ea568f1..55b07b2 100644
--- a/plugins/audio/CMakeLists.txt
+++ b/plugins/audio/CMakeLists.txt
@@ -44,7 +44,8 @@ add_executable(${TRAY_PROCESS}
         ${DBUS_SRC}
         ${SYSTEM_TRAY_SRC}
         ${AUDIO_QM_FILES}
-        ${QRC})
+        ${QRC}
+        ${PROJECT_SOURCE_DIR}/include/dbus-tray-monitor.h)
 
 target_include_directories(${TRAY_PROCESS} PRIVATE
         ${CMAKE_BINARY_DIR}
diff --git a/plugins/audio/src/system-tray/main.cpp b/plugins/audio/src/system-tray/main.cpp
index 431e6c9..8c36383 100644
--- a/plugins/audio/src/system-tray/main.cpp
+++ b/plugins/audio/src/system-tray/main.cpp
@@ -25,6 +25,7 @@
 #include <QDateTime>
 #include <QFile>
 #include <QTranslator>
+#include "dbus-tray-monitor.h"
 
 #define DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER "org.kde.StatusNotifierWatcher"
 
@@ -47,27 +48,24 @@ int main(int argc, char *argv[])
     }
 
     AudioSystemTray *audioSystemTray = nullptr;
-
-    if (QDBusConnection::sessionBus().interface()->isServiceRegistered(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER))
+    if (KiranControlPanel::isDBusTrayAvailable())
     {
-        KLOG_DEBUG() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is registered,create audio tray icon";
+        KLOG_DEBUG() << KDE_STATUS_NOTIFIER_HOST << "is registered,create network tray icon";
         audioSystemTray = new AudioSystemTray;
     }
     else
     {
-        KLOG_WARNING() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is not registered,wait";
-        QDBusServiceWatcher* dbusServiceWatcher = new QDBusServiceWatcher;
-        dbusServiceWatcher->setConnection(QDBusConnection::sessionBus());
-        dbusServiceWatcher->addWatchedService(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER);
-        QObject::connect(dbusServiceWatcher, &QDBusServiceWatcher::serviceRegistered,
-                         [&dbusServiceWatcher, &audioSystemTray](const QString& service)
+        KLOG_WARNING() << KDE_STATUS_NOTIFIER_HOST << "is not registered,wait";
+
+        auto dBusTrayMonitor = new KiranControlPanel::DBusTrayMonitor();
+        QObject::connect(dBusTrayMonitor, &KiranControlPanel::DBusTrayMonitor::dbusTrayAvailable, [&audioSystemTray]()
                          {
-                             if (service != DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER)
-                                 return;
-                             KLOG_INFO() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is registered,create audio tray icon";
-                             audioSystemTray = new AudioSystemTray;
-                             dbusServiceWatcher->removeWatchedService(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER);
-                         });
+                            if(audioSystemTray == nullptr)
+                            {
+                                KLOG_DEBUG() << KDE_STATUS_NOTIFIER_HOST << "is registered,create network tray icon";
+                                audioSystemTray = new AudioSystemTray;
+                            } 
+                        });
     }
     return QApplication::exec();
 }
diff --git a/plugins/network/CMakeLists.txt b/plugins/network/CMakeLists.txt
index 7ea6479..94de67a 100644
--- a/plugins/network/CMakeLists.txt
+++ b/plugins/network/CMakeLists.txt
@@ -55,6 +55,7 @@ add_executable(${TRAY_PROCESS}
         ${COMMON_SRC}
         ${QRC}
         ${NETWORK_QM_FILES}
+        ${PROJECT_SOURCE_DIR}/include/dbus-tray-monitor.h
         )
 
 target_include_directories(${TRAY_PROCESS} PRIVATE
diff --git a/plugins/network/src/tray/main.cpp b/plugins/network/src/tray/main.cpp
index 2a62bf1..5c4bb65 100644
--- a/plugins/network/src/tray/main.cpp
+++ b/plugins/network/src/tray/main.cpp
@@ -20,10 +20,9 @@
 #include <QDBusServiceWatcher>
 #include <QTranslator>
 #include "config.h"
+#include "dbus-tray-monitor.h"
 #include "network-tray.h"
 
-#define DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER "org.kde.StatusNotifierWatcher"
-
 int main(int argc, char* argv[])
 {
     KiranApplication a(argc, argv);
@@ -43,26 +42,24 @@ int main(int argc, char* argv[])
     }
 
     NetworkTray* tray = nullptr;
-    if (QDBusConnection::sessionBus().interface()->isServiceRegistered(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER))
+    if (KiranControlPanel::isDBusTrayAvailable())
     {
-        KLOG_DEBUG() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is registered,create network tray icon";
+        KLOG_DEBUG() << KDE_STATUS_NOTIFIER_HOST << "is registered,create network tray icon";
         tray = new NetworkTray;
     }
     else
     {
-        KLOG_WARNING() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is not registered,wait";
-        QDBusServiceWatcher* dbusServiceWatcher = new QDBusServiceWatcher;
-        dbusServiceWatcher->setConnection(QDBusConnection::sessionBus());
-        dbusServiceWatcher->addWatchedService(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER);
-        QObject::connect(dbusServiceWatcher, &QDBusServiceWatcher::serviceRegistered,
-                         [&dbusServiceWatcher, &tray](const QString& service)
+        KLOG_WARNING() << KDE_STATUS_NOTIFIER_HOST << "is not registered,wait";
+
+        auto dBusTrayMonitor = new KiranControlPanel::DBusTrayMonitor();
+        QObject::connect(dBusTrayMonitor, &KiranControlPanel::DBusTrayMonitor::dbusTrayAvailable, [&tray]()
                          {
-                             if (service != DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER)
-                                 return;
-                             KLOG_INFO() << DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER << "is registered,create network tray icon";
-                             tray = new NetworkTray;
-                             dbusServiceWatcher->removeWatchedService(DBUS_SERVICE_KDE_STATUS_NOTIFIER_WATCHER);
-                         });
+                            if(tray == nullptr)
+                            {
+                                KLOG_DEBUG() << KDE_STATUS_NOTIFIER_HOST << "is registered,create network tray icon";
+                                tray = new NetworkTray;
+                            } 
+                        });
     }
 
     return QApplication::exec();
-- 
2.33.0

