diff --git a/CMakeLists.txt b/CMakeLists.txt index f38abd0..de70376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,13 @@ set(SOURCE_FILES src/settings/user-config.cpp src/settings/user-config.h ) + +if(COMMAND qt_add_dbus_adaptor) + qt_add_dbus_adaptor(SOURCE_FILES data/org.ukui.menu.xml menu-dbus-service.h UkuiMenu::MenuDbusService) +else() + qt5_add_dbus_adaptor(SOURCE_FILES data/org.ukui.menu.xml menu-dbus-service.h UkuiMenu::MenuDbusService) +endif() + # library sources set(LIBRARY_SOURCES src/data-entity.cpp diff --git a/data/org.ukui.menu.xml b/data/org.ukui.menu.xml new file mode 100644 index 0000000..5ab53f7 --- /dev/null +++ b/data/org.ukui.menu.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/main.cpp b/src/main.cpp index 59453ae..35f291d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -149,6 +149,7 @@ int main(int argc, char *argv[]) QString appid = QString("ukui-menu-%1").arg(display); qDebug() << "ukui-menu launch with:" << display << "appid:" << appid; QtSingleApplication app(appid, argc, argv); + QGuiApplication::instance()->setProperty("display", display); QTranslator translator; QString translationFile{(QString(UKUI_MENU_TRANSLATION_DIR) + "/ukui-menu_" + QLocale::system().name() + ".qm")}; diff --git a/src/menu-dbus-service.cpp b/src/menu-dbus-service.cpp index 8202d84..832e9d0 100644 --- a/src/menu-dbus-service.cpp +++ b/src/menu-dbus-service.cpp @@ -16,25 +16,69 @@ * */ -#include #include "menu-dbus-service.h" +#include "menuadaptor.h" + +#include + +#include +#include +#include +#include +#include #define MENU_CORE_DBUS_SERVICE "org.ukui.menu" #define MENU_CORE_DBUS_PATH "/org/ukui/menu" +#define MENU_CORE_DBUS_INTERFACE "org.ukui.menu" using namespace UkuiMenu; -MenuDbusService::MenuDbusService(QObject *parent) : QObject(parent) +QString MenuDbusService::displayFromPid(uint pid) { - QDBusConnection::sessionBus().unregisterService(MENU_CORE_DBUS_SERVICE); - QDBusConnection::sessionBus().registerService(MENU_CORE_DBUS_SERVICE); - //注册对象路径,导出所有此对象的插槽 ,registerObject参数:路径,interface,对象,options - QDBusConnection::sessionBus().registerObject(MENU_CORE_DBUS_PATH, this, QDBusConnection::ExportAllSlots); + QFile environFile(QStringLiteral("/proc/%1/environ").arg(QString::number(pid))); + if (environFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + const QByteArray DISPLAY = KWindowSystem::isPlatformWayland() ? QByteArrayLiteral("WAYLAND_DISPLAY") + : QByteArrayLiteral("DISPLAY"); + const auto lines = environFile.readAll().split('\0'); + for (const QByteArray &line : lines) { + const int equalsIdx = line.indexOf('='); + if (equalsIdx <= 0) { + continue; + } + const QByteArray key = line.left(equalsIdx); + if (key == DISPLAY) { + const QByteArray value = line.mid(equalsIdx + 1); + return value; + } + } + } + + return {}; +} + +MenuDbusService::MenuDbusService(const QString &display, QObject *parent) : QObject(parent), m_display(display) +{ + bool isServiceRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(MENU_CORE_DBUS_SERVICE); + qDebug() << "menu service is registered:" << isServiceRegistered << ", display:" << display; + + if (isServiceRegistered) { + initWatcher(); + + } else { + bool success = registerService(); + if (!success) { + initWatcher(); + } + qDebug() << "menu service register:" << success; + } } void MenuDbusService::WinKeyResponse() { - Q_EMIT menuActive(); + uint callerPid = QDBusConnection::sessionBus().interface()->servicePid(message().service()).value(); + QString display = MenuDbusService::displayFromPid(callerPid); + + active(display); } QString MenuDbusService::GetSecurityConfigPath() @@ -47,3 +91,90 @@ void MenuDbusService::ReloadSecurityConfig() { Q_EMIT reloadConfigSignal(); } + +void MenuDbusService::active(const QString &display) +{ + if (display.isEmpty() || (display == m_display)) { + Q_EMIT menuActive(); + return; + } + + if (m_menuAdaptor) { + Q_EMIT m_menuAdaptor->activeRequest(display); + } +} + +void MenuDbusService::activeMenu(QString display) +{ + if (display == m_display) { + Q_EMIT menuActive(); + } +} + +bool MenuDbusService::registerService() +{ + m_menuAdaptor = new MenuAdaptor(this); + QDBusReply reply = + QDBusConnection::sessionBus().interface()->registerService(MENU_CORE_DBUS_SERVICE, + QDBusConnectionInterface::ReplaceExistingService, + QDBusConnectionInterface::DontAllowReplacement); + + if (reply.value() == QDBusConnectionInterface::ServiceNotRegistered) { + return false; + } + + bool res = QDBusConnection::sessionBus().registerObject(MENU_CORE_DBUS_PATH, this); + if (!res) { + QDBusConnection::sessionBus().interface()->unregisterService(MENU_CORE_DBUS_SERVICE); + } + + return res; +} + +void MenuDbusService::onServiceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner) +{ + qDebug() << "serviceOwnerChanged:" << service << oldOwner << newOwner; + if (newOwner.isEmpty()) { + bool success = registerService(); + if (success) { + disConnectActiveRequest(); + delete m_watcher; + m_watcher = nullptr; + } + qDebug() << "try to register service:" << success; + return; + } + + uint newOwnerPid = QDBusConnection::sessionBus().interface()->servicePid(newOwner); + qDebug() << "newOwnerPid:" << newOwnerPid << ", myPid:" << QCoreApplication::applicationPid() << ", display:" << m_display; +// if (newOwnerPid == QCoreApplication::applicationPid()) { +// qDebug() << "Becoming a new service"; +// } +} + +void MenuDbusService::connectToActiveRequest() +{ + QDBusConnection::sessionBus().connect(MENU_CORE_DBUS_SERVICE, + MENU_CORE_DBUS_PATH, + MENU_CORE_DBUS_INTERFACE, + "activeRequest", + this, + SLOT(activeMenu(QString))); +} + +void MenuDbusService::disConnectActiveRequest() +{ + QDBusConnection::sessionBus().disconnect(MENU_CORE_DBUS_SERVICE, + MENU_CORE_DBUS_PATH, + MENU_CORE_DBUS_INTERFACE, + "activeRequest", + this, + SLOT(activeMenu(QString))); +} + +void MenuDbusService::initWatcher() +{ + m_watcher = new QDBusServiceWatcher(MENU_CORE_DBUS_SERVICE,QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange); + connect(m_watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &MenuDbusService::onServiceOwnerChanged); + connectToActiveRequest(); +} diff --git a/src/menu-dbus-service.h b/src/menu-dbus-service.h index d2819f5..e533cdd 100644 --- a/src/menu-dbus-service.h +++ b/src/menu-dbus-service.h @@ -20,27 +20,45 @@ #define MENU_DBUS_SERVICE_H #include -#include -#include +#include + +class MenuAdaptor; +class QDBusServiceWatcher; namespace UkuiMenu { -class MenuDbusService : public QObject +class MenuDbusService : public QObject, public QDBusContext { Q_OBJECT - //申明该类有D-BUS服务接口 - Q_CLASSINFO("D-Bus Interface", "org.ukui.menu") public: - explicit MenuDbusService(QObject *parent = nullptr); //传入父指针,调用 + explicit MenuDbusService(const QString& display, QObject *parent = nullptr); public Q_SLOTS: void WinKeyResponse(); QString GetSecurityConfigPath(); void ReloadSecurityConfig(); + void active(const QString &display); Q_SIGNALS: void menuActive(); void reloadConfigSignal(); + +private Q_SLOTS: + void activeMenu(QString display); + void onServiceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner); + +private: + static QString displayFromPid(uint pid); + bool registerService(); + + void initWatcher(); + void connectToActiveRequest(); + void disConnectActiveRequest(); + +private: + QString m_display {}; + MenuAdaptor *m_menuAdaptor {nullptr}; + QDBusServiceWatcher *m_watcher {nullptr}; }; } diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp index 8b6bc3a..0267bf8 100644 --- a/src/ukui-menu-application.cpp +++ b/src/ukui-menu-application.cpp @@ -32,7 +32,7 @@ #include "menu-manager.h" #include "data-provider-manager.h" -#include +#include #include #include #include @@ -127,7 +127,7 @@ void UkuiMenuApplication::loadMenuUI() void UkuiMenuApplication::initDbusService() { - m_menuDbusService = new MenuDbusService(this); + m_menuDbusService = new MenuDbusService(QGuiApplication::instance()->property("display").toString(), this); if (m_menuDbusService) { connect(m_menuDbusService, &MenuDbusService::menuActive, this, [this] { execCommand(Active);