KWin
Loading...
Searching...
No Matches
main_x11.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11#include "main_x11.h"
12
13#include <config-kwin.h>
14
16#include "compositor_x11.h"
17#include "core/outputbackend.h"
18#include "core/session.h"
19#include "cursor.h"
21#include "outline.h"
22#include "screenedge.h"
23#include "sm.h"
24#include "tabletmodemanager.h"
25#include "utils/xcbutils.h"
26#include "workspace.h"
27
28#include <KConfigGroup>
29#include <KCrash>
30#include <KGlobalAccel>
31#include <KLocalizedString>
32#include <KSelectionOwner>
33#include <KSignalHandler>
34
35#include <QAction>
36#include <QComboBox>
37#include <QCommandLineParser>
38#include <QDialog>
39#include <QDialogButtonBox>
40#include <QFile>
41#include <QLabel>
42#include <QPushButton>
43#include <QSurfaceFormat>
44#include <QVBoxLayout>
45#include <qplatformdefs.h>
46#include <private/qtx11extras_p.h>
47#include <QtDBus>
48
49// system
50#include <iostream>
51#include <unistd.h>
52
53Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtWarningMsg)
54
55namespace KWin
56{
57
58class AlternativeWMDialog : public QDialog
59{
60public:
62 : QDialog()
63 {
64 QWidget *mainWidget = new QWidget(this);
65 QVBoxLayout *layout = new QVBoxLayout(mainWidget);
66 QString text = i18n("KWin is unstable.\n"
67 "It seems to have crashed several times in a row.\n"
68 "You can select another window manager to run:");
69 QLabel *textLabel = new QLabel(text, mainWidget);
70 layout->addWidget(textLabel);
71 wmList = new QComboBox(mainWidget);
72 wmList->setEditable(true);
73 layout->addWidget(wmList);
74
75 addWM(QStringLiteral("metacity"));
76 addWM(QStringLiteral("openbox"));
77 addWM(QStringLiteral("fvwm2"));
78 addWM(QStringLiteral("kwin_x11"));
79
80 QVBoxLayout *mainLayout = new QVBoxLayout(this);
81 mainLayout->addWidget(mainWidget);
82 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
83 buttons->button(QDialogButtonBox::Ok)->setDefault(true);
84 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
85 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
86 mainLayout->addWidget(buttons);
87
88 raise();
89 }
90
91 void addWM(const QString &wm)
92 {
93 // TODO: Check if WM is installed
94 if (!QStandardPaths::findExecutable(wm).isEmpty()) {
95 wmList->addItem(wm);
96 }
97 }
98 QString selectedWM() const
99 {
100 return wmList->currentText();
101 }
102
103private:
104 QComboBox *wmList;
105};
106
107class KWinSelectionOwner : public KSelectionOwner
108{
109 Q_OBJECT
110public:
112 : KSelectionOwner(make_selection_atom())
113 {
114 }
115
116private:
117 bool genericReply(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P) override
118 {
119 if (target_P == xa_version) {
120 int32_t version[] = {2, 0};
121 xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_REPLACE, requestor_P,
122 property_P, XCB_ATOM_INTEGER, 32, 2, version);
123 } else {
124 return KSelectionOwner::genericReply(target_P, property_P, requestor_P);
125 }
126 return true;
127 }
128
129 void replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P) override
130 {
131 KSelectionOwner::replyTargets(property_P, requestor_P);
132 xcb_atom_t atoms[1] = {xa_version};
133 // PropModeAppend !
134 xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_APPEND, requestor_P,
135 property_P, XCB_ATOM_ATOM, 32, 1, atoms);
136 }
137
138 void getAtoms() override
139 {
140 KSelectionOwner::getAtoms();
141 if (xa_version == XCB_ATOM_NONE) {
142 constexpr QByteArrayView name{"VERSION"};
143 UniqueCPtr<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(
144 kwinApp()->x11Connection(),
145 xcb_intern_atom_unchecked(kwinApp()->x11Connection(), false, name.length(), name.constData()),
146 nullptr));
147 if (atom) {
148 xa_version = atom->atom;
149 }
150 }
151 }
152
153 xcb_atom_t make_selection_atom()
154 {
155 constexpr QByteArrayView screen{"WM_S0"};
156 UniqueCPtr<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(
157 kwinApp()->x11Connection(),
158 xcb_intern_atom_unchecked(kwinApp()->x11Connection(), false, screen.length(), screen.constData()),
159 nullptr));
160 if (!atom) {
161 return XCB_ATOM_NONE;
162 }
163 return atom->atom;
164 }
165 static xcb_atom_t xa_version;
166};
167xcb_atom_t KWinSelectionOwner::xa_version = XCB_ATOM_NONE;
168
169//************************************
170// ApplicationX11
171//************************************
172
173ApplicationX11::ApplicationX11(int &argc, char **argv)
174 : Application(OperationModeX11, argc, argv)
175 , owner()
176 , m_replace(false)
177{
178 setX11Connection(QX11Info::connection());
179 setX11RootWindow(QX11Info::appRootWindow());
180}
181
183{
185 // need to unload all effects before destroying Workspace, as effects might call into Workspace
186 if (effects) {
188 }
193 // If there was no --replace (no new WM)
194 if (owner != nullptr && owner->ownerWindow() != XCB_WINDOW_NONE) {
195 Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
196 }
197}
198
200{
201 m_replace = replace;
202}
203
204std::unique_ptr<Edge> ApplicationX11::createScreenEdge(ScreenEdges *parent)
205{
206 return static_cast<X11StandaloneBackend *>(outputBackend())->createScreenEdge(parent);
207}
208
210{
211 return static_cast<X11StandaloneBackend *>(outputBackend())->createPlatformCursor();
212}
213
214std::unique_ptr<OutlineVisual> ApplicationX11::createOutline(Outline *outline)
215{
216 // first try composited Outline
217 if (auto outlineVisual = Application::createOutline(outline)) {
218 return outlineVisual;
219 }
220 return static_cast<X11StandaloneBackend *>(outputBackend())->createOutline(outline);
221}
222
224{
225 static_cast<X11StandaloneBackend *>(outputBackend())->createEffectsHandler(compositor, scene);
226}
227
228void ApplicationX11::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName)
229{
230 static_cast<X11StandaloneBackend *>(outputBackend())->startInteractiveWindowSelection(callback, cursorName);
231}
232
233void ApplicationX11::startInteractivePositionSelection(std::function<void(const QPointF &)> callback)
234{
236}
237
242
243void ApplicationX11::lostSelection()
244{
245 sendPostedEvents();
246 // need to unload all effects before destroying Workspace, as effects might call into Workspace
247 if (effects) {
249 }
254 // Remove windowmanager privileges
255 Xcb::selectInput(kwinApp()->x11RootWindow(), XCB_EVENT_MASK_PROPERTY_CHANGE);
257 quit();
258}
259
261{
262 crashChecking();
263
264 owner = std::make_unique<KWinSelectionOwner>();
265 connect(owner.get(), &KSelectionOwner::failedToClaimOwnership, [] {
266 fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit().constData(), stderr);
267 ::exit(1);
268 });
269 connect(owner.get(), &KSelectionOwner::lostOwnership, this, &ApplicationX11::lostSelection);
270 connect(owner.get(), &KSelectionOwner::claimedOwnership, this, [this] {
271 installNativeX11EventFilter();
272 createOptions();
273
274 if (!outputBackend()->initialize()) {
275 std::exit(1);
276 }
277
278 // Check whether another windowmanager is running
279 const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
280 UniqueCPtr<xcb_generic_error_t> redirectCheck(xcb_request_check(kwinApp()->x11Connection(),
281 xcb_change_window_attributes_checked(kwinApp()->x11Connection(),
282 kwinApp()->x11RootWindow(),
283 XCB_CW_EVENT_MASK,
284 maskValues)));
285 if (redirectCheck) {
286 fputs(i18n("kwin: another window manager is running (try using --replace)\n").toLocal8Bit().constData(), stderr);
287 if (!wasCrash()) { // if this is a crash-restart, DrKonqi may have stopped the process w/o killing the connection
288 ::exit(1);
289 }
290 }
291
292 // Update the timestamp if a global shortcut is pressed or released. Needed
293 // to ensure that kwin can grab the keyboard.
294 connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutActiveChanged, this, [this](QAction *triggeredAction) {
295 QVariant timestamp = triggeredAction->property("org.kde.kglobalaccel.activationTimestamp");
296 bool ok = false;
297 const quint32 t = timestamp.toULongLong(&ok);
298 if (ok) {
299 setX11Time(t);
300 }
301 });
302
303 createInput();
308
309 Xcb::sync(); // Trigger possible errors, there's still a chance to abort
310
311 notifyKSplash();
313 });
314 // we need to do an XSync here, otherwise the QPA might crash us later on
315 Xcb::sync();
316 owner->claim(m_replace || wasCrash(), true);
317
318 createAtoms();
319
320 createTabletModeManager();
321}
322
323void ApplicationX11::setupCrashHandler()
324{
325 KCrash::setEmergencySaveFunction(ApplicationX11::crashHandler);
326}
327
328void ApplicationX11::crashChecking()
329{
330 setupCrashHandler();
331 if (crashes >= 4) {
332 // Something has gone seriously wrong
333 AlternativeWMDialog dialog;
334 QString cmd = QStringLiteral("kwin_x11");
335 if (dialog.exec() == QDialog::Accepted) {
336 cmd = dialog.selectedWM();
337 } else {
338 ::exit(1);
339 }
340 if (cmd.length() > 500) {
341 qCDebug(KWIN_CORE) << "Command is too long, truncating";
342 cmd = cmd.left(500);
343 }
344 qCDebug(KWIN_CORE) << "Starting" << cmd << "and exiting";
345 char buf[1024];
346 sprintf(buf, "%s &", cmd.toLatin1().data());
347 system(buf);
348 ::exit(1);
349 }
350 // Reset crashes count if we stay up for more that 15 seconds
351 QTimer::singleShot(15 * 1000, this, &Application::resetCrashesCount);
352}
353
354void ApplicationX11::notifyKSplash()
355{
356 // Tell KSplash that KWin has started
357 QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"),
358 QStringLiteral("/KSplash"),
359 QStringLiteral("org.kde.KSplash"),
360 QStringLiteral("setStage"));
361 ksplashProgressMessage.setArguments(QList<QVariant>() << QStringLiteral("wm"));
362 QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage);
363}
364
365void ApplicationX11::crashHandler(int signal)
366{
367 crashes++;
368
369 fprintf(stderr, "Application::crashHandler() called with signal %d; recent crashes: %d\n", signal, crashes);
370 char cmd[1024];
371 sprintf(cmd, "%s --crashes %d &",
372 QFile::encodeName(QCoreApplication::applicationFilePath()).constData(), crashes);
373
374 sleep(1);
375 system(cmd);
376}
377
378} // namespace
379
380int main(int argc, char *argv[])
381{
384
385 signal(SIGPIPE, SIG_IGN);
386
387 // enforce xcb plugin, unfortunately command line switch has precedence
388 setenv("QT_QPA_PLATFORM", "xcb", true);
389
390 // disable highdpi scaling
391 setenv("QT_ENABLE_HIGHDPI_SCALING", "0", true);
392
393 qunsetenv("QT_SCALE_FACTOR");
394 qunsetenv("QT_SCREEN_SCALE_FACTORS");
395
396 // KSMServer talks to us directly on DBus.
397 QCoreApplication::setAttribute(Qt::AA_DisableSessionManager);
398 // For sharing thumbnails between our scene graph and qtquick.
399 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
400
401 QSurfaceFormat format = QSurfaceFormat::defaultFormat();
402 // shared opengl contexts must have the same reset notification policy
403 format.setOptions(QSurfaceFormat::ResetNotification);
404 // disables vsync for any QtQuick windows we create (BUG 406180)
405 format.setSwapInterval(0);
406 QSurfaceFormat::setDefaultFormat(format);
407
408 KWin::ApplicationX11 a(argc, argv);
409
410 // reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect)
411 qunsetenv("QT_QPA_PLATFORM");
412 qunsetenv("QT_ENABLE_HIGHDPI_SCALING");
413
414 a.setProcessStartupEnvironment(QProcessEnvironment(QProcessEnvironment::InheritFromParent));
415
416 KSignalHandler::self()->watchSignal(SIGTERM);
417 KSignalHandler::self()->watchSignal(SIGINT);
418 KSignalHandler::self()->watchSignal(SIGHUP);
419 QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived,
420 &a, &QCoreApplication::exit);
421
423
424 QCommandLineOption replaceOption(QStringLiteral("replace"), i18n("Replace already-running ICCCM2.0-compliant window manager"));
425
426 QCommandLineParser parser;
427 a.setupCommandLine(&parser);
428 parser.addOption(replaceOption);
429#if KWIN_BUILD_ACTIVITIES
430 QCommandLineOption noActivitiesOption(QStringLiteral("no-kactivities"),
431 i18n("Disable KActivities integration."));
432 parser.addOption(noActivitiesOption);
433#endif
434
435 parser.process(a);
436 a.processCommandLine(&parser);
437 a.setReplace(parser.isSet(replaceOption));
438#if KWIN_BUILD_ACTIVITIES
439 if (parser.isSet(noActivitiesOption)) {
440 a.setUseKActivities(false);
441 }
442#endif
443
444 // perform sanity checks
445 if (a.platformName().toLower() != QStringLiteral("xcb")) {
446 fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n",
447 argv[0], qPrintable(a.platformName()));
448 exit(1);
449 }
450 if (!QX11Info::display()) {
451 fprintf(stderr, "%s: FATAL ERROR KWin requires Xlib support in the xcb plugin. Do not configure Qt with -no-xcb-xlib\n",
452 argv[0]);
453 exit(1);
454 }
455
457 a.setOutputBackend(std::make_unique<KWin::X11StandaloneBackend>());
458 a.start();
459
460 return a.exec();
461}
462
463#include "main_x11.moc"
464
465#include "moc_main_x11.cpp"
void addWM(const QString &wm)
Definition main_x11.cpp:91
QString selectedWM() const
Definition main_x11.cpp:98
void destroyWorkspace()
Definition main.cpp:313
void notifyStarted()
Definition main.cpp:146
OutputBackend * outputBackend() const
Definition main.h:242
void destroyColorManager()
Definition main.cpp:328
static void createAboutData()
Definition main.cpp:177
virtual std::unique_ptr< OutlineVisual > createOutline(Outline *outline)
Definition main.cpp:348
void createPlugins()
Definition main.cpp:273
void setX11RootWindow(xcb_window_t root)
Definition main.h:204
static void setupMalloc()
Definition main.cpp:220
void createInput()
Definition main.cpp:253
void setSession(std::unique_ptr< Session > &&session)
Definition main.cpp:644
void destroyPlugins()
Definition main.cpp:323
void setOutputBackend(std::unique_ptr< OutputBackend > &&backend)
Definition main.cpp:638
void setX11Time(xcb_timestamp_t timestamp, TimestampUpdate force=TimestampUpdate::OnlyIfLarger)
Definition main.h:154
void processCommandLine(QCommandLineParser *parser)
Definition main.cpp:212
void setProcessStartupEnvironment(const QProcessEnvironment &environment)
Definition main.cpp:633
void removeNativeX11EventFilter()
Definition main.cpp:303
void createColorManager()
Definition main.cpp:278
static void setupLocalizedString()
Definition main.cpp:234
static bool wasCrash()
Definition main.cpp:172
void setupCommandLine(QCommandLineParser *parser)
Definition main.cpp:201
void * x11Connection
Definition main.h:73
void createWorkspace()
Definition main.cpp:239
void setTerminating()
Definition main.h:365
void destroyCompositor()
Definition main.cpp:318
void setX11Connection(xcb_connection_t *c)
Definition main.h:212
quint32 x11RootWindow
Definition main.h:72
ApplicationX11(int &argc, char **argv)
Definition main_x11.cpp:173
void setReplace(bool replace)
Definition main_x11.cpp:199
void startInteractiveWindowSelection(std::function< void(KWin::Window *)> callback, const QByteArray &cursorName=QByteArray()) override
Definition main_x11.cpp:228
std::unique_ptr< OutlineVisual > createOutline(Outline *outline) override
Definition main_x11.cpp:214
void performStartup() override
Definition main_x11.cpp:260
void startInteractivePositionSelection(std::function< void(const QPointF &)> callback) override
Definition main_x11.cpp:233
std::unique_ptr< Edge > createScreenEdge(ScreenEdges *parent) override
Definition main_x11.cpp:204
std::unique_ptr< Cursor > createPlatformCursor() override
Definition main_x11.cpp:209
PlatformCursorImage cursorImage() const override
Definition main_x11.cpp:238
~ApplicationX11() override
Definition main_x11.cpp:182
void createEffectsHandler(Compositor *compositor, WorkspaceScene *scene) override
Definition main_x11.cpp:223
This class is used to show the outline of a given geometry.
Definition outline.h:38
Class for controlling screen edges.
Definition screenedge.h:222
static std::unique_ptr< Session > create()
Definition session.cpp:25
static X11Compositor * create(QObject *parent=nullptr)
constexpr int version
EffectsHandler * effects
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28