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
8 SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "main.h"
12 // kwin
13 #include "platform.h"
14 #include "atoms.h"
15 #ifdef KWIN_BUILD_CMS
16 #include "colormanager.h"
17 #endif
18 #include "composite.h"
19 #include "cursor.h"
20 #include "input.h"
21 #include "options.h"
22 #include "pluginmanager.h"
23 #include "screens.h"
24 #include "screenlockerwatcher.h"
25 #include "sm.h"
26 #include "workspace.h"
27 #include "x11eventfilter.h"
28 #include "xcbutils.h"
29
30 #include <kwineffects.h>
31
32 // KDE
33 #include <KAboutData>
34 #include <KLocalizedString>
35 #include <KPluginMetaData>
36 #include <KWaylandServer/surface_interface.h>
37 // Qt
38 #include <qplatformdefs.h>
39 #include <QCommandLineParser>
40 #include <QQuickWindow>
41 #include <QStandardPaths>
42 #include <QTranslator>
43 #include <QLibraryInfo>
44
45 // system
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif // HAVE_UNISTD_H
49
50 #ifdef HAVE_MALLOC_H
51 #include <malloc.h>
52 #endif // HAVE_MALLOC_H
53
54 // xcb
55 #include <xcb/damage.h>
56 #ifndef XCB_GE_GENERIC
57 #define XCB_GE_GENERIC 35
58 #endif
59
60 Q_DECLARE_METATYPE(KSharedConfigPtr)
61
62 namespace KWin
63 {
64
65 Options* options;
66
67 Atoms* atoms;
68
69 int screen_number = -1;
70 bool is_multihead = false;
71
72 int Application::crashes = 0;
73
isX11MultiHead()74 bool Application::isX11MultiHead()
75 {
76 return is_multihead;
77 }
78
setX11MultiHead(bool multiHead)79 void Application::setX11MultiHead(bool multiHead)
80 {
81 is_multihead = multiHead;
82 }
83
setX11ScreenNumber(int screenNumber)84 void Application::setX11ScreenNumber(int screenNumber)
85 {
86 screen_number = screenNumber;
87 }
88
x11ScreenNumber()89 int Application::x11ScreenNumber()
90 {
91 return screen_number;
92 }
93
Application(Application::OperationMode mode,int & argc,char ** argv)94 Application::Application(Application::OperationMode mode, int &argc, char **argv)
95 : QApplication(argc, argv)
96 , m_eventFilter(new XcbEventFilter())
97 , m_configLock(false)
98 , m_config()
99 , m_kxkbConfig()
100 , m_operationMode(mode)
101 {
102 qRegisterMetaType<Options::WindowOperation>("Options::WindowOperation");
103 qRegisterMetaType<KWin::EffectWindow*>();
104 qRegisterMetaType<KWaylandServer::SurfaceInterface *>("KWaylandServer::SurfaceInterface *");
105 qRegisterMetaType<KSharedConfigPtr>();
106 qRegisterMetaType<std::chrono::nanoseconds>();
107 }
108
setConfigLock(bool lock)109 void Application::setConfigLock(bool lock)
110 {
111 m_configLock = lock;
112 }
113
operationMode() const114 Application::OperationMode Application::operationMode() const
115 {
116 return m_operationMode;
117 }
118
setOperationMode(OperationMode mode)119 void Application::setOperationMode(OperationMode mode)
120 {
121 m_operationMode = mode;
122 }
123
shouldUseWaylandForCompositing() const124 bool Application::shouldUseWaylandForCompositing() const
125 {
126 return m_operationMode == OperationModeWaylandOnly || m_operationMode == OperationModeXwayland;
127 }
128
start()129 void Application::start()
130 {
131 // Prevent KWin from synchronously autostarting kactivitymanagerd
132 // Indeed, kactivitymanagerd being a QApplication it will depend
133 // on KWin startup... this is unsatisfactory dependency wise,
134 // and it turns out that it leads to a deadlock in the Wayland case
135 setProperty("org.kde.KActivities.core.disableAutostart", true);
136
137 setQuitOnLastWindowClosed(false);
138
139 if (!m_config) {
140 m_config = KSharedConfig::openConfig();
141 }
142 if (!m_config->isImmutable() && m_configLock) {
143 // TODO: This shouldn't be necessary
144 //config->setReadOnly( true );
145 m_config->reparseConfiguration();
146 }
147 if (!m_kxkbConfig) {
148 m_kxkbConfig = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals);
149 }
150
151 performStartup();
152 }
153
~Application()154 Application::~Application()
155 {
156 delete options;
157 destroyPlugins();
158 destroyColorManager();
159 destroyAtoms();
160 destroyPlatform();
161 }
162
notifyStarted()163 void Application::notifyStarted()
164 {
165 Q_EMIT started();
166 }
167
destroyAtoms()168 void Application::destroyAtoms()
169 {
170 delete atoms;
171 atoms = nullptr;
172 }
173
destroyPlatform()174 void Application::destroyPlatform()
175 {
176 delete m_platform;
177 m_platform = nullptr;
178 }
179
resetCrashesCount()180 void Application::resetCrashesCount()
181 {
182 crashes = 0;
183 }
184
setCrashCount(int count)185 void Application::setCrashCount(int count)
186 {
187 crashes = count;
188 }
189
wasCrash()190 bool Application::wasCrash()
191 {
192 return crashes > 0;
193 }
194
195 static const char description[] = I18N_NOOP("KDE window manager");
196
createAboutData()197 void Application::createAboutData()
198 {
199 KAboutData aboutData(QStringLiteral(KWIN_NAME), // The program name used internally
200 i18n("KWin"), // A displayable program name string
201 QStringLiteral(KWIN_VERSION_STRING), // The program version string
202 i18n(description), // Short description of what the app does
203 KAboutLicense::GPL, // The license this code is released under
204 i18n("(c) 1999-2019, The KDE Developers")); // Copyright Statement
205
206 aboutData.addAuthor(i18n("Matthias Ettrich"), QString(), QStringLiteral("ettrich@kde.org"));
207 aboutData.addAuthor(i18n("Cristian Tibirna"), QString(), QStringLiteral("tibirna@kde.org"));
208 aboutData.addAuthor(i18n("Daniel M. Duley"), QString(), QStringLiteral("mosfet@kde.org"));
209 aboutData.addAuthor(i18n("Luboš Luňák"), QString(), QStringLiteral("l.lunak@kde.org"));
210 aboutData.addAuthor(i18n("Martin Flöser"), QString(), QStringLiteral("mgraesslin@kde.org"));
211 aboutData.addAuthor(i18n("David Edmundson"), QStringLiteral("Maintainer"), QStringLiteral("davidedmundson@kde.org"));
212 aboutData.addAuthor(i18n("Roman Gilg"), QStringLiteral("Maintainer"), QStringLiteral("subdiff@gmail.com"));
213 aboutData.addAuthor(i18n("Vlad Zahorodnii"), QStringLiteral("Maintainer"), QStringLiteral("vlad.zahorodnii@kde.org"));
214 KAboutData::setApplicationData(aboutData);
215 }
216
217 static const QString s_lockOption = QStringLiteral("lock");
218 static const QString s_crashesOption = QStringLiteral("crashes");
219
setupCommandLine(QCommandLineParser * parser)220 void Application::setupCommandLine(QCommandLineParser *parser)
221 {
222 QCommandLineOption lockOption(s_lockOption, i18n("Disable configuration options"));
223 QCommandLineOption crashesOption(s_crashesOption, i18n("Indicate that KWin has recently crashed n times"), QStringLiteral("n"));
224
225 parser->setApplicationDescription(i18n("KDE window manager"));
226 parser->addOption(lockOption);
227 parser->addOption(crashesOption);
228 KAboutData::applicationData().setupCommandLine(parser);
229 }
230
processCommandLine(QCommandLineParser * parser)231 void Application::processCommandLine(QCommandLineParser *parser)
232 {
233 KAboutData aboutData = KAboutData::applicationData();
234 aboutData.processCommandLine(parser);
235 setConfigLock(parser->isSet(s_lockOption));
236 Application::setCrashCount(parser->value(s_crashesOption).toInt());
237 }
238
setupTranslator()239 void Application::setupTranslator()
240 {
241 QTranslator *qtTranslator = new QTranslator(qApp);
242 qtTranslator->load("qt_" + QLocale::system().name(),
243 QLibraryInfo::location(QLibraryInfo::TranslationsPath));
244 installTranslator(qtTranslator);
245 }
246
setupMalloc()247 void Application::setupMalloc()
248 {
249 #ifdef M_TRIM_THRESHOLD
250 // Prevent fragmentation of the heap by malloc (glibc).
251 //
252 // The default threshold is 128*1024, which can result in a large memory usage
253 // due to fragmentation especially if we use the raster graphicssystem. On the
254 // otherside if the threshold is too low, free() starts to permanently ask the kernel
255 // about shrinking the heap.
256 #ifdef HAVE_UNISTD_H
257 const int pagesize = sysconf(_SC_PAGESIZE);
258 #else
259 const int pagesize = 4*1024;
260 #endif // HAVE_UNISTD_H
261 mallopt(M_TRIM_THRESHOLD, 5*pagesize);
262 #endif // M_TRIM_THRESHOLD
263 }
264
setupLocalizedString()265 void Application::setupLocalizedString()
266 {
267 KLocalizedString::setApplicationDomain("kwin");
268 }
269
createWorkspace()270 void Application::createWorkspace()
271 {
272 // we want all QQuickWindows with an alpha buffer, do here as Workspace might create QQuickWindows
273 QQuickWindow::setDefaultAlphaBuffer(true);
274
275 // This tries to detect compositing options and can use GLX. GLX problems
276 // (X errors) shouldn't cause kwin to abort, so this is out of the
277 // critical startup section where x errors cause kwin to abort.
278
279 // create workspace.
280 (void) new Workspace();
281 Q_EMIT workspaceCreated();
282 }
283
createInput()284 void Application::createInput()
285 {
286 ScreenLockerWatcher::create(this);
287 auto input = InputRedirection::create(this);
288 input->init();
289 m_platform->createPlatformCursor(this);
290 }
291
createScreens()292 void Application::createScreens()
293 {
294 if (Screens::self()) {
295 return;
296 }
297 Screens::create(this);
298 Q_EMIT screensCreated();
299 }
300
createAtoms()301 void Application::createAtoms()
302 {
303 atoms = new Atoms;
304 }
305
createOptions()306 void Application::createOptions()
307 {
308 options = new Options;
309 }
310
createPlugins()311 void Application::createPlugins()
312 {
313 PluginManager::create(this);
314 }
315
createColorManager()316 void Application::createColorManager()
317 {
318 #ifdef KWIN_BUILD_CMS
319 ColorManager::create(this);
320 #endif
321 }
322
installNativeX11EventFilter()323 void Application::installNativeX11EventFilter()
324 {
325 installNativeEventFilter(m_eventFilter.data());
326 }
327
removeNativeX11EventFilter()328 void Application::removeNativeX11EventFilter()
329 {
330 removeNativeEventFilter(m_eventFilter.data());
331 }
332
destroyInput()333 void Application::destroyInput()
334 {
335 delete InputRedirection::self();
336 }
337
destroyWorkspace()338 void Application::destroyWorkspace()
339 {
340 delete Workspace::self();
341 }
342
destroyCompositor()343 void Application::destroyCompositor()
344 {
345 delete Compositor::self();
346 }
347
destroyPlugins()348 void Application::destroyPlugins()
349 {
350 delete PluginManager::self();
351 }
352
destroyColorManager()353 void Application::destroyColorManager()
354 {
355 #ifdef KWIN_BUILD_CMS
356 delete ColorManager::self();
357 #endif
358 }
359
registerEventFilter(X11EventFilter * filter)360 void Application::registerEventFilter(X11EventFilter *filter)
361 {
362 if (filter->isGenericEvent()) {
363 m_genericEventFilters.append(new X11EventFilterContainer(filter));
364 } else {
365 m_eventFilters.append(new X11EventFilterContainer(filter));
366 }
367 }
368
takeEventFilter(X11EventFilter * eventFilter,QList<QPointer<X11EventFilterContainer>> & list)369 static X11EventFilterContainer *takeEventFilter(X11EventFilter *eventFilter,
370 QList<QPointer<X11EventFilterContainer>> &list)
371 {
372 for (int i = 0; i < list.count(); ++i) {
373 X11EventFilterContainer *container = list.at(i);
374 if (container->filter() == eventFilter) {
375 return list.takeAt(i);
376 }
377 }
378 return nullptr;
379 }
380
unregisterEventFilter(X11EventFilter * filter)381 void Application::unregisterEventFilter(X11EventFilter *filter)
382 {
383 X11EventFilterContainer *container = nullptr;
384 if (filter->isGenericEvent()) {
385 container = takeEventFilter(filter, m_genericEventFilters);
386 } else {
387 container = takeEventFilter(filter, m_eventFilters);
388 }
389 delete container;
390 }
391
dispatchEvent(xcb_generic_event_t * event)392 bool Application::dispatchEvent(xcb_generic_event_t *event)
393 {
394 static const QVector<QByteArray> s_xcbEerrors({
395 QByteArrayLiteral("Success"),
396 QByteArrayLiteral("BadRequest"),
397 QByteArrayLiteral("BadValue"),
398 QByteArrayLiteral("BadWindow"),
399 QByteArrayLiteral("BadPixmap"),
400 QByteArrayLiteral("BadAtom"),
401 QByteArrayLiteral("BadCursor"),
402 QByteArrayLiteral("BadFont"),
403 QByteArrayLiteral("BadMatch"),
404 QByteArrayLiteral("BadDrawable"),
405 QByteArrayLiteral("BadAccess"),
406 QByteArrayLiteral("BadAlloc"),
407 QByteArrayLiteral("BadColor"),
408 QByteArrayLiteral("BadGC"),
409 QByteArrayLiteral("BadIDChoice"),
410 QByteArrayLiteral("BadName"),
411 QByteArrayLiteral("BadLength"),
412 QByteArrayLiteral("BadImplementation"),
413 QByteArrayLiteral("Unknown")
414 });
415
416 kwinApp()->updateX11Time(event);
417
418 const uint8_t x11EventType = event->response_type & ~0x80;
419 if (!x11EventType) {
420 // let's check whether it's an error from one of the extensions KWin uses
421 xcb_generic_error_t *error = reinterpret_cast<xcb_generic_error_t*>(event);
422 const QVector<Xcb::ExtensionData> extensions = Xcb::Extensions::self()->extensions();
423 for (const auto &extension : extensions) {
424 if (error->major_code == extension.majorOpcode) {
425 QByteArray errorName;
426 if (error->error_code < s_xcbEerrors.size()) {
427 errorName = s_xcbEerrors.at(error->error_code);
428 } else if (error->error_code >= extension.errorBase) {
429 const int index = error->error_code - extension.errorBase;
430 if (index >= 0 && index < extension.errorCodes.size()) {
431 errorName = extension.errorCodes.at(index);
432 }
433 }
434 if (errorName.isEmpty()) {
435 errorName = QByteArrayLiteral("Unknown");
436 }
437 qCWarning(KWIN_CORE, "XCB error: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d (%s)",
438 int(error->error_code), errorName.constData(),
439 int(error->sequence), int(error->resource_id),
440 int(error->major_code), extension.name.constData(),
441 int(error->minor_code),
442 extension.opCodes.size() > error->minor_code ? extension.opCodes.at(error->minor_code).constData() : "Unknown");
443 return true;
444 }
445 }
446 return false;
447 }
448
449 if (x11EventType == XCB_GE_GENERIC) {
450 xcb_ge_generic_event_t *ge = reinterpret_cast<xcb_ge_generic_event_t *>(event);
451
452 // We need to make a shadow copy of the event filter list because an activated event
453 // filter may mutate it by removing or installing another event filter.
454 const auto eventFilters = m_genericEventFilters;
455
456 for (X11EventFilterContainer *container : eventFilters) {
457 if (!container) {
458 continue;
459 }
460 X11EventFilter *filter = container->filter();
461 if (filter->extension() == ge->extension && filter->genericEventTypes().contains(ge->event_type) && filter->event(event)) {
462 return true;
463 }
464 }
465 } else {
466 // We need to make a shadow copy of the event filter list because an activated event
467 // filter may mutate it by removing or installing another event filter.
468 const auto eventFilters = m_eventFilters;
469
470 for (X11EventFilterContainer *container : eventFilters) {
471 if (!container) {
472 continue;
473 }
474 X11EventFilter *filter = container->filter();
475 if (filter->eventTypes().contains(x11EventType) && filter->event(event)) {
476 return true;
477 }
478 }
479 }
480
481 if (workspace()) {
482 return workspace()->workspaceEvent(event);
483 }
484
485 return false;
486 }
487
updateX11Time(xcb_generic_event_t * event)488 void Application::updateX11Time(xcb_generic_event_t *event)
489 {
490 xcb_timestamp_t time = XCB_TIME_CURRENT_TIME;
491 const uint8_t eventType = event->response_type & ~0x80;
492 switch(eventType) {
493 case XCB_KEY_PRESS:
494 case XCB_KEY_RELEASE:
495 time = reinterpret_cast<xcb_key_press_event_t*>(event)->time;
496 break;
497 case XCB_BUTTON_PRESS:
498 case XCB_BUTTON_RELEASE:
499 time = reinterpret_cast<xcb_button_press_event_t*>(event)->time;
500 break;
501 case XCB_MOTION_NOTIFY:
502 time = reinterpret_cast<xcb_motion_notify_event_t*>(event)->time;
503 break;
504 case XCB_ENTER_NOTIFY:
505 case XCB_LEAVE_NOTIFY:
506 time = reinterpret_cast<xcb_enter_notify_event_t*>(event)->time;
507 break;
508 case XCB_FOCUS_IN:
509 case XCB_FOCUS_OUT:
510 case XCB_KEYMAP_NOTIFY:
511 case XCB_EXPOSE:
512 case XCB_GRAPHICS_EXPOSURE:
513 case XCB_NO_EXPOSURE:
514 case XCB_VISIBILITY_NOTIFY:
515 case XCB_CREATE_NOTIFY:
516 case XCB_DESTROY_NOTIFY:
517 case XCB_UNMAP_NOTIFY:
518 case XCB_MAP_NOTIFY:
519 case XCB_MAP_REQUEST:
520 case XCB_REPARENT_NOTIFY:
521 case XCB_CONFIGURE_NOTIFY:
522 case XCB_CONFIGURE_REQUEST:
523 case XCB_GRAVITY_NOTIFY:
524 case XCB_RESIZE_REQUEST:
525 case XCB_CIRCULATE_NOTIFY:
526 case XCB_CIRCULATE_REQUEST:
527 // no timestamp
528 return;
529 case XCB_PROPERTY_NOTIFY:
530 time = reinterpret_cast<xcb_property_notify_event_t*>(event)->time;
531 break;
532 case XCB_SELECTION_CLEAR:
533 time = reinterpret_cast<xcb_selection_clear_event_t*>(event)->time;
534 break;
535 case XCB_SELECTION_REQUEST:
536 time = reinterpret_cast<xcb_selection_request_event_t*>(event)->time;
537 break;
538 case XCB_SELECTION_NOTIFY:
539 time = reinterpret_cast<xcb_selection_notify_event_t*>(event)->time;
540 break;
541 case XCB_COLORMAP_NOTIFY:
542 case XCB_CLIENT_MESSAGE:
543 case XCB_MAPPING_NOTIFY:
544 case XCB_GE_GENERIC:
545 // no timestamp
546 return;
547 default:
548 // extension handling
549 if (Xcb::Extensions::self()) {
550 if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) {
551 time = reinterpret_cast<xcb_shape_notify_event_t*>(event)->server_time;
552 }
553 if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) {
554 time = reinterpret_cast<xcb_damage_notify_event_t*>(event)->timestamp;
555 }
556 }
557 break;
558 }
559 setX11Time(time);
560 }
561
nativeEventFilter(const QByteArray & eventType,void * message,long int * result)562 bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long int *result)
563 {
564 Q_UNUSED(result)
565 if (eventType == "xcb_generic_event_t") {
566 return kwinApp()->dispatchEvent(static_cast<xcb_generic_event_t *>(message));
567 }
568 return false;
569 }
570
571 static bool s_useLibinput = false;
572
setUseLibinput(bool use)573 void Application::setUseLibinput(bool use)
574 {
575 s_useLibinput = use;
576 }
577
usesLibinput()578 bool Application::usesLibinput()
579 {
580 return s_useLibinput;
581 }
582
processStartupEnvironment() const583 QProcessEnvironment Application::processStartupEnvironment() const
584 {
585 return QProcessEnvironment::systemEnvironment();
586 }
587
initPlatform(const KPluginMetaData & plugin)588 void Application::initPlatform(const KPluginMetaData &plugin)
589 {
590 Q_ASSERT(!m_platform);
591 QPluginLoader loader(plugin.fileName());
592 m_platform = qobject_cast<Platform *>(loader.instance());
593 if (m_platform) {
594 m_platform->setParent(this);
595 // check whether it needs libinput
596 const QJsonObject &metaData = plugin.rawData();
597 auto it = metaData.find(QStringLiteral("input"));
598 if (it != metaData.end()) {
599 if ((*it).isBool()) {
600 if (!(*it).toBool()) {
601 qCDebug(KWIN_CORE) << "Platform does not support input, enforcing libinput support";
602 setUseLibinput(true);
603 }
604 }
605 }
606 Q_EMIT platformCreated();
607 } else {
608 qCWarning(KWIN_CORE) << "Could not create plugin" << plugin.name() << "error:" << loader.errorString();
609 }
610 }
611
ApplicationWaylandAbstract(OperationMode mode,int & argc,char ** argv)612 ApplicationWaylandAbstract::ApplicationWaylandAbstract(OperationMode mode, int &argc, char **argv)
613 : Application(mode, argc, argv)
614 {
615 }
616
~ApplicationWaylandAbstract()617 ApplicationWaylandAbstract::~ApplicationWaylandAbstract()
618 {
619 }
620
621 } // namespace
622
623