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