KontactInterface

pimuniqueapplication.cpp
1/* This file is part of the KDE project
2
3 SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "pimuniqueapplication.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include "config-kontactinterface.h"
12#include "kontactinterface_debug.h"
13
14#include <KAboutData>
15#include <KWindowSystem>
16
17#include "config-kontactinterface.h"
18#if KONTACTINTERFACE_HAVE_X11
19#include <KStartupInfo>
20#include <private/qtx11extras_p.h>
21#endif
22
23#if __has_include(<KWaylandExtras>)
24#include <KWaylandExtras>
25#define HAVE_WAYLAND
26#endif
27
28#ifdef Q_OS_WINDOWS
29#include <QFont>
30#include <Windows.h>
31#endif
32
33#include <QCommandLineParser>
34#include <QDir>
35
36#include <QMainWindow>
37#include <QWidget>
38
39#include <QDBusConnectionInterface>
40#include <QDBusInterface>
41
42using namespace KontactInterface;
43
44namespace
45{
46const char kChromiumFlagsEnv[] = "QTWEBENGINE_CHROMIUM_FLAGS";
47const char kDisableInProcessStackTraces[] = "--disable-in-process-stack-traces";
48
49}
50
51//@cond PRIVATE
52class Q_DECL_HIDDEN KontactInterface::PimUniqueApplication::PimUniqueApplicationPrivate
53{
54public:
55 PimUniqueApplicationPrivate()
56 : cmdArgs(new QCommandLineParser())
57 {
58 }
59
60 ~PimUniqueApplicationPrivate()
61 {
62 delete cmdArgs;
63 }
64
65 static void disableChromiumCrashHandler()
66 {
67 // Disable Chromium's own crash handler, which overrides DrKonqi.
68 auto flags = qgetenv(kChromiumFlagsEnv);
69 if (!flags.contains(kDisableInProcessStackTraces)) {
70 qputenv(kChromiumFlagsEnv, QByteArray(flags + " " + kDisableInProcessStackTraces));
71 }
72 }
73
74 void exportFocusWindow()
75 {
76#ifdef HAVE_WAYLAND
77 KWaylandExtras::self()->exportWindow(QGuiApplication::focusWindow());
78#endif
79 }
80
81 QCommandLineParser *const cmdArgs;
82};
83//@endcond
84
85PimUniqueApplication::PimUniqueApplication(int &argc, char **argv[])
86 : QApplication(argc, *argv)
87 , d(new PimUniqueApplicationPrivate())
88{
89#ifdef Q_OS_WINDOWS
90 if (AttachConsole(ATTACH_PARENT_PROCESS)) {
91 freopen("CONOUT$", "w", stdout);
92 freopen("CONOUT$", "w", stderr);
93 }
94
95 setStyle(QStringLiteral("breeze"));
96 QFont font(QStringLiteral("Segoe UI Emoji"));
97 font.setPointSize(10);
98 font.setHintingPreference(QFont::PreferNoHinting);
99 setFont(font);
100#endif
101
102#ifdef HAVE_WAYLAND
103 connect(KWaylandExtras::self(), &KWaylandExtras::windowExported, this, [](const auto, const auto &token) {
104 qputenv("PINENTRY_GEOM_HINT", QUrl::toPercentEncoding(token));
105 });
106 connect(qApp, &QGuiApplication::focusWindowChanged, this, [this](auto w) {
107 if (!w) {
108 return;
109 }
110 d->exportFocusWindow();
111 });
112
114 this,
115 [this]() {
116 d->exportFocusWindow();
117 },
119#endif
120}
121
122PimUniqueApplication::~PimUniqueApplication() = default;
123
124QCommandLineParser *PimUniqueApplication::cmdArgs() const
125{
126 return d->cmdArgs;
127}
128
129void PimUniqueApplication::setAboutData(KAboutData &aboutData)
130{
132 aboutData.setupCommandLine(d->cmdArgs);
133 // This object name is used in start(), and also in kontact's UniqueAppHandler.
134 const QString objectName = QLatin1Char('/') + QApplication::applicationName() + "_PimApplication"_L1;
136 this,
139}
140
141static bool callNewInstance(const QString &appName, const QString &serviceName, const QByteArray &asn_id, const QStringList &arguments)
142{
143 const QString objectName = QLatin1Char('/') + appName + "_PimApplication"_L1;
144 QDBusInterface iface(serviceName, objectName, QStringLiteral("org.kde.PIMUniqueApplication"), QDBusConnection::sessionBus());
145 if (iface.isValid()) {
146 QDBusReply<int> reply = iface.call(QStringLiteral("newInstance"), asn_id, arguments, QDir::currentPath());
147 if (reply.isValid()) {
148 return true;
149 }
150 }
151 return false;
152}
153
154int PimUniqueApplication::newInstance()
155{
156 return newInstance(QByteArray(), QStringList() << QApplication::applicationName(), QDir::currentPath());
157}
158
160{
161 const QString appName = QApplication::applicationName();
162
163 // Try talking to /appName_PimApplication in org.kde.appName,
164 // (which could be kontact or the standalone application),
165 // otherwise the current app being started will register to DBus.
166
167 const QString serviceName = "org.kde."_L1 + appName;
168 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName)) {
169 QByteArray new_asn_id;
171#if KONTACTINTERFACE_HAVE_X11
172 new_asn_id = QX11Info::nextStartupId();
173#endif
175 new_asn_id = qgetenv("XDG_ACTIVATION_TOKEN");
176 }
177
178 if (callNewInstance(appName, serviceName, new_asn_id, arguments)) {
179 return false; // success means that main() can exit now.
180 }
181 }
182
183 qCDebug(KONTACTINTERFACE_LOG) << "kontact not running -- start standalone application";
184
186
187 // Make sure we have DrKonqi
188 PimUniqueApplicationPrivate::disableChromiumCrashHandler();
189
190 static_cast<PimUniqueApplication *>(qApp)->activate(arguments, QDir::currentPath());
191 return true;
192}
193
194// This is called via DBus either by another instance that has just been
195// started or by Kontact when the module is activated
196int PimUniqueApplication::newInstance(const QByteArray &startupId, const QStringList &arguments, const QString &workingDirectory)
197{
199#if KONTACTINTERFACE_HAVE_X11
201#endif
204 }
205
206 const QWidgetList tlws = topLevelWidgets();
207 for (QWidget *win : tlws) {
209 win->show();
210 win->setAttribute(Qt::WA_NativeWindow, true);
211
212 KWindowSystem::activateWindow(win->windowHandle());
213 break;
214 }
215 }
216
217 activate(arguments, workingDirectory);
218 return 0;
219}
220
221int PimUniqueApplication::activate(const QStringList &arguments, const QString &workingDirectory)
222{
223 Q_UNUSED(arguments)
224 Q_UNUSED(workingDirectory)
225 return 0;
226}
227
228#include "moc_pimuniqueapplication.cpp"
static void setApplicationData(const KAboutData &aboutData)
bool setupCommandLine(QCommandLineParser *parser)
static void setStartupId(const QByteArray &startup_id)
void windowExported(QWindow *window, const QString &handle)
static Q_INVOKABLE void activateWindow(QWindow *window, long time=0)
static bool isPlatformX11()
static Q_INVOKABLE void setCurrentXdgActivationToken(const QString &token)
static bool isPlatformWayland()
static bool start(const QStringList &arguments)
Register this process as a unique application, if not already running.
QCA_EXPORT QString appName()
QWidgetList topLevelWidgets()
QStringList arguments()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool registerService(const QString &serviceName)
QDBusConnection sessionBus()
bool isValid() const const
QString currentPath()
PreferNoHinting
QWindow * focusWindow()
void focusWindowChanged(QWindow *focusWindow)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
T qobject_cast(QObject *object)
QString fromUtf8(QByteArrayView str)
QueuedConnection
WA_NativeWindow
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:54:23 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.