File indexing completed on 2024-05-12 05:21:08

0001 /* This file is part of the KDE project
0002 
0003    SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "pimuniqueapplication.h"
0009 #include "config-kontactinterface.h"
0010 #include "kontactinterface_debug.h"
0011 
0012 #include <KAboutData>
0013 #include <KWindowSystem>
0014 
0015 #include "config-kontactinterface.h"
0016 #if KONTACTINTERFACE_HAVE_X11
0017 #include <KStartupInfo>
0018 #include <private/qtx11extras_p.h>
0019 #endif
0020 
0021 #ifdef Q_OS_WINDOWS
0022 #include <QFont>
0023 #include <Windows.h>
0024 #endif
0025 
0026 #include <QCommandLineParser>
0027 #include <QDir>
0028 
0029 #include <QMainWindow>
0030 #include <QWidget>
0031 
0032 #include <QDBusConnectionInterface>
0033 #include <QDBusInterface>
0034 
0035 using namespace KontactInterface;
0036 
0037 namespace
0038 {
0039 const char kChromiumFlagsEnv[] = "QTWEBENGINE_CHROMIUM_FLAGS";
0040 const char kDisableInProcessStackTraces[] = "--disable-in-process-stack-traces";
0041 
0042 }
0043 
0044 //@cond PRIVATE
0045 class Q_DECL_HIDDEN KontactInterface::PimUniqueApplication::PimUniqueApplicationPrivate
0046 {
0047 public:
0048     PimUniqueApplicationPrivate()
0049         : cmdArgs(new QCommandLineParser())
0050     {
0051     }
0052 
0053     ~PimUniqueApplicationPrivate()
0054     {
0055         delete cmdArgs;
0056     }
0057 
0058     static void disableChromiumCrashHandler()
0059     {
0060         // Disable Chromium's own crash handler, which overrides DrKonqi.
0061         auto flags = qgetenv(kChromiumFlagsEnv);
0062         if (!flags.contains(kDisableInProcessStackTraces)) {
0063             qputenv(kChromiumFlagsEnv, QByteArray(flags + " " + kDisableInProcessStackTraces));
0064         }
0065     }
0066 
0067     QCommandLineParser *const cmdArgs;
0068 };
0069 //@endcond
0070 
0071 PimUniqueApplication::PimUniqueApplication(int &argc, char **argv[])
0072     : QApplication(argc, *argv)
0073     , d(new PimUniqueApplicationPrivate())
0074 {
0075 #ifdef Q_OS_WINDOWS
0076     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0077         freopen("CONOUT$", "w", stdout);
0078         freopen("CONOUT$", "w", stderr);
0079     }
0080 
0081     setStyle(QStringLiteral("breeze"));
0082     QFont font(QStringLiteral("Segoe UI Emoji"));
0083     font.setPointSize(10);
0084     font.setHintingPreference(QFont::PreferNoHinting);
0085     setFont(font);
0086 #endif
0087 }
0088 
0089 PimUniqueApplication::~PimUniqueApplication() = default;
0090 
0091 QCommandLineParser *PimUniqueApplication::cmdArgs() const
0092 {
0093     return d->cmdArgs;
0094 }
0095 
0096 void PimUniqueApplication::setAboutData(KAboutData &aboutData)
0097 {
0098     KAboutData::setApplicationData(aboutData);
0099     aboutData.setupCommandLine(d->cmdArgs);
0100     // This object name is used in start(), and also in kontact's UniqueAppHandler.
0101     const QString objectName = QLatin1Char('/') + QApplication::applicationName() + QLatin1StringView("_PimApplication");
0102     QDBusConnection::sessionBus().registerObject(objectName,
0103                                                  this,
0104                                                  QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties
0105                                                      | QDBusConnection::ExportAdaptors);
0106 }
0107 
0108 static bool callNewInstance(const QString &appName, const QString &serviceName, const QByteArray &asn_id, const QStringList &arguments)
0109 {
0110     const QString objectName = QLatin1Char('/') + appName + QLatin1StringView("_PimApplication");
0111     QDBusInterface iface(serviceName, objectName, QStringLiteral("org.kde.PIMUniqueApplication"), QDBusConnection::sessionBus());
0112     if (iface.isValid()) {
0113         QDBusReply<int> reply = iface.call(QStringLiteral("newInstance"), asn_id, arguments, QDir::currentPath());
0114         if (reply.isValid()) {
0115             return true;
0116         }
0117     }
0118     return false;
0119 }
0120 
0121 int PimUniqueApplication::newInstance()
0122 {
0123     return newInstance(QByteArray(), QStringList() << QApplication::applicationName(), QDir::currentPath());
0124 }
0125 
0126 bool PimUniqueApplication::start(const QStringList &arguments)
0127 {
0128     const QString appName = QApplication::applicationName();
0129 
0130     // Try talking to /appName_PimApplication in org.kde.appName,
0131     // (which could be kontact or the standalone application),
0132     // otherwise the current app being started will register to DBus.
0133 
0134     const QString serviceName = QLatin1StringView("org.kde.") + appName;
0135     if (QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName)) {
0136         QByteArray new_asn_id;
0137         if (KWindowSystem::isPlatformX11()) {
0138 #if KONTACTINTERFACE_HAVE_X11
0139             new_asn_id = QX11Info::nextStartupId();
0140 #endif
0141         } else if (KWindowSystem::isPlatformWayland()) {
0142             new_asn_id = qgetenv("XDG_ACTIVATION_TOKEN");
0143         }
0144 
0145         if (callNewInstance(appName, serviceName, new_asn_id, arguments)) {
0146             return false; // success means that main() can exit now.
0147         }
0148     }
0149 
0150     qCDebug(KONTACTINTERFACE_LOG) << "kontact not running -- start standalone application";
0151 
0152     QDBusConnection::sessionBus().registerService(serviceName);
0153 
0154     // Make sure we have DrKonqi
0155     PimUniqueApplicationPrivate::disableChromiumCrashHandler();
0156 
0157     static_cast<PimUniqueApplication *>(qApp)->activate(arguments, QDir::currentPath());
0158     return true;
0159 }
0160 
0161 // This is called via DBus either by another instance that has just been
0162 // started or by Kontact when the module is activated
0163 int PimUniqueApplication::newInstance(const QByteArray &startupId, const QStringList &arguments, const QString &workingDirectory)
0164 {
0165     if (KWindowSystem::isPlatformX11()) {
0166 #if KONTACTINTERFACE_HAVE_X11
0167         KStartupInfo::setStartupId(startupId);
0168 #endif
0169     } else if (KWindowSystem::isPlatformWayland()) {
0170         KWindowSystem::setCurrentXdgActivationToken(QString::fromUtf8(startupId));
0171     }
0172 
0173     const QWidgetList tlws = topLevelWidgets();
0174     for (QWidget *win : tlws) {
0175         if (qobject_cast<QMainWindow *>(win)) {
0176             win->show();
0177             win->setAttribute(Qt::WA_NativeWindow, true);
0178 
0179             KWindowSystem::activateWindow(win->windowHandle());
0180             break;
0181         }
0182     }
0183 
0184     activate(arguments, workingDirectory);
0185     return 0;
0186 }
0187 
0188 int PimUniqueApplication::activate(const QStringList &arguments, const QString &workingDirectory)
0189 {
0190     Q_UNUSED(arguments)
0191     Q_UNUSED(workingDirectory)
0192     return 0;
0193 }
0194 
0195 #include "moc_pimuniqueapplication.cpp"