File indexing completed on 2024-02-18 16:20:08

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "main_wayland.h"
0010 
0011 #include <config-kwin.h>
0012 
0013 #include "backends/drm/drm_backend.h"
0014 #include "backends/virtual/virtual_backend.h"
0015 #include "backends/wayland/wayland_backend.h"
0016 #include "backends/x11/windowed/x11_windowed_backend.h"
0017 #include "composite.h"
0018 #include "core/outputbackend.h"
0019 #include "core/session.h"
0020 #include "effects.h"
0021 #include "inputmethod.h"
0022 #include "tabletmodemanager.h"
0023 #include "utils/realtime.h"
0024 #include "wayland/display.h"
0025 #include "wayland/seat_interface.h"
0026 #include "wayland_server.h"
0027 #include "workspace.h"
0028 #include "xwayland/xwayland.h"
0029 #include "xwayland/xwaylandlauncher.h"
0030 
0031 // KDE
0032 #include <KCrash>
0033 #include <KDesktopFile>
0034 #include <KLocalizedString>
0035 #include <KShell>
0036 #include <KSignalHandler>
0037 
0038 // Qt
0039 #include <QCommandLineParser>
0040 #include <QDBusInterface>
0041 #include <QDebug>
0042 #include <QFileInfo>
0043 #include <QProcess>
0044 #include <QWindow>
0045 #include <qplatformdefs.h>
0046 
0047 #include <sched.h>
0048 #include <sys/resource.h>
0049 
0050 #include <iomanip>
0051 #include <iostream>
0052 
0053 Q_IMPORT_PLUGIN(KWinIntegrationPlugin)
0054 Q_IMPORT_PLUGIN(KGlobalAccelImpl)
0055 Q_IMPORT_PLUGIN(KWindowSystemKWinPlugin)
0056 Q_IMPORT_PLUGIN(KWinIdleTimePoller)
0057 #if PipeWire_FOUND
0058 Q_IMPORT_PLUGIN(ScreencastManagerFactory)
0059 #endif
0060 
0061 namespace KWin
0062 {
0063 
0064 static rlimit originalNofileLimit = {
0065     .rlim_cur = 0,
0066     .rlim_max = 0,
0067 };
0068 
0069 static bool bumpNofileLimit()
0070 {
0071     if (getrlimit(RLIMIT_NOFILE, &originalNofileLimit) == -1) {
0072         std::cerr << "Failed to bump RLIMIT_NOFILE limit, getrlimit() failed: " << strerror(errno) << std::endl;
0073         return false;
0074     }
0075 
0076     rlimit limit = originalNofileLimit;
0077     limit.rlim_cur = limit.rlim_max;
0078 
0079     if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
0080         std::cerr << "Failed to bump RLIMIT_NOFILE limit, setrlimit() failed: " << strerror(errno) << std::endl;
0081         return false;
0082     }
0083 
0084     return true;
0085 }
0086 
0087 static void restoreNofileLimit()
0088 {
0089     if (setrlimit(RLIMIT_NOFILE, &originalNofileLimit) == -1) {
0090         std::cerr << "Failed to restore RLIMIT_NOFILE limit, legacy apps might be broken" << std::endl;
0091     }
0092 }
0093 
0094 void disableDrKonqi()
0095 {
0096     KCrash::setDrKonqiEnabled(false);
0097 }
0098 // run immediately, before Q_CORE_STARTUP functions
0099 // that would enable drkonqi
0100 Q_CONSTRUCTOR_FUNCTION(disableDrKonqi)
0101 
0102 //************************************
0103 // ApplicationWayland
0104 //************************************
0105 
0106 ApplicationWayland::ApplicationWayland(int &argc, char **argv)
0107     : Application(OperationModeWaylandOnly, argc, argv)
0108 {
0109 }
0110 
0111 ApplicationWayland::~ApplicationWayland()
0112 {
0113     setTerminating();
0114     if (!waylandServer()) {
0115         return;
0116     }
0117 
0118     destroyPlugins();
0119 
0120     // need to unload all effects prior to destroying X connection as they might do X calls
0121     if (effects) {
0122         static_cast<EffectsHandlerImpl *>(effects)->unloadAllEffects();
0123     }
0124     m_xwayland.reset();
0125     destroyColorManager();
0126     destroyWorkspace();
0127 
0128     destroyInputMethod();
0129     destroyCompositor();
0130     destroyInput();
0131 }
0132 
0133 void ApplicationWayland::performStartup()
0134 {
0135     if (m_startXWayland) {
0136         setOperationMode(OperationModeXwayland);
0137         setXwaylandScale(config()->group("Xwayland").readEntry("Scale", 1.0));
0138     }
0139     // first load options - done internally by a different thread
0140     createOptions();
0141 
0142     if (!outputBackend()->initialize()) {
0143         std::exit(1);
0144     }
0145 
0146     createInput();
0147     createInputMethod();
0148     createTabletModeManager();
0149 
0150     WaylandCompositor::create();
0151 
0152     connect(Compositor::self(), &Compositor::sceneCreated, outputBackend(), &OutputBackend::sceneInitialized);
0153     connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithScene);
0154 }
0155 
0156 void ApplicationWayland::continueStartupWithScene()
0157 {
0158     disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithScene);
0159 
0160     // Note that we start accepting client connections after creating the Workspace.
0161     createWorkspace();
0162     createColorManager();
0163     createPlugins();
0164 
0165     if (!waylandServer()->start()) {
0166         qFatal("Failed to initialze the Wayland server, exiting now");
0167     }
0168 
0169     if (operationMode() == OperationModeWaylandOnly) {
0170         finalizeStartup();
0171         return;
0172     }
0173 
0174     m_xwayland = std::make_unique<Xwl::Xwayland>(this);
0175     m_xwayland->xwaylandLauncher()->setListenFDs(m_xwaylandListenFds);
0176     m_xwayland->xwaylandLauncher()->setDisplayName(m_xwaylandDisplay);
0177     m_xwayland->xwaylandLauncher()->setXauthority(m_xwaylandXauthority);
0178     connect(m_xwayland.get(), &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup);
0179     connect(m_xwayland.get(), &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup);
0180     m_xwayland->start();
0181 }
0182 
0183 void ApplicationWayland::finalizeStartup()
0184 {
0185     if (m_xwayland) {
0186         disconnect(m_xwayland.get(), &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup);
0187         disconnect(m_xwayland.get(), &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup);
0188     }
0189     startSession();
0190     notifyStarted();
0191 }
0192 
0193 void ApplicationWayland::refreshSettings(const KConfigGroup &group, const QByteArrayList &names)
0194 {
0195     if (group.name() == "Wayland" && names.contains("InputMethod")) {
0196         KDesktopFile file(group.readPathEntry("InputMethod", QString()));
0197         kwinApp()->inputMethod()->setInputMethodCommand(file.desktopGroup().readEntry("Exec", QString()));
0198     }
0199 
0200     if (m_startXWayland && group.name() == "Xwayland" && names.contains("Scale")) {
0201         setXwaylandScale(group.readEntry("Scale", 1.0));
0202     }
0203 }
0204 
0205 void ApplicationWayland::startSession()
0206 {
0207     KSharedConfig::Ptr kwinSettings = kwinApp()->config();
0208     m_settingsWatcher = KConfigWatcher::create(kwinSettings);
0209     connect(m_settingsWatcher.data(), &KConfigWatcher::configChanged, this, &ApplicationWayland::refreshSettings);
0210 
0211     if (!m_inputMethodServerToStart.isEmpty()) {
0212         kwinApp()->inputMethod()->setInputMethodCommand(m_inputMethodServerToStart);
0213     } else {
0214         refreshSettings(kwinSettings->group("Wayland"), {"InputMethod"});
0215     }
0216 
0217     // start session
0218     if (!m_sessionArgument.isEmpty()) {
0219         QStringList arguments = KShell::splitArgs(m_sessionArgument);
0220         if (!arguments.isEmpty()) {
0221             QString program = arguments.takeFirst();
0222             QProcess *p = new QProcess(this);
0223             p->setProcessChannelMode(QProcess::ForwardedErrorChannel);
0224             p->setProcessEnvironment(processStartupEnvironment());
0225             connect(p, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, [p](int code, QProcess::ExitStatus status) {
0226                 p->deleteLater();
0227                 if (status == QProcess::CrashExit) {
0228                     qWarning() << "Session process has crashed";
0229                     QCoreApplication::exit(-1);
0230                     return;
0231                 }
0232 
0233                 if (code) {
0234                     qWarning() << "Session process exited with code" << code;
0235                 }
0236 
0237                 QCoreApplication::exit(code);
0238             });
0239             p->setProgram(program);
0240             p->setArguments(arguments);
0241             p->start();
0242         } else {
0243             qWarning("Failed to launch the session process: %s is an invalid command",
0244                      qPrintable(m_sessionArgument));
0245         }
0246     }
0247     // start the applications passed to us as command line arguments
0248     if (!m_applicationsToStart.isEmpty()) {
0249         for (const QString &application : std::as_const(m_applicationsToStart)) {
0250             QStringList arguments = KShell::splitArgs(application);
0251             if (arguments.isEmpty()) {
0252                 qWarning("Failed to launch application: %s is an invalid command",
0253                          qPrintable(application));
0254                 continue;
0255             }
0256             QString program = arguments.takeFirst();
0257             // note: this will kill the started process when we exit
0258             // this is going to happen anyway as we are the wayland and X server the app connects to
0259             QProcess *p = new QProcess(this);
0260             p->setProcessChannelMode(QProcess::ForwardedErrorChannel);
0261             p->setProcessEnvironment(processStartupEnvironment());
0262             p->setProgram(program);
0263             p->setArguments(arguments);
0264             p->startDetached();
0265             p->deleteLater();
0266         }
0267     }
0268 }
0269 
0270 XwaylandInterface *ApplicationWayland::xwayland() const
0271 {
0272     return m_xwayland.get();
0273 }
0274 
0275 } // namespace
0276 
0277 int main(int argc, char *argv[])
0278 {
0279     KWin::Application::setupMalloc();
0280     KWin::Application::setupLocalizedString();
0281     KWin::gainRealTime();
0282 
0283     signal(SIGPIPE, SIG_IGN);
0284 
0285     // It's easy to exceed the file descriptor limit because many things are backed using fds
0286     // nowadays, e.g. dmabufs, shm buffers, etc. Bump the RLIMIT_NOFILE limit to handle that.
0287     // Some apps may still use select(), so we reset the limit to its original value in fork().
0288     if (KWin::bumpNofileLimit()) {
0289         pthread_atfork(nullptr, nullptr, KWin::restoreNofileLimit);
0290     }
0291 
0292     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
0293 
0294     // enforce our internal qpa plugin, unfortunately command line switch has precedence
0295     setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true);
0296 
0297     qunsetenv("QT_DEVICE_PIXEL_RATIO");
0298     qputenv("QSG_RENDER_LOOP", "basic");
0299     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0300     KWin::ApplicationWayland a(argc, argv);
0301     a.setupTranslator();
0302     // reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect)
0303     qunsetenv("QT_QPA_PLATFORM");
0304 
0305     KSignalHandler::self()->watchSignal(SIGTERM);
0306     KSignalHandler::self()->watchSignal(SIGINT);
0307     KSignalHandler::self()->watchSignal(SIGHUP);
0308     QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived,
0309                      &a, &QCoreApplication::exit);
0310 
0311     KWin::Application::createAboutData();
0312 
0313     QCommandLineOption xwaylandOption(QStringLiteral("xwayland"),
0314                                       i18n("Start a rootless Xwayland server."));
0315     QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")},
0316                                            i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."),
0317                                            QStringLiteral("socket"));
0318     QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"),
0319                                         i18n("The X11 Display to use in windowed mode on platform X11."),
0320                                         QStringLiteral("display"));
0321     QCommandLineOption waylandDisplayOption(QStringLiteral("wayland-display"),
0322                                             i18n("The Wayland Display to use in windowed mode on platform Wayland."),
0323                                             QStringLiteral("display"));
0324     QCommandLineOption virtualFbOption(QStringLiteral("virtual"), i18n("Render to a virtual framebuffer."));
0325     QCommandLineOption widthOption(QStringLiteral("width"),
0326                                    i18n("The width for windowed mode. Default width is 1024."),
0327                                    QStringLiteral("width"));
0328     widthOption.setDefaultValue(QString::number(1024));
0329     QCommandLineOption heightOption(QStringLiteral("height"),
0330                                     i18n("The height for windowed mode. Default height is 768."),
0331                                     QStringLiteral("height"));
0332     heightOption.setDefaultValue(QString::number(768));
0333 
0334     QCommandLineOption scaleOption(QStringLiteral("scale"),
0335                                    i18n("The scale for windowed mode. Default value is 1."),
0336                                    QStringLiteral("scale"));
0337     scaleOption.setDefaultValue(QString::number(1));
0338 
0339     QCommandLineOption outputCountOption(QStringLiteral("output-count"),
0340                                          i18n("The number of windows to open as outputs in windowed mode. Default value is 1"),
0341                                          QStringLiteral("count"));
0342     outputCountOption.setDefaultValue(QString::number(1));
0343 
0344     QCommandLineOption waylandSocketFdOption(QStringLiteral("wayland-fd"),
0345                                              i18n("Wayland socket to use for incoming connections. This can be combined with --socket to name the socket"),
0346                                              QStringLiteral("wayland-fd"));
0347 
0348     QCommandLineOption xwaylandListenFdOption(QStringLiteral("xwayland-fd"),
0349                                               i18n("XWayland socket to use for Xwayland's incoming connections. This can be set multiple times"),
0350                                               QStringLiteral("xwayland-fds"));
0351 
0352     QCommandLineOption xwaylandDisplayOption(QStringLiteral("xwayland-display"),
0353                                              i18n("Name of the xwayland display that has been pre-set up"),
0354                                              "xwayland-display");
0355 
0356     QCommandLineOption xwaylandXAuthorityOption(QStringLiteral("xwayland-xauthority"),
0357                                                 i18n("Name of the xauthority file "),
0358                                                 "xwayland-xauthority");
0359 
0360     QCommandLineOption replaceOption(QStringLiteral("replace"),
0361                                      i18n("Exits this instance so it can be restarted by kwin_wayland_wrapper."));
0362 
0363     QCommandLineOption drmOption(QStringLiteral("drm"), i18n("Render through drm node."));
0364     QCommandLineOption locale1Option(QStringLiteral("locale1"), i18n("Extract locale information from locale1 rather than the user's configuration"));
0365 
0366     QCommandLineParser parser;
0367     a.setupCommandLine(&parser);
0368     parser.addOption(xwaylandOption);
0369     parser.addOption(waylandSocketOption);
0370     parser.addOption(waylandSocketFdOption);
0371     parser.addOption(xwaylandListenFdOption);
0372     parser.addOption(xwaylandDisplayOption);
0373     parser.addOption(xwaylandXAuthorityOption);
0374     parser.addOption(replaceOption);
0375     parser.addOption(x11DisplayOption);
0376     parser.addOption(waylandDisplayOption);
0377     parser.addOption(virtualFbOption);
0378     parser.addOption(widthOption);
0379     parser.addOption(heightOption);
0380     parser.addOption(scaleOption);
0381     parser.addOption(outputCountOption);
0382     parser.addOption(drmOption);
0383     parser.addOption(locale1Option);
0384 
0385     QCommandLineOption inputMethodOption(QStringLiteral("inputmethod"),
0386                                          i18n("Input method that KWin starts."),
0387                                          QStringLiteral("path/to/imserver"));
0388     parser.addOption(inputMethodOption);
0389 
0390 #if KWIN_BUILD_SCREENLOCKER
0391     QCommandLineOption screenLockerOption(QStringLiteral("lockscreen"),
0392                                           i18n("Starts the session in locked mode."));
0393     parser.addOption(screenLockerOption);
0394 
0395     QCommandLineOption noScreenLockerOption(QStringLiteral("no-lockscreen"),
0396                                             i18n("Starts the session without lock screen support."));
0397     parser.addOption(noScreenLockerOption);
0398 #endif
0399 
0400     QCommandLineOption noGlobalShortcutsOption(QStringLiteral("no-global-shortcuts"),
0401                                                i18n("Starts the session without global shortcuts support."));
0402     parser.addOption(noGlobalShortcutsOption);
0403 
0404 #if KWIN_BUILD_ACTIVITIES
0405     QCommandLineOption noActivitiesOption(QStringLiteral("no-kactivities"),
0406                                           i18n("Disable KActivities integration."));
0407     parser.addOption(noActivitiesOption);
0408 #endif
0409 
0410     QCommandLineOption exitWithSessionOption(QStringLiteral("exit-with-session"),
0411                                              i18n("Exit after the session application, which is started by KWin, closed."),
0412                                              QStringLiteral("/path/to/session"));
0413     parser.addOption(exitWithSessionOption);
0414 
0415     parser.addPositionalArgument(QStringLiteral("applications"),
0416                                  i18n("Applications to start once Wayland and Xwayland server are started"),
0417                                  QStringLiteral("[/path/to/application...]"));
0418 
0419     parser.process(a);
0420     a.processCommandLine(&parser);
0421 
0422 #if KWIN_BUILD_ACTIVITIES
0423     if (parser.isSet(noActivitiesOption)) {
0424         a.setUseKActivities(false);
0425     }
0426 #endif
0427 
0428     if (parser.isSet(replaceOption)) {
0429         QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"), QStringLiteral("/KWin"),
0430                                                           QStringLiteral("org.kde.KWin"), QStringLiteral("replace"));
0431         QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
0432         return 0;
0433     }
0434 
0435     if (parser.isSet(exitWithSessionOption)) {
0436         a.setSessionArgument(parser.value(exitWithSessionOption));
0437     }
0438 
0439     enum class BackendType {
0440         Kms,
0441         X11,
0442         Wayland,
0443         Virtual,
0444     };
0445 
0446     BackendType backendType;
0447     QString pluginName;
0448     QSize initialWindowSize;
0449     int outputCount = 1;
0450     qreal outputScale = 1;
0451 
0452     // Decide what backend to use.
0453     if (parser.isSet(drmOption)) {
0454         backendType = BackendType::Kms;
0455     } else if (parser.isSet(x11DisplayOption)) {
0456         backendType = BackendType::X11;
0457     } else if (parser.isSet(waylandDisplayOption)) {
0458         backendType = BackendType::Wayland;
0459     } else if (parser.isSet(virtualFbOption)) {
0460         backendType = BackendType::Virtual;
0461     } else {
0462         if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) {
0463             qWarning("No backend specified, automatically choosing Wayland because WAYLAND_DISPLAY is set");
0464             backendType = BackendType::Wayland;
0465         } else if (qEnvironmentVariableIsSet("DISPLAY")) {
0466             qWarning("No backend specified, automatically choosing X11 because DISPLAY is set");
0467             backendType = BackendType::X11;
0468         } else {
0469             qWarning("No backend specified, automatically choosing drm");
0470             backendType = BackendType::Kms;
0471         }
0472     }
0473 
0474     if (parser.isSet(locale1Option)) {
0475         a.setFollowLocale1(true);
0476     }
0477 
0478     bool ok = false;
0479     const int width = parser.value(widthOption).toInt(&ok);
0480     if (!ok) {
0481         std::cerr << "FATAL ERROR incorrect value for width" << std::endl;
0482         return 1;
0483     }
0484     const int height = parser.value(heightOption).toInt(&ok);
0485     if (!ok) {
0486         std::cerr << "FATAL ERROR incorrect value for height" << std::endl;
0487         return 1;
0488     }
0489     const qreal scale = parser.value(scaleOption).toDouble(&ok);
0490     if (!ok || scale <= 0) {
0491         std::cerr << "FATAL ERROR incorrect value for scale" << std::endl;
0492         return 1;
0493     }
0494 
0495     outputScale = scale;
0496     initialWindowSize = QSize(width, height);
0497 
0498     const int count = parser.value(outputCountOption).toInt(&ok);
0499     if (ok) {
0500         outputCount = std::max(1, count);
0501     }
0502 
0503     // TODO: create backend without having the server running
0504     KWin::WaylandServer *server = KWin::WaylandServer::create(&a);
0505 
0506     KWin::WaylandServer::InitializationFlags flags;
0507 #if KWIN_BUILD_SCREENLOCKER
0508     if (parser.isSet(screenLockerOption)) {
0509         flags = KWin::WaylandServer::InitializationFlag::LockScreen;
0510     } else if (parser.isSet(noScreenLockerOption)) {
0511         flags = KWin::WaylandServer::InitializationFlag::NoLockScreenIntegration;
0512     }
0513 #endif
0514     if (parser.isSet(noGlobalShortcutsOption)) {
0515         flags |= KWin::WaylandServer::InitializationFlag::NoGlobalShortcuts;
0516     }
0517 
0518     const QString socketName = parser.value(waylandSocketOption);
0519     if (parser.isSet(waylandSocketFdOption)) {
0520         bool ok;
0521         int fd = parser.value(waylandSocketFdOption).toInt(&ok);
0522         if (ok) {
0523             // make sure we don't leak this FD to children
0524             fcntl(fd, F_SETFD, FD_CLOEXEC);
0525             server->display()->addSocketFileDescriptor(fd, socketName);
0526         } else {
0527             std::cerr << "FATAL ERROR: could not parse socket FD" << std::endl;
0528             return 1;
0529         }
0530     } else {
0531         // socketName empty is fine here, addSocketName will automatically pick one
0532         if (!server->display()->addSocketName(socketName)) {
0533             std::cerr << "FATAL ERROR: could not add wayland socket " << qPrintable(socketName) << std::endl;
0534             return 1;
0535         }
0536     }
0537 
0538     if (!server->init(flags)) {
0539         std::cerr << "FATAL ERROR: could not create Wayland server" << std::endl;
0540         return 1;
0541     }
0542 
0543     switch (backendType) {
0544     case BackendType::Kms:
0545         a.setSession(KWin::Session::create());
0546         if (!a.session()) {
0547             std::cerr << "FATAl ERROR: could not acquire a session" << std::endl;
0548             return 1;
0549         }
0550         a.setOutputBackend(std::make_unique<KWin::DrmBackend>(a.session()));
0551         break;
0552     case BackendType::Virtual: {
0553         auto outputBackend = std::make_unique<KWin::VirtualBackend>();
0554         for (int i = 0; i < outputCount; ++i) {
0555             outputBackend->addOutput(initialWindowSize, outputScale);
0556         }
0557         a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
0558         a.setOutputBackend(std::move(outputBackend));
0559         break;
0560     }
0561     case BackendType::X11: {
0562         QString display = parser.value(x11DisplayOption);
0563         if (display.isEmpty()) {
0564             display = qgetenv("DISPLAY");
0565         }
0566         a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
0567         a.setOutputBackend(std::make_unique<KWin::X11WindowedBackend>(KWin::X11WindowedBackendOptions{
0568             .display = display,
0569             .outputCount = outputCount,
0570             .outputScale = outputScale,
0571             .outputSize = initialWindowSize,
0572         }));
0573         break;
0574     }
0575     case BackendType::Wayland: {
0576         QString socketName = parser.value(waylandDisplayOption);
0577         if (socketName.isEmpty()) {
0578             socketName = qgetenv("WAYLAND_DISPLAY");
0579         }
0580         a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
0581         a.setOutputBackend(std::make_unique<KWin::Wayland::WaylandBackend>(KWin::Wayland::WaylandBackendOptions{
0582             .socketName = socketName,
0583             .outputCount = outputCount,
0584             .outputScale = outputScale,
0585             .outputSize = initialWindowSize,
0586         }));
0587         break;
0588     }
0589     }
0590 
0591     QObject::connect(&a, &KWin::Application::workspaceCreated, server, &KWin::WaylandServer::initWorkspace);
0592     if (!server->socketName().isEmpty()) {
0593         environment.insert(QStringLiteral("WAYLAND_DISPLAY"), server->socketName());
0594         qputenv("WAYLAND_DISPLAY", server->socketName().toUtf8());
0595     }
0596     a.setProcessStartupEnvironment(environment);
0597 
0598     if (parser.isSet(xwaylandOption)) {
0599         a.setStartXwayland(true);
0600 
0601         if (parser.isSet(xwaylandListenFdOption)) {
0602             const QStringList fdStrings = parser.values(xwaylandListenFdOption);
0603             for (const QString &fdString : fdStrings) {
0604                 bool ok;
0605                 int fd = fdString.toInt(&ok);
0606                 if (ok) {
0607                     // make sure we don't leak this FD to children
0608                     fcntl(fd, F_SETFD, FD_CLOEXEC);
0609                     a.addXwaylandSocketFileDescriptor(fd);
0610                 }
0611             }
0612             if (parser.isSet(xwaylandDisplayOption)) {
0613                 a.setXwaylandDisplay(parser.value(xwaylandDisplayOption));
0614             } else {
0615                 std::cerr << "Using xwayland-fd without xwayland-display is undefined" << std::endl;
0616                 return 1;
0617             }
0618             if (parser.isSet(xwaylandXAuthorityOption)) {
0619                 a.setXwaylandXauthority(parser.value(xwaylandXAuthorityOption));
0620             }
0621         }
0622     }
0623 
0624     a.setApplicationsToStart(parser.positionalArguments());
0625     a.setInputMethodServerToStart(parser.value(inputMethodOption));
0626     a.start();
0627 
0628     return a.exec();
0629 }