File indexing completed on 2024-03-24 04:59:45

0001 /*
0002  * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@kde.org>
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 <QPointer>
0009 #include <QProcess>
0010 #include <QThread>
0011 
0012 #ifdef Q_OS_WIN
0013 #include <Windows.h>
0014 #endif
0015 
0016 #ifdef Q_OS_WIN
0017 #include <QSystemTrayIcon>
0018 #else
0019 #include <KStatusNotifierItem>
0020 #endif
0021 
0022 #include <KAboutData>
0023 #include <KCMultiDialog>
0024 #include <KColorSchemeManager>
0025 #include <KDBusService>
0026 #include <KLocalizedString>
0027 
0028 #include "deviceindicator.h"
0029 #include "interfaces/dbusinterfaces.h"
0030 #include "interfaces/devicesmodel.h"
0031 #include "kdeconnect-version.h"
0032 
0033 #include <dbushelper.h>
0034 
0035 #include "indicatorhelper.h"
0036 
0037 int main(int argc, char **argv)
0038 {
0039 #ifdef Q_OS_WIN
0040     // If ran from a console, redirect the output there
0041     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0042         freopen("CONOUT$", "w", stdout);
0043         freopen("CONOUT$", "w", stderr);
0044     }
0045 #endif
0046 
0047     QIcon::setFallbackThemeName(QStringLiteral("breeze"));
0048     QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0049     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0050 
0051     QApplication app(argc, argv);
0052     KAboutData about(QStringLiteral("kdeconnect-indicator"),
0053                      i18n("KDE Connect Indicator"),
0054                      QStringLiteral(KDECONNECT_VERSION_STRING),
0055                      i18n("KDE Connect Indicator tool"),
0056                      KAboutLicense::GPL,
0057                      i18n("(C) 2016 Aleix Pol Gonzalez"));
0058     KAboutData::setApplicationData(about);
0059 
0060 #ifdef Q_OS_WIN
0061     KColorSchemeManager manager;
0062     QApplication::setStyle(QStringLiteral("breeze"));
0063     IndicatorHelper helper(QUrl::fromLocalFile(qApp->applicationDirPath()));
0064 #else
0065     IndicatorHelper helper;
0066 #endif
0067 
0068     helper.preInit();
0069 
0070     // Run Daemon initialization step
0071     // When run from macOS app bundle, D-Bus call should be later than kdeconnectd and D-Bus daemon
0072     QProcess kdeconnectd;
0073     if (helper.daemonHook(kdeconnectd)) {
0074         return -1;
0075     }
0076 
0077     KDBusService dbusService(KDBusService::Unique);
0078 
0079     // Trigger loading the KIconLoader plugin
0080     about.setProgramLogo(QIcon(QStringLiteral(":/icons/kdeconnect/kdeconnect.svg")));
0081 
0082     DevicesModel model;
0083     model.setDisplayFilter(DevicesModel::Reachable | DevicesModel::Paired);
0084     QMenu *menu = new QMenu;
0085 
0086     QPointer<KCMultiDialog> dialog;
0087 
0088     DaemonDbusInterface iface;
0089 
0090     auto refreshMenu = [&iface, &model, &menu, &dialog]() {
0091         menu->clear();
0092         auto configure = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure..."));
0093         QObject::connect(configure, &QAction::triggered, configure, [&dialog]() {
0094             if (dialog == nullptr) {
0095                 dialog = new KCMultiDialog;
0096                 dialog->addModule(KPluginMetaData(QStringLiteral("plasma/kcms/systemsettings_qwidgets/kcm_kdeconnect")));
0097                 dialog->setAttribute(Qt::WA_DeleteOnClose);
0098                 dialog->show();
0099                 dialog->raise();
0100             } else {
0101                 dialog->raise();
0102                 dialog->activateWindow();
0103             }
0104         });
0105         for (int i = 0, count = model.rowCount(); i < count; ++i) {
0106             DeviceDbusInterface *device = model.getDevice(i);
0107             auto indicator = new DeviceIndicator(device);
0108             QObject::connect(device, &DeviceDbusInterface::destroyed, indicator, &QObject::deleteLater);
0109 
0110             menu->addMenu(indicator);
0111         }
0112         const QStringList requests = iface.pairingRequests();
0113         if (!requests.isEmpty()) {
0114             menu->addSection(i18n("Pairing requests"));
0115 
0116             for (const auto &req : requests) {
0117                 DeviceDbusInterface *dev = new DeviceDbusInterface(req, menu);
0118                 auto pairMenu = menu->addMenu(dev->name());
0119                 pairMenu->addAction(i18nc("Accept a pairing request", "Pair"), dev, &DeviceDbusInterface::acceptPairing);
0120                 pairMenu->addAction(i18n("Reject"), dev, &DeviceDbusInterface::cancelPairing);
0121             }
0122         }
0123         // Add quit menu
0124 #if defined Q_OS_MAC
0125 
0126         menu->addAction(i18n("Quit"), []() {
0127             auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect.daemon"),
0128                                                           QStringLiteral("/MainApplication"),
0129                                                           QStringLiteral("org.qtproject.Qt.QCoreApplication"),
0130                                                           QStringLiteral("quit"));
0131             QDBusConnection::sessionBus().call(message, QDBus::NoBlock);
0132             qApp->quit();
0133         });
0134 #elif defined Q_OS_WIN
0135 
0136         menu->addAction(QIcon::fromTheme(QStringLiteral("application-exit")), i18n("Quit"), []() {
0137             qApp->quit();
0138         });
0139 #endif
0140     };
0141 
0142     QObject::connect(&iface, &DaemonDbusInterface::pairingRequestsChanged, &model, refreshMenu);
0143     QObject::connect(&model, &DevicesModel::rowsInserted, &model, refreshMenu);
0144     QObject::connect(&model, &DevicesModel::rowsRemoved, &model, refreshMenu);
0145 
0146     // Run icon to add icon path (if necessary)
0147     helper.iconPathHook();
0148 
0149 #ifdef Q_OS_WIN
0150     QSystemTrayIcon systray;
0151     helper.systrayIconHook(systray);
0152     systray.setVisible(true);
0153     systray.setToolTip(QStringLiteral("KDE Connect"));
0154     QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() {
0155         systray.setToolTip(i18np("%1 device connected", "%1 devices connected", model.rowCount()));
0156     });
0157     QObject::connect(&systray, &QSystemTrayIcon::activated, [](QSystemTrayIcon::ActivationReason reason) {
0158         if (reason == QSystemTrayIcon::Trigger) {
0159             const QString kdeconnectAppExecutable = QStandardPaths::findExecutable(QStringLiteral("kdeconnect-app"), {QCoreApplication::applicationDirPath()});
0160             if (!kdeconnectAppExecutable.isEmpty()) {
0161                 QProcess::startDetached(kdeconnectAppExecutable, {});
0162             }
0163         }
0164     });
0165 
0166     systray.setContextMenu(menu);
0167 #else
0168     KStatusNotifierItem systray;
0169     helper.systrayIconHook(systray);
0170     systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), QStringLiteral("KDE Connect"));
0171     systray.setCategory(KStatusNotifierItem::Communications);
0172     systray.setStatus(KStatusNotifierItem::Passive);
0173     systray.setStandardActionsEnabled(false);
0174     QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() {
0175         const auto count = model.rowCount();
0176 #ifndef Q_OS_MACOS // On MacOS, setting status to Active disables color theme syncing of the menu icon
0177         systray.setStatus(count == 0 ? KStatusNotifierItem::Passive : KStatusNotifierItem::Active);
0178 #endif
0179         systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), i18np("%1 device connected", "%1 devices connected", count));
0180     });
0181 
0182     systray.setContextMenu(menu);
0183 #endif
0184 
0185     refreshMenu();
0186 
0187     app.setQuitOnLastWindowClosed(false);
0188 
0189     // Finish init
0190     helper.postInit();
0191 
0192     return app.exec();
0193 }