File indexing completed on 2024-05-05 17:32:16

0001 // SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
0002 // SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
0003 //
0004 // SPDX-License-Identifier: LGPL-2.0-or-later
0005 
0006 #include <QGuiApplication>
0007 
0008 #include "config.h"
0009 #include "version.h"
0010 
0011 #include <KAboutData>
0012 #include <KDBusService>
0013 #include <KLocalizedContext>
0014 #include <KLocalizedString>
0015 #include <KWaylandExtras>
0016 #include <KWindowSystem>
0017 #include <QCommandLineParser>
0018 #include <QIcon>
0019 #include <QObject>
0020 #include <QQuickStyle>
0021 #include <QQuickWindow>
0022 #include <QtQml>
0023 
0024 #ifdef DIALER_BUILD_SHELL_OVERLAY
0025 #include "qwayland-kde-lockscreen-overlay-v1.h"
0026 #include <QDBusConnection>
0027 #include <QDBusMessage>
0028 #include <QDBusReply>
0029 #include <QWaylandClientExtensionTemplate>
0030 #include <qpa/qplatformnativeinterface.h>
0031 #endif // DIALER_BUILD_SHELL_OVERLAY
0032 
0033 static QString parseTelArgument(const QString &numberArg)
0034 {
0035     QString result;
0036     if (numberArg.startsWith(QStringLiteral("tel:"))) {
0037         result = numberArg.mid(4);
0038     }
0039     if (numberArg.startsWith(QStringLiteral("callto:"))) {
0040         result = numberArg.mid(7);
0041     }
0042     return result;
0043 }
0044 
0045 static void inputCallNumber(QWindow *window, const QString &number)
0046 {
0047     QMetaObject::invokeMethod(window, "call", Q_ARG(QVariant, number));
0048 }
0049 
0050 #ifdef DIALER_BUILD_SHELL_OVERLAY
0051 static void updateLockscreenMode(QWindow *window, bool mode)
0052 {
0053     QMetaObject::invokeMethod(window, "updateLockscreenMode", Q_ARG(QVariant, mode));
0054 }
0055 #endif // DIALER_BUILD_SHELL_OVERLAY
0056 
0057 struct ScreenSaverUtils {
0058     Q_GADGET
0059 public:
0060     Q_INVOKABLE static bool getActive()
0061     {
0062         bool active = false;
0063 #ifdef DIALER_BUILD_SHELL_OVERLAY
0064         QDBusMessage request = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
0065                                                               QStringLiteral("/ScreenSaver"),
0066                                                               QStringLiteral("org.freedesktop.ScreenSaver"),
0067                                                               QStringLiteral("GetActive"));
0068         const QDBusReply<bool> response = QDBusConnection::sessionBus().call(request);
0069         active = response.isValid() ? response.value() : false;
0070 #endif // DIALER_BUILD_SHELL_OVERLAY
0071         return active;
0072     }
0073 };
0074 Q_DECLARE_METATYPE(ScreenSaverUtils)
0075 
0076 #ifdef DIALER_BUILD_SHELL_OVERLAY
0077 class WaylandAboveLockscreen : public QWaylandClientExtensionTemplate<WaylandAboveLockscreen>, public QtWayland::kde_lockscreen_overlay_v1
0078 {
0079 public:
0080     WaylandAboveLockscreen()
0081         : QWaylandClientExtensionTemplate<WaylandAboveLockscreen>(1)
0082     {
0083         initialize();
0084     }
0085 
0086     void allowWindow(QWindow *window)
0087     {
0088         QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
0089         wl_surface *surface = reinterpret_cast<wl_surface *>(native->nativeResourceForWindow(QByteArrayLiteral("surface"), window));
0090 
0091         Q_ASSERT(surface);
0092         allow(surface);
0093     }
0094 };
0095 
0096 #endif // DIALER_BUILD_SHELL_OVERLAY
0097 
0098 // raiseWindow with lockscreen support if possible
0099 static void raiseWindow(QWindow *window)
0100 {
0101 #ifdef DIALER_BUILD_SHELL_OVERLAY
0102     bool screenLocked = ScreenSaverUtils::getActive();
0103     updateLockscreenMode(window, screenLocked);
0104     if (screenLocked) {
0105         window->setVisibility(QWindow::Visibility::FullScreen);
0106         KWaylandExtras::requestXdgActivationToken(window, 0, QStringLiteral("org.kde.phone.dialer.desktop"));
0107         QObject::connect(KWaylandExtras::self(), &KWaylandExtras::xdgActivationTokenArrived, window, [window](int, const QString &token) {
0108             KWindowSystem::setCurrentXdgActivationToken(token);
0109             KWindowSystem::activateWindow(window);
0110         });
0111     }
0112 #endif // DIALER_BUILD_SHELL_OVERLAY
0113 }
0114 
0115 int main(int argc, char **argv)
0116 {
0117     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0118     QGuiApplication app(argc, argv);
0119 
0120     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE") && QQuickStyle::name().isEmpty()) {
0121         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0122     }
0123 
0124     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0125     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0126     KLocalizedString::setApplicationDomain("plasma-dialer");
0127     QCoreApplication::setApplicationName(QStringLiteral("dialer"));
0128     QCoreApplication::setApplicationVersion(QStringLiteral(PLASMADIALER_VERSION_STRING));
0129     QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("dialer")));
0130     QGuiApplication::setApplicationDisplayName(QStringLiteral("Phone"));
0131 
0132     KAboutData aboutData(QStringLiteral("dialer"), 
0133                          i18n("Phone"), 
0134                          QStringLiteral(PLASMADIALER_VERSION_STRING), 
0135                          i18n("Plasma phone dialer"), 
0136                          KAboutLicense::GPL,
0137                          i18n("© 2015-2022 KDE Community"));
0138     aboutData.setBugAddress("https://bugs.kde.org/describecomponents.cgi?product=Plasma%20Mobile%20Dialer");
0139     aboutData.setDesktopFileName(QStringLiteral("org.kde.phone.dialer"));
0140     aboutData.addAuthor(i18n("Alexey Andreyev"), QString(), QStringLiteral("aa13q@ya.ru"));
0141     KAboutData::setApplicationData(aboutData);
0142 
0143     QCommandLineParser parser;
0144     QCommandLineOption telSchemeOption(QStringList() << QStringLiteral("number"),
0145                                        QCoreApplication::translate("main", "Input a phone number"),
0146                                        QCoreApplication::translate("main", "phone number"));
0147     parser.addOption(telSchemeOption);
0148     parser.process(app);
0149 
0150     QString telNumber = parser.value(telSchemeOption);
0151 
0152     QQmlApplicationEngine engine;
0153 
0154     auto config = Config::self();
0155 
0156     qmlRegisterSingletonInstance("org.kde.phone.dialer", 1, 0, "Config", config);
0157 
0158     engine.rootContext()->setContextProperty(QStringLiteral("DialerAboutData"), QVariant::fromValue(aboutData));
0159 
0160     KDBusService service(KDBusService::Unique, &app);
0161 
0162     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0163 
0164     ScreenSaverUtils screenSaverUtils;
0165     engine.rootContext()->setContextProperty(QStringLiteral("ScreenSaverUtils"), QVariant::fromValue<ScreenSaverUtils>(screenSaverUtils));
0166 
0167     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
0168 
0169     if (engine.rootObjects().isEmpty()) {
0170         return -1;
0171     }
0172 
0173     QWindow *window = qobject_cast<QWindow *>(engine.rootObjects().at(0));
0174     Q_ASSERT(window);
0175 
0176 #ifdef DIALER_BUILD_SHELL_OVERLAY
0177     Q_ASSERT(!window->isVisible());
0178     WaylandAboveLockscreen aboveLockscreen;
0179     Q_ASSERT(aboveLockscreen.isInitialized());
0180     aboveLockscreen.allowWindow(window);
0181 #endif // DIALER_BUILD_SHELL_OVERLAY
0182 
0183     raiseWindow(window);
0184     QObject::connect(&service, &KDBusService::activateRequested, window, [&window](const QStringList &arguments) {
0185         for (const auto &arg : arguments) {
0186             QString numberArg = parseTelArgument(arg);
0187             if (!numberArg.isEmpty()) {
0188                 inputCallNumber(window, numberArg);
0189                 break;
0190             }
0191         }
0192         raiseWindow(window);
0193     });
0194 
0195     if (!parser.positionalArguments().isEmpty()) {
0196         QString numberArg = parseTelArgument(parser.positionalArguments().constFirst());
0197         if (!numberArg.isEmpty()) {
0198             telNumber = numberArg;
0199         }
0200     }
0201 
0202     if (!telNumber.isEmpty()) {
0203         inputCallNumber(window, telNumber);
0204     }
0205 
0206     return app.exec();
0207 }
0208 
0209 #include "main.moc"