File indexing completed on 2023-05-30 09:17:23

0001 /*
0002  * SPDX-FileCopyrightText: 2019 Weixuan XIAO <veyx.shaw@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include <QApplication>
0008 #include <QDebug>
0009 #include <QFile>
0010 #include <QIcon>
0011 #include <QMessageBox>
0012 #include <QStandardPaths>
0013 #include <QThread>
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <dbushelper.h>
0018 
0019 #include "indicatorhelper.h"
0020 
0021 #include "serviceregister_mac.h"
0022 
0023 #include <kdeconnectconfig.h>
0024 
0025 IndicatorHelper::IndicatorHelper()
0026 {
0027     registerServices();
0028 
0029     QIcon kdeconnectIcon = QIcon::fromTheme(QStringLiteral("kdeconnect"));
0030     QPixmap splashPixmap(kdeconnectIcon.pixmap(256, 256));
0031 
0032     m_splashScreen = new QSplashScreen(splashPixmap);
0033 
0034     m_splashScreen->showMessage(i18n("Launching") + QStringLiteral("\n"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
0035     m_splashScreen->show();
0036 }
0037 
0038 IndicatorHelper::~IndicatorHelper()
0039 {
0040     if (m_splashScreen != nullptr) {
0041         delete m_splashScreen;
0042         m_splashScreen = nullptr;
0043     }
0044 }
0045 
0046 void IndicatorHelper::preInit()
0047 {
0048 }
0049 
0050 void IndicatorHelper::postInit()
0051 {
0052     m_splashScreen->finish(nullptr);
0053 }
0054 
0055 void IndicatorHelper::iconPathHook()
0056 {
0057     const QString iconPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdeconnect-icons"), QStandardPaths::LocateDirectory);
0058     if (!iconPath.isNull()) {
0059         QStringList themeSearchPaths = QIcon::themeSearchPaths();
0060         themeSearchPaths << iconPath;
0061         QIcon::setThemeSearchPaths(themeSearchPaths);
0062     }
0063 }
0064 
0065 int IndicatorHelper::daemonHook(QProcess &kdeconnectd)
0066 {
0067     // This flag marks whether a session DBus daemon is installed and run
0068     bool hasUsableSessionBus = true;
0069     // Use another bus instance for detecting, avoid session bus cache in Qt
0070     if (!QDBusConnection::connectToBus(QDBusConnection::SessionBus, QStringLiteral("kdeconnect-test-client")).isConnected()) {
0071         qDebug() << "Default session bus not detected, will use private D-Bus.";
0072 
0073         // Unset launchctl env and private dbus addr file, avoid block
0074         DBusHelper::macosUnsetLaunchctlEnv();
0075         QFile privateDBusAddressFile(KdeConnectConfig::instance().privateDBusAddressPath());
0076         if (privateDBusAddressFile.exists())
0077             privateDBusAddressFile.resize(0);
0078 
0079         // Update session bus usability state
0080         hasUsableSessionBus = false;
0081     }
0082 
0083     // Start daemon
0084     m_splashScreen->showMessage(i18n("Launching daemon") + QStringLiteral("\n"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
0085 
0086     // Here we will try to bring our private session D-Bus
0087     if (!hasUsableSessionBus) {
0088         qDebug() << "Launching private session D-Bus.";
0089         DBusHelper::macosUnsetLaunchctlEnv();
0090         DBusHelper::launchDBusDaemon();
0091         // Wait for dbus daemon env
0092         QProcess getLaunchdDBusEnv;
0093         m_splashScreen->showMessage(i18n("Waiting D-Bus") + QStringLiteral("\n"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
0094         int retry = 0;
0095         getLaunchdDBusEnv.setProgram(QStringLiteral("launchctl"));
0096         getLaunchdDBusEnv.setArguments({QStringLiteral("getenv"), QStringLiteral(KDECONNECT_SESSION_DBUS_LAUNCHD_ENV)});
0097         getLaunchdDBusEnv.start();
0098         getLaunchdDBusEnv.waitForFinished();
0099 
0100         QString launchdDBusEnv = QString::fromLocal8Bit(getLaunchdDBusEnv.readAllStandardOutput());
0101 
0102         if (!launchdDBusEnv.isEmpty() && QDBusConnection::sessionBus().isConnected()) {
0103             qDebug() << "Private D-Bus daemon launched and connected.";
0104             hasUsableSessionBus = true;
0105         } else if (!launchdDBusEnv.isEmpty()) {
0106             // Show a warning and exit
0107             qCritical() << "Invalid " << KDECONNECT_SESSION_DBUS_LAUNCHD_ENV << "env: \"" << launchdDBusEnv << "\"";
0108 
0109             QMessageBox::critical(nullptr,
0110                                   i18n("KDE Connect"),
0111                                   i18n("Cannot connect to DBus\n"
0112                                        "KDE Connect will quit"),
0113                                   QMessageBox::Abort,
0114                                   QMessageBox::Abort);
0115         } else {
0116             // Show a warning and exit
0117             qCritical() << "Fail to get launchctl" << KDECONNECT_SESSION_DBUS_LAUNCHD_ENV << "env";
0118 
0119             QMessageBox::critical(nullptr,
0120                                   i18n("KDE Connect"),
0121                                   i18n("Cannot connect to DBus\n"
0122                                        "KDE Connect will quit"),
0123                                   QMessageBox::Abort,
0124                                   QMessageBox::Abort);
0125             return -2;
0126         }
0127     }
0128 
0129     // Start kdeconnectd, the daemon will not duplicate when there is already one
0130     if (QFile::exists(QCoreApplication::applicationDirPath() + QStringLiteral("/kdeconnectd"))) {
0131         kdeconnectd.setProgram(QCoreApplication::applicationDirPath() + QStringLiteral("/kdeconnectd"));
0132     } else if (QFile::exists(QString::fromLatin1(qgetenv("craftRoot")) + QStringLiteral("/../lib/libexec/kdeconnectd"))) {
0133         kdeconnectd.setProgram(QString::fromLatin1(qgetenv("craftRoot")) + QStringLiteral("/../lib/libexec/kdeconnectd"));
0134     } else {
0135         QMessageBox::critical(nullptr, i18n("KDE Connect"), i18n("Cannot find kdeconnectd"), QMessageBox::Abort, QMessageBox::Abort);
0136         return -1;
0137     }
0138     kdeconnectd.startDetached();
0139 
0140     m_splashScreen->showMessage(i18n("Loading modules") + QStringLiteral("\n"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
0141 
0142     return 0;
0143 }
0144 
0145 #ifdef QSYSTRAY
0146 void IndicatorHelper::systrayIconHook(QSystemTrayIcon &systray)
0147 {
0148     Q_UNUSED(systray);
0149 }
0150 #else
0151 void IndicatorHelper::systrayIconHook(KStatusNotifierItem &systray)
0152 {
0153     const QString iconPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdeconnect-icons"), QStandardPaths::LocateDirectory);
0154     if (!iconPath.isNull()) {
0155         auto icon = QIcon::fromTheme(QStringLiteral("kdeconnectindicator"));
0156         icon.setIsMask(true); // Make icon adapt to menu bar color
0157         systray.setIconByPixmap(icon);
0158     } else {
0159         // We are in macOS dev env, just continue
0160         qWarning() << "Fail to find indicator icon, continue anyway";
0161     }
0162 }
0163 #endif