File indexing completed on 2024-04-21 04:56:46
0001 /** 0002 * SPDX-FileCopyrightText: 2014 Yuri Samoilenko <kinnalru@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 <QCommandLineOption> 0009 #include <QCommandLineParser> 0010 #include <QDBusMessage> 0011 #include <QIcon> 0012 #include <QProcess> 0013 #include <QSessionManager> 0014 #include <QStandardPaths> 0015 #include <QTimer> 0016 0017 #ifdef Q_OS_WIN 0018 #include <Windows.h> 0019 #endif 0020 0021 #include <KAboutData> 0022 #include <KDBusService> 0023 #include <KIO/Global> 0024 #include <KIO/JobTracker> 0025 #include <KLocalizedString> 0026 #include <KNotification> 0027 #include <KWindowSystem> 0028 0029 #include <dbushelper.h> 0030 0031 #include "core/backends/pairinghandler.h" 0032 #include "core/daemon.h" 0033 #include "core/device.h" 0034 #include "core/openconfig.h" 0035 #include "kdeconnect-version.h" 0036 #include "kdeconnectd_debug.h" 0037 0038 class DesktopDaemon : public Daemon 0039 { 0040 Q_OBJECT 0041 Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.daemon") 0042 public: 0043 DesktopDaemon(QObject *parent = nullptr) 0044 : Daemon(parent) 0045 { 0046 qApp->setWindowIcon(QIcon(QStringLiteral(":/icons/kdeconnect/kdeconnect.png"))); 0047 } 0048 0049 void askPairingConfirmation(Device *device) override 0050 { 0051 KNotification *notification = new KNotification(QStringLiteral("pairingRequest"), KNotification::NotificationFlag::Persistent); 0052 QTimer::singleShot(PairingHandler::pairingTimeoutMsec, notification, &KNotification::close); 0053 notification->setIconName(QStringLiteral("dialog-information")); 0054 notification->setComponentName(QStringLiteral("kdeconnect")); 0055 notification->setTitle(QStringLiteral("KDE Connect")); 0056 notification->setText( 0057 i18n("Pairing request from %1\nKey: %2...", device->name().toHtmlEscaped(), QString::fromUtf8(device->verificationKey().left(8)))); 0058 QString deviceId = device->id(); 0059 auto openSettings = [deviceId, notification] { 0060 OpenConfig oc; 0061 oc.setXdgActivationToken(notification->xdgActivationToken()); 0062 oc.openConfiguration(deviceId); 0063 }; 0064 0065 #if QT_VERSION_MAJOR == 6 0066 KNotificationAction *openSettingsAction = notification->addDefaultAction(i18n("Open")); 0067 connect(openSettingsAction, &KNotificationAction::activated, openSettings); 0068 0069 KNotificationAction *acceptAction = notification->addAction(i18n("Accept")); 0070 connect(acceptAction, &KNotificationAction::activated, device, &Device::acceptPairing); 0071 0072 KNotificationAction *rejectAction = notification->addAction(i18n("Reject")); 0073 connect(rejectAction, &KNotificationAction::activated, device, &Device::cancelPairing); 0074 0075 KNotificationAction *viewKeyAction = notification->addAction(i18n("View key")); 0076 connect(viewKeyAction, &KNotificationAction::activated, openSettings); 0077 #else 0078 notification->setDefaultAction(i18n("Open")); 0079 notification->setActions(QStringList() << i18n("Accept") << i18n("Reject") << i18n("View key")); 0080 connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing); 0081 connect(notification, &KNotification::action2Activated, device, &Device::cancelPairing); 0082 connect(notification, &KNotification::action3Activated, openSettings); 0083 connect(notification, &KNotification::activated, openSettings); 0084 #endif 0085 0086 notification->sendEvent(); 0087 } 0088 0089 void reportError(const QString &title, const QString &description) override 0090 { 0091 qCWarning(KDECONNECT_DAEMON) << title << ":" << description; 0092 KNotification::event(KNotification::Error, title, description); 0093 } 0094 0095 KJobTrackerInterface *jobTracker() override 0096 { 0097 return KIO::getJobTracker(); 0098 } 0099 0100 Q_SCRIPTABLE void sendSimpleNotification(const QString &eventId, const QString &title, const QString &text, const QString &iconName) override 0101 { 0102 KNotification *notification = new KNotification(eventId); // KNotification::Persistent 0103 notification->setIconName(iconName); 0104 notification->setComponentName(QStringLiteral("kdeconnect")); 0105 notification->setTitle(title); 0106 notification->setText(text); 0107 notification->sendEvent(); 0108 } 0109 0110 void quit() override 0111 { 0112 QApplication::quit(); 0113 } 0114 }; 0115 0116 // Copied from plasma-workspace/libkworkspace/kworkspace.cpp 0117 static void detectPlatform(int argc, char **argv) 0118 { 0119 if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { 0120 return; 0121 } 0122 for (int i = 0; i < argc; i++) { 0123 if (qstrcmp(argv[i], "-platform") == 0 || qstrcmp(argv[i], "--platform") == 0 || QByteArray(argv[i]).startsWith("-platform=") 0124 || QByteArray(argv[i]).startsWith("--platform=")) { 0125 return; 0126 } 0127 } 0128 const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE"); 0129 if (sessionType.isEmpty()) { 0130 return; 0131 } 0132 if (qstrcmp(sessionType.constData(), "wayland") == 0) { 0133 qputenv("QT_QPA_PLATFORM", "wayland"); 0134 } else if (qstrcmp(sessionType.constData(), "x11") == 0) { 0135 qputenv("QT_QPA_PLATFORM", "xcb"); 0136 } 0137 } 0138 0139 int main(int argc, char *argv[]) 0140 { 0141 #ifdef Q_OS_WIN 0142 // If ran from a console, redirect the output there 0143 if (AttachConsole(ATTACH_PARENT_PROCESS)) { 0144 freopen("CONOUT$", "w", stdout); 0145 freopen("CONOUT$", "w", stderr); 0146 } 0147 #endif 0148 0149 detectPlatform(argc, argv); 0150 QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 0151 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 0152 #if QT_VERSION_MAJOR == 6 0153 QGuiApplication::setQuitLockEnabled(false); 0154 #endif 0155 0156 QApplication app(argc, argv); 0157 KAboutData aboutData(QStringLiteral("kdeconnect.daemon"), 0158 i18n("KDE Connect Daemon"), 0159 QStringLiteral(KDECONNECT_VERSION_STRING), 0160 i18n("KDE Connect Daemon"), 0161 KAboutLicense::GPL); 0162 KAboutData::setApplicationData(aboutData); 0163 app.setQuitOnLastWindowClosed(false); 0164 0165 QCommandLineParser parser; 0166 QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance")); 0167 parser.addOption(replaceOption); 0168 #ifdef Q_OS_MAC 0169 QCommandLineOption macosPrivateDBusOption({QStringLiteral("use-private-dbus")}, 0170 i18n("Launch a private D-Bus daemon with kdeconnectd (macOS test-purpose only)")); 0171 parser.addOption(macosPrivateDBusOption); 0172 #endif 0173 aboutData.setupCommandLine(&parser); 0174 0175 parser.process(app); 0176 #ifdef Q_OS_MAC 0177 if (parser.isSet(macosPrivateDBusOption)) { 0178 DBusHelper::launchDBusDaemon(); 0179 } 0180 #endif 0181 aboutData.processCommandLine(&parser); 0182 if (parser.isSet(replaceOption)) { 0183 auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), 0184 QStringLiteral("/MainApplication"), 0185 QStringLiteral("org.qtproject.Qt.QCoreApplication"), 0186 QStringLiteral("quit")); 0187 QDBusConnection::sessionBus().call(message); // deliberately block until it's done, so we register the name after the app quits 0188 } 0189 0190 KDBusService dbusService(KDBusService::Unique); 0191 0192 DesktopDaemon daemon; 0193 0194 #ifdef Q_OS_WIN 0195 // make sure indicator shows up in the tray whenever daemon is spawned 0196 QProcess::startDetached(QStringLiteral("kdeconnect-indicator.exe"), QStringList()); 0197 #endif 0198 0199 // kdeconnectd is autostarted, so disable session management to speed up startup 0200 auto disableSessionManagement = [](QSessionManager &sm) { 0201 sm.setRestartHint(QSessionManager::RestartNever); 0202 }; 0203 QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); 0204 QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); 0205 0206 qSetMessagePattern(QStringLiteral("%{time} %{category}: %{message}")); 0207 0208 return app.exec(); 0209 } 0210 0211 #include "kdeconnectd.moc"