d4676cceb3
This change removes async bits from the startup code in the wayland backend to make it simpler. The main motivation behind this change is to refactor the wayland backend so OutputBackend::isReady() can be removed.
627 lines
24 KiB
C++
627 lines
24 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
#include "main_wayland.h"
|
|
|
|
#include <config-kwin.h>
|
|
|
|
#include "backends/drm/drm_backend.h"
|
|
#include "backends/virtual/virtual_backend.h"
|
|
#include "backends/wayland/wayland_backend.h"
|
|
#include "backends/x11/windowed/x11_windowed_backend.h"
|
|
#include "composite.h"
|
|
#include "core/outputbackend.h"
|
|
#include "core/session.h"
|
|
#include "effects.h"
|
|
#include "inputmethod.h"
|
|
#include "tabletmodemanager.h"
|
|
#include "utils/realtime.h"
|
|
#include "wayland/display.h"
|
|
#include "wayland/seat_interface.h"
|
|
#include "wayland_server.h"
|
|
#include "workspace.h"
|
|
#include "xwayland/xwayland.h"
|
|
#include "xwayland/xwaylandlauncher.h"
|
|
|
|
// KDE
|
|
#include <KCrash>
|
|
#include <KDesktopFile>
|
|
#include <KLocalizedString>
|
|
#include <KShell>
|
|
#include <KSignalHandler>
|
|
|
|
// Qt
|
|
#include <QCommandLineParser>
|
|
#include <QDBusInterface>
|
|
#include <QDebug>
|
|
#include <QFileInfo>
|
|
#include <QProcess>
|
|
#include <QWindow>
|
|
#include <qplatformdefs.h>
|
|
|
|
#include <sched.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
|
|
Q_IMPORT_PLUGIN(KWinIntegrationPlugin)
|
|
Q_IMPORT_PLUGIN(KGlobalAccelImpl)
|
|
Q_IMPORT_PLUGIN(KWindowSystemKWinPlugin)
|
|
Q_IMPORT_PLUGIN(KWinIdleTimePoller)
|
|
#if PipeWire_FOUND
|
|
Q_IMPORT_PLUGIN(ScreencastManagerFactory)
|
|
#endif
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
static rlimit originalNofileLimit = {
|
|
.rlim_cur = 0,
|
|
.rlim_max = 0,
|
|
};
|
|
|
|
static bool bumpNofileLimit()
|
|
{
|
|
if (getrlimit(RLIMIT_NOFILE, &originalNofileLimit) == -1) {
|
|
std::cerr << "Failed to bump RLIMIT_NOFILE limit, getrlimit() failed: " << strerror(errno) << std::endl;
|
|
return false;
|
|
}
|
|
|
|
rlimit limit = originalNofileLimit;
|
|
limit.rlim_cur = limit.rlim_max;
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
|
|
std::cerr << "Failed to bump RLIMIT_NOFILE limit, setrlimit() failed: " << strerror(errno) << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void restoreNofileLimit()
|
|
{
|
|
if (setrlimit(RLIMIT_NOFILE, &originalNofileLimit) == -1) {
|
|
std::cerr << "Failed to restore RLIMIT_NOFILE limit, legacy apps might be broken" << std::endl;
|
|
}
|
|
}
|
|
|
|
void disableDrKonqi()
|
|
{
|
|
KCrash::setDrKonqiEnabled(false);
|
|
}
|
|
// run immediately, before Q_CORE_STARTUP functions
|
|
// that would enable drkonqi
|
|
Q_CONSTRUCTOR_FUNCTION(disableDrKonqi)
|
|
|
|
//************************************
|
|
// ApplicationWayland
|
|
//************************************
|
|
|
|
ApplicationWayland::ApplicationWayland(int &argc, char **argv)
|
|
: Application(OperationModeWaylandOnly, argc, argv)
|
|
{
|
|
}
|
|
|
|
ApplicationWayland::~ApplicationWayland()
|
|
{
|
|
setTerminating();
|
|
if (!waylandServer()) {
|
|
return;
|
|
}
|
|
|
|
destroyPlugins();
|
|
|
|
// need to unload all effects prior to destroying X connection as they might do X calls
|
|
if (effects) {
|
|
static_cast<EffectsHandlerImpl *>(effects)->unloadAllEffects();
|
|
}
|
|
m_xwayland.reset();
|
|
destroyColorManager();
|
|
destroyWorkspace();
|
|
|
|
destroyInputMethod();
|
|
destroyCompositor();
|
|
destroyInput();
|
|
}
|
|
|
|
void ApplicationWayland::performStartup()
|
|
{
|
|
if (m_startXWayland) {
|
|
setOperationMode(OperationModeXwayland);
|
|
setXwaylandScale(config()->group("Xwayland").readEntry("Scale", 1.0));
|
|
}
|
|
// first load options - done internally by a different thread
|
|
createOptions();
|
|
|
|
if (!outputBackend()->initialize()) {
|
|
std::exit(1);
|
|
}
|
|
|
|
createInput();
|
|
createInputMethod();
|
|
createTabletModeManager();
|
|
|
|
WaylandCompositor::create();
|
|
|
|
connect(Compositor::self(), &Compositor::sceneCreated, outputBackend(), &OutputBackend::sceneInitialized);
|
|
connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithScene);
|
|
}
|
|
|
|
void ApplicationWayland::continueStartupWithScene()
|
|
{
|
|
disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithScene);
|
|
|
|
// Note that we start accepting client connections after creating the Workspace.
|
|
createWorkspace();
|
|
createColorManager();
|
|
createPlugins();
|
|
|
|
if (!waylandServer()->start()) {
|
|
qFatal("Failed to initialze the Wayland server, exiting now");
|
|
}
|
|
|
|
if (operationMode() == OperationModeWaylandOnly) {
|
|
finalizeStartup();
|
|
return;
|
|
}
|
|
|
|
m_xwayland = std::make_unique<Xwl::Xwayland>(this);
|
|
m_xwayland->xwaylandLauncher()->setListenFDs(m_xwaylandListenFds);
|
|
m_xwayland->xwaylandLauncher()->setDisplayName(m_xwaylandDisplay);
|
|
m_xwayland->xwaylandLauncher()->setXauthority(m_xwaylandXauthority);
|
|
connect(m_xwayland.get(), &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup);
|
|
connect(m_xwayland.get(), &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup);
|
|
m_xwayland->start();
|
|
}
|
|
|
|
void ApplicationWayland::finalizeStartup()
|
|
{
|
|
if (m_xwayland) {
|
|
disconnect(m_xwayland.get(), &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup);
|
|
disconnect(m_xwayland.get(), &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup);
|
|
}
|
|
startSession();
|
|
notifyStarted();
|
|
}
|
|
|
|
void ApplicationWayland::refreshSettings(const KConfigGroup &group, const QByteArrayList &names)
|
|
{
|
|
if (group.name() == "Wayland" && names.contains("InputMethod")) {
|
|
KDesktopFile file(group.readPathEntry("InputMethod", QString()));
|
|
kwinApp()->inputMethod()->setInputMethodCommand(file.desktopGroup().readEntry("Exec", QString()));
|
|
}
|
|
|
|
if (m_startXWayland && group.name() == "Xwayland" && names.contains("Scale")) {
|
|
setXwaylandScale(group.readEntry("Scale", 1.0));
|
|
}
|
|
|
|
if (group.name() == "Wayland" && names.contains("EnablePrimarySelection")) {
|
|
waylandServer()->seat()->setPrimarySelectionEnabled(group.readEntry("EnablePrimarySelection", true));
|
|
}
|
|
}
|
|
|
|
void ApplicationWayland::startSession()
|
|
{
|
|
KSharedConfig::Ptr kwinSettings = kwinApp()->config();
|
|
m_settingsWatcher = KConfigWatcher::create(kwinSettings);
|
|
connect(m_settingsWatcher.data(), &KConfigWatcher::configChanged, this, &ApplicationWayland::refreshSettings);
|
|
|
|
if (!m_inputMethodServerToStart.isEmpty()) {
|
|
kwinApp()->inputMethod()->setInputMethodCommand(m_inputMethodServerToStart);
|
|
} else {
|
|
refreshSettings(kwinSettings->group("Wayland"), {"InputMethod"});
|
|
}
|
|
|
|
// start session
|
|
if (!m_sessionArgument.isEmpty()) {
|
|
QStringList arguments = KShell::splitArgs(m_sessionArgument);
|
|
if (!arguments.isEmpty()) {
|
|
QString program = arguments.takeFirst();
|
|
QProcess *p = new QProcess(this);
|
|
p->setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
|
p->setProcessEnvironment(processStartupEnvironment());
|
|
connect(p, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, [p](int code, QProcess::ExitStatus status) {
|
|
p->deleteLater();
|
|
if (status == QProcess::CrashExit) {
|
|
qWarning() << "Session process has crashed";
|
|
QCoreApplication::exit(-1);
|
|
return;
|
|
}
|
|
|
|
if (code) {
|
|
qWarning() << "Session process exited with code" << code;
|
|
}
|
|
|
|
QCoreApplication::exit(code);
|
|
});
|
|
p->setProgram(program);
|
|
p->setArguments(arguments);
|
|
p->start();
|
|
} else {
|
|
qWarning("Failed to launch the session process: %s is an invalid command",
|
|
qPrintable(m_sessionArgument));
|
|
}
|
|
}
|
|
// start the applications passed to us as command line arguments
|
|
if (!m_applicationsToStart.isEmpty()) {
|
|
for (const QString &application : std::as_const(m_applicationsToStart)) {
|
|
QStringList arguments = KShell::splitArgs(application);
|
|
if (arguments.isEmpty()) {
|
|
qWarning("Failed to launch application: %s is an invalid command",
|
|
qPrintable(application));
|
|
continue;
|
|
}
|
|
QString program = arguments.takeFirst();
|
|
// note: this will kill the started process when we exit
|
|
// this is going to happen anyway as we are the wayland and X server the app connects to
|
|
QProcess *p = new QProcess(this);
|
|
p->setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
|
p->setProcessEnvironment(processStartupEnvironment());
|
|
p->setProgram(program);
|
|
p->setArguments(arguments);
|
|
p->startDetached();
|
|
p->deleteLater();
|
|
}
|
|
}
|
|
}
|
|
|
|
XwaylandInterface *ApplicationWayland::xwayland() const
|
|
{
|
|
return m_xwayland.get();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
KWin::Application::setupMalloc();
|
|
KWin::Application::setupLocalizedString();
|
|
KWin::gainRealTime();
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
// It's easy to exceed the file descriptor limit because many things are backed using fds
|
|
// nowadays, e.g. dmabufs, shm buffers, etc. Bump the RLIMIT_NOFILE limit to handle that.
|
|
// Some apps may still use select(), so we reset the limit to its original value in fork().
|
|
if (KWin::bumpNofileLimit()) {
|
|
pthread_atfork(nullptr, nullptr, KWin::restoreNofileLimit);
|
|
}
|
|
|
|
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
|
|
|
// enforce our internal qpa plugin, unfortunately command line switch has precedence
|
|
setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true);
|
|
|
|
qunsetenv("QT_DEVICE_PIXEL_RATIO");
|
|
qputenv("QSG_RENDER_LOOP", "basic");
|
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
|
KWin::ApplicationWayland a(argc, argv);
|
|
a.setupTranslator();
|
|
// reset QT_QPA_PLATFORM so we don't propagate it to our children (e.g. apps launched from the overview effect)
|
|
qunsetenv("QT_QPA_PLATFORM");
|
|
|
|
KSignalHandler::self()->watchSignal(SIGTERM);
|
|
KSignalHandler::self()->watchSignal(SIGINT);
|
|
KSignalHandler::self()->watchSignal(SIGHUP);
|
|
QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived,
|
|
&a, &QCoreApplication::exit);
|
|
|
|
KWin::Application::createAboutData();
|
|
|
|
QCommandLineOption xwaylandOption(QStringLiteral("xwayland"),
|
|
i18n("Start a rootless Xwayland server."));
|
|
QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")},
|
|
i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."),
|
|
QStringLiteral("socket"));
|
|
QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"),
|
|
i18n("The X11 Display to use in windowed mode on platform X11."),
|
|
QStringLiteral("display"));
|
|
QCommandLineOption waylandDisplayOption(QStringLiteral("wayland-display"),
|
|
i18n("The Wayland Display to use in windowed mode on platform Wayland."),
|
|
QStringLiteral("display"));
|
|
QCommandLineOption virtualFbOption(QStringLiteral("virtual"), i18n("Render to a virtual framebuffer."));
|
|
QCommandLineOption widthOption(QStringLiteral("width"),
|
|
i18n("The width for windowed mode. Default width is 1024."),
|
|
QStringLiteral("width"));
|
|
widthOption.setDefaultValue(QString::number(1024));
|
|
QCommandLineOption heightOption(QStringLiteral("height"),
|
|
i18n("The height for windowed mode. Default height is 768."),
|
|
QStringLiteral("height"));
|
|
heightOption.setDefaultValue(QString::number(768));
|
|
|
|
QCommandLineOption scaleOption(QStringLiteral("scale"),
|
|
i18n("The scale for windowed mode. Default value is 1."),
|
|
QStringLiteral("scale"));
|
|
scaleOption.setDefaultValue(QString::number(1));
|
|
|
|
QCommandLineOption outputCountOption(QStringLiteral("output-count"),
|
|
i18n("The number of windows to open as outputs in windowed mode. Default value is 1"),
|
|
QStringLiteral("count"));
|
|
outputCountOption.setDefaultValue(QString::number(1));
|
|
|
|
QCommandLineOption waylandSocketFdOption(QStringLiteral("wayland-fd"),
|
|
i18n("Wayland socket to use for incoming connections. This can be combined with --socket to name the socket"),
|
|
QStringLiteral("wayland-fd"));
|
|
|
|
QCommandLineOption xwaylandListenFdOption(QStringLiteral("xwayland-fd"),
|
|
i18n("XWayland socket to use for Xwayland's incoming connections. This can be set multiple times"),
|
|
QStringLiteral("xwayland-fds"));
|
|
|
|
QCommandLineOption xwaylandDisplayOption(QStringLiteral("xwayland-display"),
|
|
i18n("Name of the xwayland display that has been pre-set up"),
|
|
"xwayland-display");
|
|
|
|
QCommandLineOption xwaylandXAuthorityOption(QStringLiteral("xwayland-xauthority"),
|
|
i18n("Name of the xauthority file "),
|
|
"xwayland-xauthority");
|
|
|
|
QCommandLineOption replaceOption(QStringLiteral("replace"),
|
|
i18n("Exits this instance so it can be restarted by kwin_wayland_wrapper."));
|
|
|
|
QCommandLineOption drmOption(QStringLiteral("drm"), i18n("Render through drm node."));
|
|
QCommandLineOption locale1Option(QStringLiteral("locale1"), i18n("Extract locale information from locale1 rather than the user's configuration"));
|
|
|
|
QCommandLineParser parser;
|
|
a.setupCommandLine(&parser);
|
|
parser.addOption(xwaylandOption);
|
|
parser.addOption(waylandSocketOption);
|
|
parser.addOption(waylandSocketFdOption);
|
|
parser.addOption(xwaylandListenFdOption);
|
|
parser.addOption(xwaylandDisplayOption);
|
|
parser.addOption(xwaylandXAuthorityOption);
|
|
parser.addOption(replaceOption);
|
|
parser.addOption(x11DisplayOption);
|
|
parser.addOption(waylandDisplayOption);
|
|
parser.addOption(virtualFbOption);
|
|
parser.addOption(widthOption);
|
|
parser.addOption(heightOption);
|
|
parser.addOption(scaleOption);
|
|
parser.addOption(outputCountOption);
|
|
parser.addOption(drmOption);
|
|
parser.addOption(locale1Option);
|
|
|
|
QCommandLineOption inputMethodOption(QStringLiteral("inputmethod"),
|
|
i18n("Input method that KWin starts."),
|
|
QStringLiteral("path/to/imserver"));
|
|
parser.addOption(inputMethodOption);
|
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
|
QCommandLineOption screenLockerOption(QStringLiteral("lockscreen"),
|
|
i18n("Starts the session in locked mode."));
|
|
parser.addOption(screenLockerOption);
|
|
|
|
QCommandLineOption noScreenLockerOption(QStringLiteral("no-lockscreen"),
|
|
i18n("Starts the session without lock screen support."));
|
|
parser.addOption(noScreenLockerOption);
|
|
#endif
|
|
|
|
QCommandLineOption noGlobalShortcutsOption(QStringLiteral("no-global-shortcuts"),
|
|
i18n("Starts the session without global shortcuts support."));
|
|
parser.addOption(noGlobalShortcutsOption);
|
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
QCommandLineOption noActivitiesOption(QStringLiteral("no-kactivities"),
|
|
i18n("Disable KActivities integration."));
|
|
parser.addOption(noActivitiesOption);
|
|
#endif
|
|
|
|
QCommandLineOption exitWithSessionOption(QStringLiteral("exit-with-session"),
|
|
i18n("Exit after the session application, which is started by KWin, closed."),
|
|
QStringLiteral("/path/to/session"));
|
|
parser.addOption(exitWithSessionOption);
|
|
|
|
parser.addPositionalArgument(QStringLiteral("applications"),
|
|
i18n("Applications to start once Wayland and Xwayland server are started"),
|
|
QStringLiteral("[/path/to/application...]"));
|
|
|
|
parser.process(a);
|
|
a.processCommandLine(&parser);
|
|
|
|
#if KWIN_BUILD_ACTIVITIES
|
|
if (parser.isSet(noActivitiesOption)) {
|
|
a.setUseKActivities(false);
|
|
}
|
|
#endif
|
|
|
|
if (parser.isSet(replaceOption)) {
|
|
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"), QStringLiteral("/KWin"),
|
|
QStringLiteral("org.kde.KWin"), QStringLiteral("replace"));
|
|
QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
|
|
return 0;
|
|
}
|
|
|
|
if (parser.isSet(exitWithSessionOption)) {
|
|
a.setSessionArgument(parser.value(exitWithSessionOption));
|
|
}
|
|
|
|
enum class BackendType {
|
|
Kms,
|
|
X11,
|
|
Wayland,
|
|
Virtual,
|
|
};
|
|
|
|
BackendType backendType;
|
|
QString pluginName;
|
|
QSize initialWindowSize;
|
|
QByteArray deviceIdentifier;
|
|
int outputCount = 1;
|
|
qreal outputScale = 1;
|
|
|
|
// Decide what backend to use.
|
|
if (parser.isSet(drmOption)) {
|
|
backendType = BackendType::Kms;
|
|
} else if (parser.isSet(x11DisplayOption)) {
|
|
backendType = BackendType::X11;
|
|
deviceIdentifier = parser.value(x11DisplayOption).toUtf8();
|
|
} else if (parser.isSet(waylandDisplayOption)) {
|
|
backendType = BackendType::Wayland;
|
|
deviceIdentifier = parser.value(waylandDisplayOption).toUtf8();
|
|
} else if (parser.isSet(virtualFbOption)) {
|
|
backendType = BackendType::Virtual;
|
|
} else {
|
|
if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) {
|
|
qWarning("No backend specified, automatically choosing Wayland because WAYLAND_DISPLAY is set");
|
|
backendType = BackendType::Wayland;
|
|
} else if (qEnvironmentVariableIsSet("DISPLAY")) {
|
|
qWarning("No backend specified, automatically choosing X11 because DISPLAY is set");
|
|
backendType = BackendType::X11;
|
|
} else {
|
|
qWarning("No backend specified, automatically choosing drm");
|
|
backendType = BackendType::Kms;
|
|
}
|
|
}
|
|
|
|
if (parser.isSet(locale1Option)) {
|
|
a.setFollowLocale1(true);
|
|
}
|
|
|
|
bool ok = false;
|
|
const int width = parser.value(widthOption).toInt(&ok);
|
|
if (!ok) {
|
|
std::cerr << "FATAL ERROR incorrect value for width" << std::endl;
|
|
return 1;
|
|
}
|
|
const int height = parser.value(heightOption).toInt(&ok);
|
|
if (!ok) {
|
|
std::cerr << "FATAL ERROR incorrect value for height" << std::endl;
|
|
return 1;
|
|
}
|
|
const qreal scale = parser.value(scaleOption).toDouble(&ok);
|
|
if (!ok || scale <= 0) {
|
|
std::cerr << "FATAL ERROR incorrect value for scale" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
outputScale = scale;
|
|
initialWindowSize = QSize(width, height);
|
|
|
|
const int count = parser.value(outputCountOption).toInt(&ok);
|
|
if (ok) {
|
|
outputCount = std::max(1, count);
|
|
}
|
|
|
|
// TODO: create backend without having the server running
|
|
KWin::WaylandServer *server = KWin::WaylandServer::create(&a);
|
|
|
|
KWin::WaylandServer::InitializationFlags flags;
|
|
#if KWIN_BUILD_SCREENLOCKER
|
|
if (parser.isSet(screenLockerOption)) {
|
|
flags = KWin::WaylandServer::InitializationFlag::LockScreen;
|
|
} else if (parser.isSet(noScreenLockerOption)) {
|
|
flags = KWin::WaylandServer::InitializationFlag::NoLockScreenIntegration;
|
|
}
|
|
#endif
|
|
if (parser.isSet(noGlobalShortcutsOption)) {
|
|
flags |= KWin::WaylandServer::InitializationFlag::NoGlobalShortcuts;
|
|
}
|
|
|
|
const QString socketName = parser.value(waylandSocketOption);
|
|
if (parser.isSet(waylandSocketFdOption)) {
|
|
bool ok;
|
|
int fd = parser.value(waylandSocketFdOption).toInt(&ok);
|
|
if (ok) {
|
|
// make sure we don't leak this FD to children
|
|
fcntl(fd, F_SETFD, O_CLOEXEC);
|
|
server->display()->addSocketFileDescriptor(fd, socketName);
|
|
} else {
|
|
std::cerr << "FATAL ERROR: could not parse socket FD" << std::endl;
|
|
return 1;
|
|
}
|
|
} else {
|
|
// socketName empty is fine here, addSocketName will automatically pick one
|
|
if (!server->display()->addSocketName(socketName)) {
|
|
std::cerr << "FATAL ERROR: could not add wayland socket " << qPrintable(socketName) << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!server->init(flags)) {
|
|
std::cerr << "FATAL ERROR: could not create Wayland server" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
switch (backendType) {
|
|
case BackendType::Kms:
|
|
a.setSession(KWin::Session::create());
|
|
if (!a.session()) {
|
|
std::cerr << "FATAl ERROR: could not acquire a session" << std::endl;
|
|
return 1;
|
|
}
|
|
a.setOutputBackend(std::make_unique<KWin::DrmBackend>(a.session()));
|
|
break;
|
|
case BackendType::Virtual:
|
|
a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
|
|
a.setOutputBackend(std::make_unique<KWin::VirtualBackend>());
|
|
break;
|
|
case BackendType::X11:
|
|
a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
|
|
a.setOutputBackend(std::make_unique<KWin::X11WindowedBackend>());
|
|
if (deviceIdentifier.isEmpty()) {
|
|
a.outputBackend()->setDeviceIdentifier(qgetenv("DISPLAY"));
|
|
} else {
|
|
a.outputBackend()->setDeviceIdentifier(deviceIdentifier);
|
|
}
|
|
break;
|
|
case BackendType::Wayland:
|
|
a.setSession(KWin::Session::create(KWin::Session::Type::Noop));
|
|
a.setOutputBackend(std::make_unique<KWin::Wayland::WaylandBackend>());
|
|
if (deviceIdentifier.isEmpty()) {
|
|
a.outputBackend()->setDeviceIdentifier(qgetenv("WAYLAND_DISPLAY"));
|
|
} else {
|
|
a.outputBackend()->setDeviceIdentifier(deviceIdentifier);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (initialWindowSize.isValid()) {
|
|
a.outputBackend()->setInitialWindowSize(initialWindowSize);
|
|
}
|
|
a.outputBackend()->setInitialOutputScale(outputScale);
|
|
a.outputBackend()->setInitialOutputCount(outputCount);
|
|
|
|
QObject::connect(&a, &KWin::Application::workspaceCreated, server, &KWin::WaylandServer::initWorkspace);
|
|
if (!server->socketName().isEmpty()) {
|
|
environment.insert(QStringLiteral("WAYLAND_DISPLAY"), server->socketName());
|
|
qputenv("WAYLAND_DISPLAY", server->socketName().toUtf8());
|
|
}
|
|
a.setProcessStartupEnvironment(environment);
|
|
|
|
if (parser.isSet(xwaylandOption)) {
|
|
a.setStartXwayland(true);
|
|
|
|
if (parser.isSet(xwaylandListenFdOption)) {
|
|
const QStringList fdStrings = parser.values(xwaylandListenFdOption);
|
|
for (const QString &fdString : fdStrings) {
|
|
bool ok;
|
|
int fd = fdString.toInt(&ok);
|
|
if (ok) {
|
|
// make sure we don't leak this FD to children
|
|
fcntl(fd, F_SETFD, O_CLOEXEC);
|
|
a.addXwaylandSocketFileDescriptor(fd);
|
|
}
|
|
}
|
|
if (parser.isSet(xwaylandDisplayOption)) {
|
|
a.setXwaylandDisplay(parser.value(xwaylandDisplayOption));
|
|
} else {
|
|
std::cerr << "Using xwayland-fd without xwayland-display is undefined" << std::endl;
|
|
return 1;
|
|
}
|
|
if (parser.isSet(xwaylandXAuthorityOption)) {
|
|
a.setXwaylandXauthority(parser.value(xwaylandXAuthorityOption));
|
|
}
|
|
}
|
|
}
|
|
|
|
a.setApplicationsToStart(parser.positionalArguments());
|
|
a.setInputMethodServerToStart(parser.value(inputMethodOption));
|
|
a.start();
|
|
|
|
return a.exec();
|
|
}
|