summaryrefslogtreecommitdiff
path: root/user
diff options
context:
space:
mode:
Diffstat (limited to 'user')
-rw-r--r--user/sddm/APKBUILD33
-rw-r--r--user/sddm/allocate-vt.patch220
-rw-r--r--user/sddm/authed.patch32
-rw-r--r--user/sddm/autologin-type.patch31
-rw-r--r--user/sddm/cleanup-xorg.patch63
-rw-r--r--user/sddm/qt515.patch199
-rw-r--r--user/sddm/retry.patch115
-rw-r--r--user/sddm/revert-fedora.patch74
-rw-r--r--user/sddm/rootless-xorg.patch2012
-rw-r--r--user/sddm/session-env.patch122
-rw-r--r--user/sddm/utmpx.patch8
-rw-r--r--user/sddm/xephyr.patch171
12 files changed, 3071 insertions, 9 deletions
diff --git a/user/sddm/APKBUILD b/user/sddm/APKBUILD
index 78ca0f8d5..00071648c 100644
--- a/user/sddm/APKBUILD
+++ b/user/sddm/APKBUILD
@@ -1,8 +1,8 @@
# Contributor: A. Wilcox <awilfox@adelielinux.org>
# Maintainer: A. Wilcox <awilfox@adelielinux.org>
pkgname=sddm
-pkgver=0.18.1
-pkgrel=3
+pkgver=0.19.0
+pkgrel=0
pkgdesc="Simple Desktop Display Manager"
url="https://github.com/sddm/sddm/"
pkgusers="sddm"
@@ -16,10 +16,21 @@ makedepends="cmake extra-cmake-modules qt5-qtbase-dev libxcb-dev upower-dev
subpackages="$pkgname-lang $pkgname-openrc"
install="sddm.post-install"
langdir="/usr/share/sddm/translations"
+# Patch files are in order of needed application.
source="https://github.com/sddm/sddm/releases/download/v$pkgver/sddm-$pkgver.tar.xz
pam-path-fix.patch
- sddm.initd
utmpx.patch
+ revert-fedora.patch
+ xephyr.patch
+ retry.patch
+ cleanup-xorg.patch
+ authed.patch
+ allocate-vt.patch
+ autologin-type.patch
+ session-env.patch
+ rootless-xorg.patch
+ qt515.patch
+ sddm.initd
"
build() {
@@ -56,7 +67,17 @@ openrc() {
depends="sddm xorg-server"
}
-sha512sums="ff0637600cda2f4da1f643f047f8ee822bd9651ae4ccbb614b9804175c97360ada7af93e07a7b63832f014ef6e7d1b5380ab2b8959f8024ea520fa5ff17efd60 sddm-0.18.1.tar.xz
+sha512sums="0a40816bc105a1e930fec2d65fabff0ae7e27c641235d90e41f6fbaa86af4bb774a9e30f7548ce2c6c791e6d4f8195b02afddedca60a9e7c77447702e728edc3 sddm-0.19.0.tar.xz
f0b4eb7ef0581701157f9decc637629156f36f6711b9a4bae517f94d7a1df614c81bbd891c918f07ac50e2a3d1519c43ccb9eefd80282c95dd79eca0e8d90904 pam-path-fix.patch
-d603934552bad47edda458d7a4df2310e98bde74bdb3bf8588f5171b2a5d68814192b8dc8f5599b35402f9a747d519d985d4976e7aa50dabed445f99a112594c sddm.initd
-c42d8b3edbc0ae7e3d5ea7bb0080c5c50e0569f0ea947e1ba17bc794c8c0d67a214e62aad7eba0a51791c44b29a3017692bbe738250c63cb2219891bb1313422 utmpx.patch"
+b61c02e9d75c03c5a04a44bf53bf1d7df3afe6214b2c7f6da8a2b3a78c859443fb2af6ff748a9d369707e6eaab8c0e5f9872c35e52e602836795b7099330ba00 utmpx.patch
+51a0b6d427da8ea8dfbeb043ae4eb47f6c55645d03ed3a88e7583c216c52916863d8184faa6402f348e6ca704098b180079f9c6d4faaca1855bad58f9cf5f531 revert-fedora.patch
+65fb8d772ca149f3616cb9d106eebf246b65bc8c0d2e328bd361dcc2664afb6ae467f698ac656653c5092f3b2b5f14b2980f9f1f3929839bb0674c5c801b36a1 xephyr.patch
+d47123e097e8ea918ba112332272dbc94a3f06a76dc01080fd80f5f945320e0647d8f84d4232d32650c9705e2228c054748ce7207492a4c7e6039655668a0440 retry.patch
+5dd057acc054f905ad0b9b0924eeb792b417cfe9d506f1d138266b9dc38c229b1b32d0c2443e85db5bba90d8c64efcebfceac07bd742acf0d8c10d992e8b4e1a cleanup-xorg.patch
+fea260015dfaf82e97ea2a77d7810e11f85503446ce6450ed9fe3072ce2151bb2ecac56fbc917a47b8ebcaf9fa51c077d4e798c4ef62919e18d1751fd7de92fc authed.patch
+829397bdb1d5fd6887b7836d503b081a8827346aa775b702d4ea9946d3ff4374446175e462732e52e33e1a53481b99cbe652c44e031c79823d5fbf67e4b48c46 allocate-vt.patch
+378a735946c93dc9fb746cc4d4cf436d728ec0f1fddd3fcac8797ef90acb0d6582e41ac0a91b96d3886695b8664627ef81822f531c67deb12085626206f27136 autologin-type.patch
+0997ae1ea2f0b44835a397f107d73ae2eb0d492cbf97b3f446c388eebb6ec0b9255b2352a51c9e00ebf846c0fec61998895aba865e06814ee20ab9d526b3f353 session-env.patch
+3941a27c143205f7cc5d2e4fee96bdf463319b85f412ef076eedc730f82c16f0e565c897beeddda04b678bf9040511d3900ef8f7452a6a6b8f224c597232194b rootless-xorg.patch
+a7fba93e5eb78e2afca6106559cef64885d9be8ac5b69b2618af60d857c18a8ccac4800ca9698cc808e97039a9584dd073baf0a119dc8072d796a47001c52ee6 qt515.patch
+d603934552bad47edda458d7a4df2310e98bde74bdb3bf8588f5171b2a5d68814192b8dc8f5599b35402f9a747d519d985d4976e7aa50dabed445f99a112594c sddm.initd"
diff --git a/user/sddm/allocate-vt.patch b/user/sddm/allocate-vt.patch
new file mode 100644
index 000000000..34e599cd3
--- /dev/null
+++ b/user/sddm/allocate-vt.patch
@@ -0,0 +1,220 @@
+From f0b6dec66a6fc8cc6e21da57f81190843b483f76 Mon Sep 17 00:00:00 2001
+From: Pier Luigi Fiorini <pierluigi.fiorini@liri.io>
+Date: Sun, 28 Feb 2021 12:07:33 +0100
+Subject: [PATCH] Allocate VT for the display
+
+Replace the old crude algorithm to find the next available VT with a
+more reliable method using the VT_OPENQRY ioctl.
+
+General.MinimumVT setting is now obsolete and it's no longer used.
+---
+ CMakeLists.txt | 4 ----
+ data/man/sddm.conf.rst.in | 2 +-
+ src/common/Configuration.h | 1 -
+ src/common/Constants.h.in | 1 -
+ src/daemon/Display.cpp | 7 +++++--
+ src/daemon/Display.h | 2 +-
+ src/daemon/Seat.cpp | 38 +++-----------------------------------
+ src/daemon/Seat.h | 3 +--
+ 8 files changed, 11 insertions(+), 47 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index e52e0e903..9614b4e1e 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -141,7 +141,6 @@ if(SYSTEMD_FOUND)
+ string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SYSTEM_UNIT_DIR ${SYSTEMD_SYSTEM_UNIT_DIR})
+ endif()
+
+- set(MINIMUM_VT 1)
+ set(HALT_COMMAND "/usr/bin/systemctl poweroff")
+ set(REBOOT_COMMAND "/usr/bin/systemctl reboot")
+ else()
+@@ -159,7 +158,6 @@ if(ELOGIND_FOUND)
+ add_definitions(-DHAVE_ELOGIND)
+ set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_ELOGIND)
+
+- set(MINIMUM_VT 7)
+ set(HALT_COMMAND "/usr/bin/loginctl poweroff")
+ set(REBOOT_COMMAND "/usr/bin/loginctl reboot")
+ endif()
+@@ -171,10 +169,8 @@ if (NOT ELOGIND_FOUND AND NOT SYSTEMD_FOUND)
+ # commands for shutdown and reboot. On FreeBSD, there are
+ # normally more getty's running than on Linux.
+ if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
+- set(MINIMUM_VT 9)
+ set(HALT_COMMAND "/sbin/shutdown -p now")
+ else()
+- set(MINIMUM_VT 7)
+ set(HALT_COMMAND "/sbin/shutdown -h -P now")
+ endif()
+ set(REBOOT_COMMAND "/sbin/shutdown -r now")
+diff --git a/data/man/sddm.conf.rst.in b/data/man/sddm.conf.rst.in
+index bee07681d..1061540c0 100644
+--- a/data/man/sddm.conf.rst.in
++++ b/data/man/sddm.conf.rst.in
+@@ -144,7 +144,7 @@ OPTIONS
+ Minimum virtual terminal number that will be used
+ by the first display. Virtual terminal number will
+ increase as new displays added.
+- Default value is @MINIMUM_VT@.
++ This setting is no longer available since SDDM v0.20.
+
+ `EnableHiDPI=`
+ Enables Qt's automatic HiDPI scaling.
+diff --git a/src/common/Configuration.h b/src/common/Configuration.h
+index cf44a629c..b79871988 100644
+--- a/src/common/Configuration.h
++++ b/src/common/Configuration.h
+@@ -70,7 +70,6 @@ namespace SDDM {
+ Entry(UserAuthFile, QString, _S(".Xauthority"), _S("Path to the Xauthority file"));
+ Entry(DisplayCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xsetup"), _S("Path to a script to execute when starting the display server"));
+ Entry(DisplayStopCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xstop"), _S("Path to a script to execute when stopping the display server"));
+- Entry(MinimumVT, int, MINIMUM_VT, _S("The lowest virtual terminal number that will be used."));
+ Entry(EnableHiDPI, bool, false, _S("Enable Qt's automatic high-DPI scaling"));
+ );
+
+diff --git a/src/common/Constants.h.in b/src/common/Constants.h.in
+index e174b5bfc..480512880 100644
+--- a/src/common/Constants.h.in
++++ b/src/common/Constants.h.in
+@@ -37,7 +37,6 @@
+ #define SYSTEM_CONFIG_DIR "@SYSTEM_CONFIG_DIR@"
+
+ #define LOG_FILE "@LOG_FILE@"
+-#define MINIMUM_VT @MINIMUM_VT@
+
+ #define UID_MIN @UID_MIN@
+ #define UID_MAX @UID_MAX@
+diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
+index ed13bae79..3c77454fc 100644
+--- a/src/daemon/Display.cpp
++++ b/src/daemon/Display.cpp
+@@ -46,6 +46,7 @@
+
+ #include "Login1Manager.h"
+ #include "Login1Session.h"
++#include "VirtualTerminal.h"
+
+ #if defined(Q_OS_LINUX)
+ #include <utmp.h>
+@@ -53,14 +54,16 @@
+ #include <utmpx.h>
+
+ namespace SDDM {
+- Display::Display(const int terminalId, Seat *parent) : QObject(parent),
+- m_terminalId(terminalId),
++ Display::Display(Seat *parent) : QObject(parent),
+ m_auth(new Auth(this)),
+ m_displayServer(new XorgDisplayServer(this)),
+ m_seat(parent),
+ m_socketServer(new SocketServer(this)),
+ m_greeter(new Greeter(this)) {
+
++ // Allocate vt
++ m_terminalId = VirtualTerminal::setUpNewVt();
++
+ // respond to authentication requests
+ m_auth->setVerbose(true);
+ connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged);
+diff --git a/src/daemon/Display.h b/src/daemon/Display.h
+index 8b3c2c635..61dd9f630 100644
+--- a/src/daemon/Display.h
++++ b/src/daemon/Display.h
+@@ -41,7 +41,7 @@ namespace SDDM {
+ Q_OBJECT
+ Q_DISABLE_COPY(Display)
+ public:
+- explicit Display(int terminalId, Seat *parent);
++ explicit Display(Seat *parent);
+ ~Display();
+
+ QString displayId() const;
+diff --git a/src/daemon/Seat.cpp b/src/daemon/Seat.cpp
+index 838c2221d..a2f3d0c39 100644
+--- a/src/daemon/Seat.cpp
++++ b/src/daemon/Seat.cpp
+@@ -33,18 +33,6 @@
+ #include <functional>
+
+ namespace SDDM {
+- int findUnused(int minimum, std::function<bool(const int)> used) {
+- // initialize with minimum
+- int number = minimum;
+-
+- // find unused
+- while (used(number))
+- number++;
+-
+- // return number;
+- return number;
+- }
+-
+ Seat::Seat(const QString &name, QObject *parent) : QObject(parent), m_name(name) {
+ createDisplay();
+ }
+@@ -53,30 +41,13 @@ namespace SDDM {
+ return m_name;
+ }
+
+- void Seat::createDisplay(int terminalId) {
++ void Seat::createDisplay() {
+ //reload config if needed
+ mainConfig.load();
+
+- if (m_name == QLatin1String("seat0")) {
+- if (terminalId == -1) {
+- // find unused terminal
+- terminalId = findUnused(mainConfig.X11.MinimumVT.get(), [&](const int number) {
+- return m_terminalIds.contains(number);
+- });
+- }
+-
+- // mark terminal as used
+- m_terminalIds << terminalId;
+-
+- // log message
+- qDebug() << "Adding new display" << "on vt" << terminalId << "...";
+- }
+- else {
+- qDebug() << "Adding new VT-less display...";
+- }
+-
+ // create a new display
+- Display *display = new Display(terminalId, this);
++ qDebug() << "Adding new display...";
++ Display *display = new Display(this);
+
+ // restart display on stop
+ connect(display, &Display::stopped, this, &Seat::displayStopped);
+@@ -112,9 +83,6 @@ namespace SDDM {
+ // remove display from list
+ m_displays.removeAll(display);
+
+- // mark display and terminal ids as unused
+- m_terminalIds.removeAll(display->terminalId());
+-
+ // stop the display
+ display->blockSignals(true);
+ display->stop();
+diff --git a/src/daemon/Seat.h b/src/daemon/Seat.h
+index f9fe7331f..685eaedd2 100644
+--- a/src/daemon/Seat.h
++++ b/src/daemon/Seat.h
+@@ -35,7 +35,7 @@ namespace SDDM {
+ const QString &name() const;
+
+ public slots:
+- void createDisplay(int terminalId = -1);
++ void createDisplay();
+ void removeDisplay(SDDM::Display* display);
+
+ private slots:
+@@ -47,7 +47,6 @@ namespace SDDM {
+ QString m_name;
+
+ QVector<Display *> m_displays;
+- QVector<int> m_terminalIds;
+ };
+ }
+
diff --git a/user/sddm/authed.patch b/user/sddm/authed.patch
new file mode 100644
index 000000000..fe76652f8
--- /dev/null
+++ b/user/sddm/authed.patch
@@ -0,0 +1,32 @@
+From b2520c9fdeff081b1ac66a4a15289cedfa6944b9 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+Date: Wed, 9 Dec 2020 19:33:08 +0100
+Subject: [PATCH] Emit XorgDisplayServer::started only when the auth file is
+ ready
+
+---
+ src/daemon/XorgDisplayServer.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index 3a7bee0d8..331adcda7 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -219,8 +219,6 @@ namespace SDDM {
+ // close our pipe
+ close(pipeFds[0]);
+
+- emit started();
+-
+ // The file is also used by the greeter, which does care about the
+ // display number. Write the proper entry, if it's different.
+ if(m_display != QStringLiteral(":0")) {
+@@ -232,6 +230,8 @@ namespace SDDM {
+ }
+ changeOwner(m_authPath);
+
++ emit started();
++
+ // set flag
+ m_started = true;
+
diff --git a/user/sddm/autologin-type.patch b/user/sddm/autologin-type.patch
new file mode 100644
index 000000000..cc5738065
--- /dev/null
+++ b/user/sddm/autologin-type.patch
@@ -0,0 +1,31 @@
+From e81dfcd6913c4fbd1801597168291b1e396633d8 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+Date: Wed, 6 Jan 2021 16:00:34 +0100
+Subject: [PATCH] Fix sessions being started as the wrong type on autologin
+
+For autologin, the last session is used, which contains a full path.
+Display::findSessionEntry didn't handle that correctly, which led to
+X11 sessions getting started as Wayland ones (or the other way around
+before 994fa67).
+
+Fixes #1348
+---
+ src/daemon/Display.cpp | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
+index b95f6e51e..9f1fabc68 100644
+--- a/src/daemon/Display.cpp
++++ b/src/daemon/Display.cpp
+@@ -245,6 +245,11 @@ namespace SDDM {
+ }
+
+ bool Display::findSessionEntry(const QDir &dir, const QString &name) const {
++ // Given an absolute path: Check that it matches dir
++ const QFileInfo fileInfo(name);
++ if (fileInfo.isAbsolute() && fileInfo.absolutePath() != dir.absolutePath())
++ return false;
++
+ QString fileName = name;
+
+ // append extension
diff --git a/user/sddm/cleanup-xorg.patch b/user/sddm/cleanup-xorg.patch
new file mode 100644
index 000000000..7949b2dbf
--- /dev/null
+++ b/user/sddm/cleanup-xorg.patch
@@ -0,0 +1,63 @@
+From 3b00cd06f82837ba85f37c9f632261e7b4c6fd35 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+Date: Wed, 9 Dec 2020 19:28:41 +0100
+Subject: [PATCH] Explicitly stop Xorg when starting fails
+
+When Xorg starts but there is an error, stop it explicitly instead of assuming
+that X exits itself. This avoids a possibly lingering Xorg process in the
+XorgDisplayServer instance. Add a check and warning message if Xorg is
+restarted too early (shouldn't happen).
+---
+ src/daemon/XorgDisplayServer.cpp | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index 5f40fe8c3..3a7bee0d8 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -118,6 +118,11 @@ namespace SDDM {
+ if (m_started)
+ return false;
+
++ if (process) {
++ qCritical() << "Tried to start Xorg before previous instance exited";
++ return false;
++ }
++
+ // create process
+ process = new QProcess(this);
+
+@@ -195,6 +200,7 @@ namespace SDDM {
+ qCritical("Failed to open pipe to start X Server");
+
+ close(pipeFds[0]);
++ stop();
+ return false;
+ }
+ QByteArray displayNumber = readPipe.readLine();
+@@ -203,6 +209,7 @@ namespace SDDM {
+ qCritical("Failed to read display number from pipe");
+
+ close(pipeFds[0]);
++ stop();
+ return false;
+ }
+ displayNumber.prepend(QByteArray(":"));
+@@ -219,6 +226,7 @@ namespace SDDM {
+ if(m_display != QStringLiteral(":0")) {
+ if(!addCookie(m_authPath)) {
+ qCritical() << "Failed to write xauth file";
++ stop();
+ return false;
+ }
+ }
+@@ -232,8 +240,7 @@ namespace SDDM {
+ }
+
+ void XorgDisplayServer::stop() {
+- // check flag
+- if (!m_started)
++ if (!process)
+ return;
+
+ // log message
diff --git a/user/sddm/qt515.patch b/user/sddm/qt515.patch
new file mode 100644
index 000000000..df58ca9fe
--- /dev/null
+++ b/user/sddm/qt515.patch
@@ -0,0 +1,199 @@
+From 3eaa0d6ab1c7efc0bdd91fcd789d8187c6c27678 Mon Sep 17 00:00:00 2001
+From: Aleix Pol <aleixpol@kde.org>
+Date: Thu, 24 Jun 2021 12:34:13 +0200
+Subject: [PATCH] Bump to Qt 5.15, port away from deprecated APIs
+
+---
+ CMakeLists.txt | 2 +-
+ README.md | 2 +-
+ src/auth/Auth.cpp | 6 +++---
+ src/auth/AuthRequest.cpp | 2 +-
+ src/common/ConfigReader.cpp | 4 ++--
+ src/daemon/PowerManager.cpp | 8 ++++++--
+ src/daemon/XorgDisplayServer.cpp | 17 +++++++++--------
+ src/greeter/GreeterProxy.cpp | 2 +-
+ 8 files changed, 24 insertions(+), 19 deletions(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 41aee21d4..6936b31f6 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -96,7 +96,7 @@ find_package(XCB REQUIRED)
+ find_package(XKB REQUIRED)
+
+ # Qt 5
+-find_package(Qt5 5.8.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test)
++find_package(Qt5 5.15.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test)
+
+ # find qt5 imports dir
+ get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
+diff --git a/README.md b/README.md
+index 7c7a55588..97cb2221c 100644
+--- a/README.md
++++ b/README.md
+@@ -39,7 +39,7 @@ money that can be used to support the features that you most desire.
+
+ ## INSTALLATION
+
+-Qt >= 5.8.0 is required to use SDDM.
++Qt >= 5.15.0 is required to use SDDM.
+
+ SDDM runs the greeter as a system user named "sddm" whose home directory needs
+ to be set to `/var/lib/sddm`.
+diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp
+index 4450cc606..1dc2dd186 100644
+--- a/src/auth/Auth.cpp
++++ b/src/auth/Auth.cpp
+@@ -134,7 +134,7 @@ namespace SDDM {
+ env.insert(QStringLiteral("LANG"), QStringLiteral("C"));
+ child->setProcessEnvironment(env);
+ connect(child, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished), this, &Auth::Private::childExited);
+- connect(child, QOverload<QProcess::ProcessError>::of(&QProcess::error), this, &Auth::Private::childError);
++ connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError);
+ connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished);
+ connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged);
+ }
+@@ -267,8 +267,8 @@ namespace SDDM {
+ }
+
+ void Auth::registerTypes() {
+- qmlRegisterType<AuthPrompt>();
+- qmlRegisterType<AuthRequest>();
++ qmlRegisterAnonymousType<AuthPrompt>("Auth", 1);
++ qmlRegisterAnonymousType<AuthRequest>("Auth", 1);
+ qmlRegisterType<Auth>("Auth", 1, 0, "Auth");
+ }
+
+diff --git a/src/auth/AuthRequest.cpp b/src/auth/AuthRequest.cpp
+index 9583b0a4d..9ccc2d259 100644
+--- a/src/auth/AuthRequest.cpp
++++ b/src/auth/AuthRequest.cpp
+@@ -73,7 +73,7 @@ namespace SDDM {
+ }
+
+ QQmlListProperty<AuthPrompt> AuthRequest::promptsDecl() {
+- return QQmlListProperty<AuthPrompt>(this, d->prompts);
++ return QQmlListProperty<AuthPrompt>(this, &d->prompts);
+ }
+
+ void AuthRequest::done() {
+diff --git a/src/common/ConfigReader.cpp b/src/common/ConfigReader.cpp
+index 041e5ed22..503b55e69 100644
+--- a/src/common/ConfigReader.cpp
++++ b/src/common/ConfigReader.cpp
+@@ -265,7 +265,7 @@ namespace SDDM {
+ QByteArray junk;
+ // stores the junk to the temporary storage
+ auto collectJunk = [&junk](const QString &data) {
+- junk.append(data);
++ junk.append(data.toUtf8());
+ };
+
+ // a short function to assign the current junk and current line to the right section, eventually create a new one
+@@ -275,7 +275,7 @@ namespace SDDM {
+ sectionData[currentSection] = QByteArray();
+ }
+ sectionData[currentSection].append(junk);
+- sectionData[currentSection].append(data);
++ sectionData[currentSection].append(data.toUtf8());
+ junk.clear();
+ };
+
+diff --git a/src/daemon/PowerManager.cpp b/src/daemon/PowerManager.cpp
+index 6eca29f55..ab1d6ac03 100644
+--- a/src/daemon/PowerManager.cpp
++++ b/src/daemon/PowerManager.cpp
+@@ -87,11 +87,15 @@ const QString UPOWER_OBJECT = QStringLiteral("org.freedesktop.UPower");
+ }
+
+ void powerOff() const {
+- QProcess::execute(mainConfig.HaltCommand.get());
++ auto command = QProcess::splitCommand(mainConfig.HaltCommand.get());
++ const QString program = command.takeFirst();
++ QProcess::execute(program, command);
+ }
+
+ void reboot() const {
+- QProcess::execute(mainConfig.RebootCommand.get());
++ auto command = QProcess::splitCommand(mainConfig.RebootCommand.get());
++ const QString program = command.takeFirst();
++ QProcess::execute(program, command);
+ }
+
+ void suspend() const {
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index fc61ee2dd..13a1691b8 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -110,7 +110,7 @@ namespace SDDM {
+ QStringList args;
+ if (!daemonApp->testing()) {
+ process->setProgram(mainConfig.X11.ServerPath.get());
+- args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts)
++ args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts)
+ << QStringLiteral("-background") << QStringLiteral("none")
+ << QStringLiteral("-seat") << displayPtr()->seat()->name()
+ << QStringLiteral("vt%1").arg(displayPtr()->terminalId());
+@@ -221,7 +221,7 @@ namespace SDDM {
+ // log message
+ qDebug() << "Display server stopped.";
+
+- QString displayStopCommand = mainConfig.X11.DisplayStopCommand.get();
++ QStringList displayStopCommand = QProcess::splitCommand(mainConfig.X11.DisplayStopCommand.get());
+
+ // create display setup script process
+ QProcess *displayStopScript = new QProcess();
+@@ -236,7 +236,8 @@ namespace SDDM {
+
+ // start display stop script
+ qDebug() << "Running display stop script " << displayStopCommand;
+- displayStopScript->start(displayStopCommand);
++ const auto program = displayStopCommand.takeFirst();
++ displayStopScript->start(program, displayStopCommand);
+
+ // wait for finished
+ if (!displayStopScript->waitForFinished(5000))
+@@ -254,8 +255,6 @@ namespace SDDM {
+ }
+
+ void XorgDisplayServer::setupDisplay() {
+- QString displayCommand = mainConfig.X11.DisplayCommand.get();
+-
+ // create cursor setup process
+ QProcess *setCursor = new QProcess();
+ // create display setup script process
+@@ -273,7 +272,7 @@ namespace SDDM {
+ displayScript->setProcessEnvironment(env);
+
+ qDebug() << "Setting default cursor";
+- setCursor->start(QStringLiteral("xsetroot -cursor_name left_ptr"));
++ setCursor->start(QStringLiteral("xsetroot"), { QStringLiteral("-cursor_name"), QStringLiteral("left_ptr") });
+
+ // delete setCursor on finish
+ connect(setCursor, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), setCursor, &QProcess::deleteLater);
+@@ -285,8 +284,10 @@ namespace SDDM {
+ }
+
+ // start display setup script
+- qDebug() << "Running display setup script " << displayCommand;
+- displayScript->start(displayCommand);
++ qDebug() << "Running display setup script " << mainConfig.X11.DisplayCommand.get();
++ QStringList displayCommand = QProcess::splitCommand(mainConfig.X11.DisplayCommand.get());
++ const QString program = displayCommand.takeFirst();
++ displayScript->start(program, displayCommand);
+
+ // delete displayScript on finish
+ connect(displayScript, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), displayScript, &QProcess::deleteLater);
+diff --git a/src/greeter/GreeterProxy.cpp b/src/greeter/GreeterProxy.cpp
+index 8fde3d904..d6e62cc75 100644
+--- a/src/greeter/GreeterProxy.cpp
++++ b/src/greeter/GreeterProxy.cpp
+@@ -46,7 +46,7 @@ namespace SDDM {
+ connect(d->socket, &QLocalSocket::connected, this, &GreeterProxy::connected);
+ connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected);
+ connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead);
+- connect(d->socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &GreeterProxy::error);
++ connect(d->socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error);
+
+ // connect to server
+ d->socket->connectToServer(socket);
diff --git a/user/sddm/retry.patch b/user/sddm/retry.patch
new file mode 100644
index 000000000..84edc7f08
--- /dev/null
+++ b/user/sddm/retry.patch
@@ -0,0 +1,115 @@
+From 42c51761cc82edbaa50d702a4614e179ad4bcd63 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+Date: Thu, 12 Nov 2020 20:30:55 +0100
+Subject: [PATCH] Retry starting the display server
+
+Even if the CanGraphical property of a Seat is true, it's possible that it's
+still too early for X to start, as it might need some driver or device which
+isn't present yet.
+
+Fixes #1316
+---
+ src/daemon/Seat.cpp | 23 ++++++++++++++++++-----
+ src/daemon/Seat.h | 4 +++-
+ src/daemon/XorgDisplayServer.cpp | 10 ++++++----
+ 3 files changed, 27 insertions(+), 10 deletions(-)
+
+diff --git a/src/daemon/Seat.cpp b/src/daemon/Seat.cpp
+index eef26da45..838c2221d 100644
+--- a/src/daemon/Seat.cpp
++++ b/src/daemon/Seat.cpp
+@@ -28,6 +28,7 @@
+
+ #include <QDebug>
+ #include <QFile>
++#include <QTimer>
+
+ #include <functional>
+
+@@ -52,7 +53,7 @@ namespace SDDM {
+ return m_name;
+ }
+
+- bool Seat::createDisplay(int terminalId) {
++ void Seat::createDisplay(int terminalId) {
+ //reload config if needed
+ mainConfig.load();
+
+@@ -84,12 +85,24 @@ namespace SDDM {
+ m_displays << display;
+
+ // start the display
+- if (!display->start()) {
+- qCritical() << "Could not start Display server on vt" << terminalId;
+- return false;
++ startDisplay(display);
++ }
++
++ void Seat::startDisplay(Display *display, int tryNr) {
++ if (display->start())
++ return;
++
++ // It's possible that the system isn't ready yet (driver not loaded,
++ // device not enumerated, ...). It's not possible to tell when that changes,
++ // so try a few times with a delay in between.
++ qWarning() << "Attempt" << tryNr << "starting the Display server on vt" << display->terminalId() << "failed";
++
++ if(tryNr >= 3) {
++ qCritical() << "Could not start Display server on vt" << display->terminalId();
++ return;
+ }
+
+- return true;
++ QTimer::singleShot(2000, display, [=] { startDisplay(display, tryNr + 1); });
+ }
+
+ void Seat::removeDisplay(Display* display) {
+diff --git a/src/daemon/Seat.h b/src/daemon/Seat.h
+index bf22566b7..f9fe7331f 100644
+--- a/src/daemon/Seat.h
++++ b/src/daemon/Seat.h
+@@ -35,13 +35,15 @@ namespace SDDM {
+ const QString &name() const;
+
+ public slots:
+- bool createDisplay(int terminalId = -1);
++ void createDisplay(int terminalId = -1);
+ void removeDisplay(SDDM::Display* display);
+
+ private slots:
+ void displayStopped();
+
+ private:
++ void startDisplay(SDDM::Display *display, int tryNr = 1);
++
+ QString m_name;
+
+ QVector<Display *> m_displays;
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index e60c02210..5f40fe8c3 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -248,6 +248,12 @@ namespace SDDM {
+ }
+
+ void XorgDisplayServer::finished() {
++ // clean up
++ if (process) {
++ process->deleteLater();
++ process = nullptr;
++ }
++
+ // check flag
+ if (!m_started)
+ return;
+@@ -283,10 +289,6 @@ namespace SDDM {
+ displayStopScript->deleteLater();
+ displayStopScript = nullptr;
+
+- // clean up
+- process->deleteLater();
+- process = nullptr;
+-
+ // remove authority file
+ QFile::remove(m_authPath);
+
diff --git a/user/sddm/revert-fedora.patch b/user/sddm/revert-fedora.patch
new file mode 100644
index 000000000..63bb58395
--- /dev/null
+++ b/user/sddm/revert-fedora.patch
@@ -0,0 +1,74 @@
+From 994fa67b01ccfac1aaac08572302bbbea7842dc3 Mon Sep 17 00:00:00 2001
+Subject: Revert: Prefer Wayland sessions over X11 ones
+
+Reference: https://fedoraproject.org/wiki/Changes/WaylandByDefaultForPlasma
+---
+ src/common/Session.cpp | 8 ++++----
+ src/daemon/Display.cpp | 6 +++---
+ src/greeter/SessionModel.cpp | 6 +++---
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+
+--- b/src/common/Session.cpp
++++ a/src/common/Session.cpp
+@@ -131,14 +131,14 @@
+ m_desktopNames.clear();
+
+ switch (type) {
++ case X11Session:
++ m_dir = QDir(mainConfig.X11.SessionDir.get());
++ m_xdgSessionType = QStringLiteral("x11");
++ break;
+ case WaylandSession:
+ m_dir = QDir(mainConfig.Wayland.SessionDir.get());
+ m_xdgSessionType = QStringLiteral("wayland");
+ break;
+- case X11Session:
+- m_dir = QDir(mainConfig.X11.SessionDir.get());
+- m_xdgSessionType = QStringLiteral("x11");
+- break;
+ default:
+ m_xdgSessionType.clear();
+ break;
+--- b/src/daemon/Display.cpp
++++ a/src/daemon/Display.cpp
+@@ -115,10 +115,10 @@
+ if (autologinSession.isEmpty()) {
+ autologinSession = stateConfig.Last.Session.get();
+ }
++ if (findSessionEntry(mainConfig.X11.SessionDir.get(), autologinSession)) {
++ sessionType = Session::X11Session;
++ } else if (findSessionEntry(mainConfig.Wayland.SessionDir.get(), autologinSession)) {
+- if (findSessionEntry(mainConfig.Wayland.SessionDir.get(), autologinSession)) {
+ sessionType = Session::WaylandSession;
+- } else if (findSessionEntry(mainConfig.X11.SessionDir.get(), autologinSession)) {
+- sessionType = Session::X11Session;
+ } else {
+ qCritical() << "Unable to find autologin session entry" << autologinSession;
+ return false;
+--- b/src/greeter/SessionModel.cpp
++++ a/src/greeter/SessionModel.cpp
+@@ -41,8 +41,8 @@
+ SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) {
+ // initial population
+ beginResetModel();
++ populate(Session::X11Session, mainConfig.X11.SessionDir.get());
+ populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get());
+- populate(Session::X11Session, mainConfig.X11.SessionDir.get());
+ endResetModel();
+
+ // refresh everytime a file is changed, added or removed
+@@ -50,12 +50,12 @@
+ connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) {
+ beginResetModel();
+ d->sessions.clear();
++ populate(Session::X11Session, mainConfig.X11.SessionDir.get());
+ populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get());
+- populate(Session::X11Session, mainConfig.X11.SessionDir.get());
+ endResetModel();
+ });
++ watcher->addPath(mainConfig.X11.SessionDir.get());
+ watcher->addPath(mainConfig.Wayland.SessionDir.get());
+- watcher->addPath(mainConfig.X11.SessionDir.get());
+ }
+
+ SessionModel::~SessionModel() {
diff --git a/user/sddm/rootless-xorg.patch b/user/sddm/rootless-xorg.patch
new file mode 100644
index 000000000..99a2e189d
--- /dev/null
+++ b/user/sddm/rootless-xorg.patch
@@ -0,0 +1,2012 @@
+From 9881225a92ffe6cf3d4a84273192b08b2d2f6046 Mon Sep 17 00:00:00 2001
+From: Pier Luigi Fiorini <pierluigi.fiorini@liri.io>
+Date: Tue, 27 Aug 2019 22:31:00 +0200
+Subject: [PATCH] X11 display server without root privileges
+
+Introduce an alternative display server option to run the greeter with
+the X11 server as an unprivileged user.
+
+Root privileges are required by default, but this will change in the
+future.
+
+Greeter output is forwarded to the helper process, instead of saving it
+into a separate file like user sessions do.
+This means the greeter output is available in the journal with the
+daemon and helper logs.
+
+Display start and stop commands are executed as sddm user and this might
+break the workflow of our users.
+That is the reason why we still run Xorg as root for the greeter by
+default.
+
+X11 user session always spawn a new display server without root
+privileges.
+
+Closes: #246
+---
+ CMakeLists.txt | 1 +
+ data/man/sddm.conf.rst.in | 14 +-
+ src/auth/Auth.cpp | 24 ++-
+ src/auth/Auth.h | 14 ++
+ src/auth/AuthMessages.h | 1 +
+ src/common/Configuration.h | 3 +
+ src/common/Session.cpp | 10 ++
+ src/common/Session.h | 4 +
+ src/common/XAuth.cpp | 127 ++++++++++++++
+ src/common/XAuth.h | 55 ++++++
+ src/daemon/CMakeLists.txt | 6 +-
+ src/daemon/Display.cpp | 64 +++++--
+ src/daemon/Display.h | 11 ++
+ src/daemon/Greeter.cpp | 41 +++--
+ src/daemon/Greeter.h | 5 +
+ src/daemon/XorgDisplayServer.cpp | 78 ++-------
+ src/daemon/XorgDisplayServer.h | 10 +-
+ src/daemon/XorgUserDisplayServer.cpp | 102 +++++++++++
+ src/daemon/XorgUserDisplayServer.h | 53 ++++++
+ src/greeter/GreeterApp.cpp | 9 +
+ src/helper/Backend.cpp | 5 +
+ src/helper/Backend.h | 2 +
+ src/helper/CMakeLists.txt | 4 +
+ src/helper/HelperApp.cpp | 30 +++-
+ src/helper/HelperApp.h | 1 +
+ src/helper/UserSession.cpp | 202 +++++++++++++--------
+ src/helper/UserSession.h | 20 ++-
+ src/helper/backend/PamBackend.cpp | 6 +-
+ src/helper/xorguserhelper.cpp | 252 +++++++++++++++++++++++++++
+ src/helper/xorguserhelper.h | 64 +++++++
+ 30 files changed, 1036 insertions(+), 182 deletions(-)
+ create mode 100644 src/common/XAuth.cpp
+ create mode 100644 src/common/XAuth.h
+ create mode 100644 src/daemon/XorgUserDisplayServer.cpp
+ create mode 100644 src/daemon/XorgUserDisplayServer.h
+ create mode 100644 src/helper/xorguserhelper.cpp
+ create mode 100644 src/helper/xorguserhelper.h
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 9614b4e1e..41aee21d4 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -16,6 +16,7 @@ set(SDDM_VERSION_MAJOR 0)
+ set(SDDM_VERSION_MINOR 19)
+ set(SDDM_VERSION_PATCH 0)
+ set(SDDM_VERSION_STRING "${SDDM_VERSION_MAJOR}.${SDDM_VERSION_MINOR}.${SDDM_VERSION_PATCH}")
++add_compile_definitions("SDDM_VERSION=\"${SDDM_VERSION_STRING}\"")
+
+ # Set up packaging
+ set(CPACK_PACKAGE_NAME "sddm")
+diff --git a/data/man/sddm.conf.rst.in b/data/man/sddm.conf.rst.in
+index 1061540c0..960e65c68 100644
+--- a/data/man/sddm.conf.rst.in
++++ b/data/man/sddm.conf.rst.in
+@@ -6,7 +6,7 @@
+ sddm display manager configuration
+ ----------------------------------
+
+-:Date: August 2015
++:Date: March 2021
+ :Version: sddm @SDDM_VERSION_STRING@
+ :Manual section: 5
+ :Manual group: sddm
+@@ -36,6 +36,14 @@ OPTIONS
+
+ [General] section:
+
++`DisplayServer=`
++ Select the display server to use for the greeter.
++ Valid values are:
++ * `x11`: X server running as root.
++ * `x11-user`: X server running as unprivileged user.
++ Default value is "x11".
++ For `x11-user` you might need to configure Xorg.wrap(1).
++
+ `HaltCommand=`
+ Halt command.
+ Default value is "@HALT_COMMAND@".
+@@ -134,10 +142,14 @@ OPTIONS
+
+ `DisplayCommand=`
+ Path of script to execute when starting the display server.
++ The script will be executed as root when General.DisplayServer
++ is "x11", otherwise as sddm user.
+ Default value is "@DATA_INSTALL_DIR@/scripts/Xsetup".
+
+ `DisplayStopCommand=`
+ Path of script to execute when stopping the display server.
++ The script will be executed as root when General.DisplayServer
++ is "x11", otherwise as sddm user.
+ Default value is "@DATA_INSTALL_DIR@/scripts/Xstop".
+
+ `MinimumVT=`
+diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp
+index 403186ee1..4450cc606 100644
+--- a/src/auth/Auth.cpp
++++ b/src/auth/Auth.cpp
+@@ -62,6 +62,7 @@ namespace SDDM {
+ AuthRequest *request { nullptr };
+ QProcess *child { nullptr };
+ QLocalSocket *socket { nullptr };
++ QString displayServerCmd;
+ QString sessionPath { };
+ QString user { };
+ QString cookie { };
+@@ -202,6 +203,15 @@ namespace SDDM {
+ str.send();
+ break;
+ }
++ case DISPLAY_SERVER_STARTED: {
++ QString displayName;
++ str >> displayName;
++ Q_EMIT auth->displayServerReady(displayName);
++ str.reset();
++ str << DISPLAY_SERVER_STARTED;
++ str.send();
++ break;
++ }
+ default: {
+ Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL);
+ }
+@@ -210,7 +220,9 @@ namespace SDDM {
+
+ void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) {
+ if (exitStatus != QProcess::NormalExit) {
+- qWarning("Auth: sddm-helper crashed (exit code %d)", exitCode);
++ qWarning("Auth: sddm-helper (%s) crashed (exit code %d)",
++ qPrintable(child->arguments().join(QLatin1Char(' '))),
++ HelperExitStatus(exitStatus));
+ Q_EMIT qobject_cast<Auth*>(parent())->error(child->errorString(), ERROR_INTERNAL);
+ }
+
+@@ -334,6 +346,14 @@ namespace SDDM {
+ }
+ }
+
++ void Auth::setDisplayServerCommand(const QString &command)
++ {
++ if (d->displayServerCmd != command) {
++ d->displayServerCmd = command;
++ Q_EMIT displayServerCommandChanged();
++ }
++ }
++
+ void Auth::setSession(const QString& path) {
+ if (path != d->sessionPath) {
+ d->sessionPath = path;
+@@ -361,6 +381,8 @@ namespace SDDM {
+ args << QStringLiteral("--user") << d->user;
+ if (d->autologin)
+ args << QStringLiteral("--autologin");
++ if (!d->displayServerCmd.isEmpty())
++ args << QStringLiteral("--display-server") << d->displayServerCmd;
+ if (d->greeter)
+ args << QStringLiteral("--greeter");
+ d->child->start(QStringLiteral("%1/sddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args);
+diff --git a/src/auth/Auth.h b/src/auth/Auth.h
+index f7cb8acd3..dc3df24d1 100644
+--- a/src/auth/Auth.h
++++ b/src/auth/Auth.h
+@@ -141,6 +141,12 @@ namespace SDDM {
+ */
+ void setUser(const QString &user);
+
++ /**
++ * Set the display server command to be started before the greeter.
++ * @param command Command of the display server to be started
++ */
++ void setDisplayServerCommand(const QString &command);
++
+ /**
+ * Set the session to be started after authenticating.
+ * @param path Path of the session executable to be started
+@@ -165,6 +171,7 @@ namespace SDDM {
+ void verboseChanged();
+ void cookieChanged();
+ void userChanged();
++ void displayServerCommandChanged();
+ void sessionChanged();
+ void requestChanged();
+
+@@ -186,6 +193,13 @@ namespace SDDM {
+ */
+ void sessionStarted(bool success, qint64 pid);
+
++ /**
++ * Emitted when the display server is ready.
++ *
++ * @param displayName display name
++ */
++ void displayServerReady(const QString &displayName);
++
+ /**
+ * Emitted when the helper quits, either after authentication or when the session ends.
+ * Or, when something goes wrong.
+diff --git a/src/auth/AuthMessages.h b/src/auth/AuthMessages.h
+index 3bc97b6ba..6aea7483f 100644
+--- a/src/auth/AuthMessages.h
++++ b/src/auth/AuthMessages.h
+@@ -97,6 +97,7 @@ namespace SDDM {
+ REQUEST,
+ AUTHENTICATED,
+ SESSION_STATUS,
++ DISPLAY_SERVER_STARTED,
+ MSG_LAST,
+ };
+
+diff --git a/src/common/Configuration.h b/src/common/Configuration.h
+index b79871988..47bfa2710 100644
+--- a/src/common/Configuration.h
++++ b/src/common/Configuration.h
+@@ -37,6 +37,9 @@ namespace SDDM {
+ enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF };
+
+ // Name Type Default value Description
++ // TODO: Change default to x11-user in a future release
++ Entry(DisplayServer, QString, _S("x11"), _S("Which display server should be used.\n"
++ "Valid values are: x11, x11-user."));
+ Entry(HaltCommand, QString, _S(HALT_COMMAND), _S("Halt command"));
+ Entry(RebootCommand, QString, _S(REBOOT_COMMAND), _S("Reboot command"));
+ Entry(Numlock, NumState, NUM_NONE, _S("Initial NumLock state. Can be on, off or none.\n"
+diff --git a/src/common/Session.cpp b/src/common/Session.cpp
+index a026c1f87..1b932c57a 100644
+--- a/src/common/Session.cpp
++++ b/src/common/Session.cpp
+@@ -52,6 +52,16 @@ namespace SDDM {
+ return m_type;
+ }
+
++ int Session::vt() const
++ {
++ return m_vt;
++ }
++
++ void Session::setVt(int vt)
++ {
++ m_vt = vt;
++ }
++
+ QString Session::xdgSessionType() const
+ {
+ return m_xdgSessionType;
+diff --git a/src/common/Session.h b/src/common/Session.h
+index aa196e9c6..3abc993fb 100644
+--- a/src/common/Session.h
++++ b/src/common/Session.h
+@@ -43,6 +43,9 @@ namespace SDDM {
+
+ Type type() const;
+
++ int vt() const;
++ void setVt(int vt);
++
+ QString xdgSessionType() const;
+
+ QDir directory() const;
+@@ -70,6 +73,7 @@ namespace SDDM {
+ QProcessEnvironment parseEnv(const QString &list);
+ bool m_valid;
+ Type m_type;
++ int m_vt = 0;
+ QDir m_dir;
+ QString m_name;
+ QString m_fileName;
+diff --git a/src/common/XAuth.cpp b/src/common/XAuth.cpp
+new file mode 100644
+index 000000000..bc7b10caf
+--- /dev/null
++++ b/src/common/XAuth.cpp
+@@ -0,0 +1,127 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++* Copyright (c) 2013 Abdurrahman AVCI <abdurrahmanavci@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#include <QDebug>
++#include <QDir>
++#include <QUuid>
++
++#include "Configuration.h"
++#include "Constants.h"
++#include "XAuth.h"
++
++#include <random>
++
++namespace SDDM {
++
++XAuth::XAuth()
++{
++ m_authDir = QStringLiteral(RUNTIME_DIR);
++}
++
++QString XAuth::authDirectory() const
++{
++ return m_authDir;
++}
++
++void XAuth::setAuthDirectory(const QString &path)
++{
++ if (m_setup) {
++ qWarning("Unable to set xauth directory after setup");
++ return;
++ }
++
++ m_authDir = path;
++}
++
++QString XAuth::authPath() const
++{
++ return m_authPath;
++}
++
++QString XAuth::cookie() const
++{
++ return m_cookie;
++}
++
++void XAuth::setup()
++{
++ if (m_setup)
++ return;
++
++ m_setup = true;
++
++ // Create directory if not existing
++ QDir().mkpath(m_authDir);
++
++ // Set path
++ m_authPath = QStringLiteral("%1/%2").arg(m_authDir).arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
++ qDebug() << "Xauthority path:" << m_authPath;
++
++ // Generate cookie
++ std::random_device rd;
++ std::mt19937 gen(rd());
++ std::uniform_int_distribution<> dis(0, 15);
++
++ // Reseve 32 bytes
++ m_cookie.reserve(32);
++
++ // Create a random hexadecimal number
++ const char *digits = "0123456789abcdef";
++ for (int i = 0; i < 32; ++i)
++ m_cookie[i] = QLatin1Char(digits[dis(gen)]);
++}
++
++bool XAuth::addCookie(const QString &display)
++{
++ if (!m_setup) {
++ qWarning("Please setup xauth before adding a cookie");
++ return false;
++ }
++
++ return XAuth::addCookieToFile(display, m_authPath, m_cookie);
++}
++
++bool XAuth::addCookieToFile(const QString &display, const QString &fileName,
++ const QString &cookie)
++{
++ qDebug() << "Adding cookie to" << fileName;
++
++ // Touch file
++ QFile file_handler(fileName);
++ file_handler.open(QIODevice::Append);
++ file_handler.close();
++
++ QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(fileName);
++
++ // Execute xauth
++ FILE *fp = ::popen(qPrintable(cmd), "w");
++
++ // Check file
++ if (!fp)
++ return false;
++ fprintf(fp, "remove %s\n", qPrintable(display));
++ fprintf(fp, "add %s . %s\n", qPrintable(display), qPrintable(cookie));
++ fprintf(fp, "exit\n");
++
++ // Close pipe
++ return pclose(fp) == 0;
++}
++
++} // namespace SDDM
+diff --git a/src/common/XAuth.h b/src/common/XAuth.h
+new file mode 100644
+index 000000000..3e80f4ead
+--- /dev/null
++++ b/src/common/XAuth.h
+@@ -0,0 +1,55 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++* Copyright (c) 2013 Abdurrahman AVCI <abdurrahmanavci@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#ifndef SDDM_XAUTH_H
++#define SDDM_XAUTH_H
++
++#include <QString>
++
++namespace SDDM {
++
++class XAuth
++{
++public:
++ XAuth();
++
++ QString authDirectory() const;
++ void setAuthDirectory(const QString &path);
++
++ QString authPath() const;
++ QString cookie() const;
++
++ void setup();
++ bool addCookie(const QString &display);
++
++ static bool addCookieToFile(const QString &display,
++ const QString &fileName,
++ const QString &cookie);
++
++private:
++ bool m_setup = false;
++ QString m_authDir;
++ QString m_authPath;
++ QString m_cookie;
++};
++
++} // namespace SDDM
++
++#endif // SDDM_XAUTH_H
+diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
+index 86d014bec..b411e42bd 100644
+--- a/src/daemon/CMakeLists.txt
++++ b/src/daemon/CMakeLists.txt
+@@ -13,6 +13,8 @@ set(DAEMON_SOURCES
+ ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/Session.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp
++ ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp
++ ${CMAKE_SOURCE_DIR}/src/common/XAuth.h
+ ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp
+ ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp
+ ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp
+@@ -22,13 +24,15 @@ set(DAEMON_SOURCES
+ DisplayManager.cpp
+ DisplayServer.cpp
+ LogindDBusTypes.cpp
+- XorgDisplayServer.cpp
+ Greeter.cpp
+ PowerManager.cpp
+ Seat.cpp
+ SeatManager.cpp
+ SignalHandler.cpp
+ SocketServer.cpp
++ XorgDisplayServer.cpp
++ XorgUserDisplayServer.cpp
++ XorgUserDisplayServer.h
+ )
+
+ # Different implementations of the VT switching code
+diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
+index 3c77454fc..26ca3bb60 100644
+--- a/src/daemon/Display.cpp
++++ b/src/daemon/Display.cpp
+@@ -25,6 +25,7 @@
+ #include "DaemonApp.h"
+ #include "DisplayManager.h"
+ #include "XorgDisplayServer.h"
++#include "XorgUserDisplayServer.h"
+ #include "Seat.h"
+ #include "SocketServer.h"
+ #include "Greeter.h"
+@@ -56,7 +57,6 @@
+ namespace SDDM {
+ Display::Display(Seat *parent) : QObject(parent),
+ m_auth(new Auth(this)),
+- m_displayServer(new XorgDisplayServer(this)),
+ m_seat(parent),
+ m_socketServer(new SocketServer(this)),
+ m_greeter(new Greeter(this)) {
+@@ -64,6 +64,32 @@ namespace SDDM {
+ // Allocate vt
+ m_terminalId = VirtualTerminal::setUpNewVt();
+
++ // Save display server type
++ const QString &displayServerType = mainConfig.DisplayServer.get().toLower();
++ if (displayServerType == QLatin1String("x11"))
++ m_displayServerType = X11DisplayServerType;
++ else if (displayServerType == QStringLiteral("x11-user"))
++ m_displayServerType = X11UserDisplayServerType;
++ else {
++ qWarning("\"%s\" is an invalid value for General.DisplayServer: fall back to \"x11\"",
++ qPrintable(displayServerType));
++ m_displayServerType = X11DisplayServerType;
++ }
++
++ // Create display server
++ switch (m_displayServerType) {
++ case X11DisplayServerType:
++ m_displayServer = new XorgDisplayServer(this);
++ break;
++ case X11UserDisplayServerType:
++ m_displayServer = new XorgUserDisplayServer(this);
++ m_greeter->setDisplayServerCommand(XorgUserDisplayServer::command(this));
++ break;
++ }
++
++ // Print what VT we are using for more information
++ qDebug("Using VT %d", m_terminalId);
++
+ // respond to authentication requests
+ m_auth->setVerbose(true);
+ connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged);
+@@ -89,6 +115,16 @@ namespace SDDM {
+ stop();
+ }
+
++ Display::DisplayServerType Display::displayServerType() const
++ {
++ return m_displayServerType;
++ }
++
++ DisplayServer *Display::displayServer() const
++ {
++ return m_displayServer;
++ }
++
+ QString Display::displayId() const {
+ return m_displayServer->display();
+ }
+@@ -181,7 +217,8 @@ namespace SDDM {
+
+ // set greeter params
+ m_greeter->setDisplay(this);
+- m_greeter->setAuthPath(qobject_cast<XorgDisplayServer *>(m_displayServer)->authPath());
++ if (qobject_cast<XorgDisplayServer *>(m_displayServer))
++ m_greeter->setAuthPath(qobject_cast<XorgDisplayServer *>(m_displayServer)->authPath());
+ m_greeter->setSocket(m_socketServer->socketAddress());
+ m_greeter->setTheme(findGreeterTheme());
+
+@@ -317,20 +354,16 @@ namespace SDDM {
+ // last session later, in slotAuthenticationFinished()
+ m_sessionName = session.fileName();
+
++ // New VT
++ m_lastSession.setVt(VirtualTerminal::setUpNewVt());
++
+ // some information
+ qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec();
+
+ QProcessEnvironment env;
+ env.insert(session.additionalEnv());
+
+- if (seat()->name() == QLatin1String("seat0")) {
+- // Use the greeter VT, for wayland sessions the helper overwrites this
+- env.insert(QStringLiteral("XDG_VTNR"), QString::number(terminalId()));
+- }
+-
+ env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get());
+- if (session.xdgSessionType() == QLatin1String("x11"))
+- env.insert(QStringLiteral("DISPLAY"), name());
+ env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name()));
+ env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId())));
+ env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession());
+@@ -338,10 +371,16 @@ namespace SDDM {
+ env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user"));
+ env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType());
+ env.insert(QStringLiteral("XDG_SEAT"), seat()->name());
++ env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_lastSession.vt()));
+ env.insert(QStringLiteral("XDG_SESSION_DESKTOP"), session.desktopNames());
+
+ m_auth->insertEnvironment(env);
+
++ if (session.xdgSessionType() == QLatin1String("x11"))
++ m_auth->setDisplayServerCommand(XorgUserDisplayServer::command(this));
++ else
++ m_auth->setDisplayServerCommand(QStringLiteral());
++
+ m_auth->setUser(user);
+ if (m_reuseSessionId.isNull()) {
+ m_auth->setSession(session.exec());
+@@ -358,7 +397,8 @@ namespace SDDM {
+ manager.UnlockSession(m_reuseSessionId);
+ manager.ActivateSession(m_reuseSessionId);
+ } else {
+- m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie());
++ if (qobject_cast<XorgDisplayServer *>(m_displayServer))
++ m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie());
+ }
+
+ // save last user and last session
+@@ -411,6 +451,10 @@ namespace SDDM {
+ // greeter
+ if (status != Auth::HELPER_AUTH_ERROR)
+ stop();
++
++ // Start the greeter again as soon as the user session is closed
++ if (m_auth->user() != QLatin1String("sddm"))
++ m_greeter->start();
+ }
+
+ void Display::slotRequestChanged() {
+diff --git a/src/daemon/Display.h b/src/daemon/Display.h
+index 61dd9f630..a31542cda 100644
+--- a/src/daemon/Display.h
++++ b/src/daemon/Display.h
+@@ -41,9 +41,18 @@ namespace SDDM {
+ Q_OBJECT
+ Q_DISABLE_COPY(Display)
+ public:
++ enum DisplayServerType {
++ X11DisplayServerType,
++ X11UserDisplayServerType
++ };
++ Q_ENUM(DisplayServerType)
++
+ explicit Display(Seat *parent);
+ ~Display();
+
++ DisplayServerType displayServerType() const;
++ DisplayServer *displayServer() const;
++
+ QString displayId() const;
+ const int terminalId() const;
+
+@@ -76,6 +85,8 @@ namespace SDDM {
+ void startAuth(const QString &user, const QString &password,
+ const Session &session);
+
++ DisplayServerType m_displayServerType = X11DisplayServerType;
++
+ bool m_relogin { true };
+ bool m_started { false };
+
+diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp
+index 436ecc3d5..158d8bbbe 100644
+--- a/src/daemon/Greeter.cpp
++++ b/src/daemon/Greeter.cpp
+@@ -27,6 +27,7 @@
+ #include "ThemeConfig.h"
+ #include "ThemeMetadata.h"
+ #include "Display.h"
++#include "XorgUserDisplayServer.h"
+
+ #include <QtCore/QDebug>
+ #include <QtCore/QProcess>
+@@ -71,6 +72,16 @@ namespace SDDM {
+ }
+ }
+
++ QString Greeter::displayServerCommand() const
++ {
++ return m_displayServerCmd;
++ }
++
++ void Greeter::setDisplayServerCommand(const QString &cmd)
++ {
++ m_displayServerCmd = cmd;
++ }
++
+ bool Greeter::start() {
+ // check flag
+ if (m_started)
+@@ -111,17 +122,16 @@ namespace SDDM {
+ // log message
+ qDebug() << "Greeter starting...";
+
+- // set process environment
++ args << QStringLiteral("--test-mode");
++
++ // set process environment
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert(QStringLiteral("DISPLAY"), m_display->name());
+ env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
+ env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme);
+- env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get());
+ m_process->setProcessEnvironment(env);
+
+ // start greeter
+- if (daemonApp->testing())
+- args << QStringLiteral("--test-mode");
+ m_process->start(QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)), args);
+
+ //if we fail to start bail immediately, and don't block in waitForStarted
+@@ -149,11 +159,12 @@ namespace SDDM {
+ m_auth->setVerbose(true);
+ connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged);
+ connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted);
++ connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady);
+ connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished);
+ connect(m_auth, &Auth::info, this, &Greeter::authInfo);
+ connect(m_auth, &Auth::error, this, &Greeter::authError);
+
+- // greeter command
++ // command
+ QStringList cmd;
+ cmd << QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR))
+ << args;
+@@ -173,8 +184,6 @@ namespace SDDM {
+ }, sysenv, env);
+
+ env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get());
+- env.insert(QStringLiteral("DISPLAY"), m_display->name());
+- env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
+ env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme);
+ env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name());
+ env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name()));
+@@ -183,11 +192,10 @@ namespace SDDM {
+ env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId()));
+ env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter"));
+ env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType());
+- env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get());
+-
+- //some themes may use KDE components and that will automatically load KDE's crash handler which we don't want
+- //counterintuitively setting this env disables that handler
+- env.insert(QStringLiteral("KDE_DEBUG"), QStringLiteral("1"));
++ if (m_display->displayServerType() == Display::X11DisplayServerType) {
++ env.insert(QStringLiteral("DISPLAY"), m_display->name());
++ env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
++ }
+ m_auth->insertEnvironment(env);
+
+ // log message
+@@ -195,6 +203,7 @@ namespace SDDM {
+
+ // start greeter
+ m_auth->setUser(QStringLiteral("sddm"));
++ m_auth->setDisplayServerCommand(m_displayServerCmd);
+ m_auth->setGreeter(true);
+ m_auth->setSession(cmd.join(QLatin1Char(' ')));
+ m_auth->start();
+@@ -261,6 +270,14 @@ namespace SDDM {
+ qDebug() << "Greeter session failed to start";
+ }
+
++ void Greeter::onDisplayServerReady(const QString &displayName)
++ {
++ auto *displayServer = m_display->displayServer();
++ auto *xorgUser = qobject_cast<XorgUserDisplayServer *>(displayServer);
++ if (xorgUser)
++ xorgUser->setDisplayName(displayName);
++ }
++
+ void Greeter::onHelperFinished(Auth::HelperExitStatus status) {
+ // reset flag
+ m_started = false;
+diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h
+index 7391a3597..bf472375f 100644
+--- a/src/daemon/Greeter.h
++++ b/src/daemon/Greeter.h
+@@ -43,6 +43,9 @@ namespace SDDM {
+ void setSocket(const QString &socket);
+ void setTheme(const QString &theme);
+
++ QString displayServerCommand() const;
++ void setDisplayServerCommand(const QString &cmd);
++
+ public slots:
+ bool start();
+ void stop();
+@@ -51,6 +54,7 @@ namespace SDDM {
+ private slots:
+ void onRequestChanged();
+ void onSessionStarted(bool success);
++ void onDisplayServerReady(const QString &displayName);
+ void onHelperFinished(Auth::HelperExitStatus status);
+ void onReadyReadStandardOutput();
+ void onReadyReadStandardError();
+@@ -64,6 +68,7 @@ namespace SDDM {
+ QString m_authPath;
+ QString m_socket;
+ QString m_themePath;
++ QString m_displayServerCmd;
+ ThemeMetadata *m_metadata { nullptr };
+ ThemeConfig *m_themeConfig { nullptr };
+
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index 331adcda7..fc61ee2dd 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -41,31 +41,9 @@
+
+ namespace SDDM {
+ XorgDisplayServer::XorgDisplayServer(Display *parent) : DisplayServer(parent) {
+- // get auth directory
+- QString authDir = QStringLiteral(RUNTIME_DIR);
+-
+- // use "." as authdir in test mode
+ if (daemonApp->testing())
+- authDir = QStringLiteral(".");
+-
+- // create auth dir if not existing
+- QDir().mkpath(authDir);
+-
+- // set auth path
+- m_authPath = QStringLiteral("%1/%2").arg(authDir).arg(QUuid::createUuid().toString());
+-
+- // generate cookie
+- std::random_device rd;
+- std::mt19937 gen(rd());
+- std::uniform_int_distribution<> dis(0, 15);
+-
+- // resever 32 bytes
+- m_cookie.reserve(32);
+-
+- // create a random hexadecimal number
+- const char *digits = "0123456789abcdef";
+- for (int i = 0; i < 32; ++i)
+- m_cookie[i] = digits[dis(gen)];
++ m_xauth.setAuthDirectory(QStringLiteral("."));
++ m_xauth.setup();
+ }
+
+ XorgDisplayServer::~XorgDisplayServer() {
+@@ -76,41 +54,16 @@ namespace SDDM {
+ return m_display;
+ }
+
+- const QString &XorgDisplayServer::authPath() const {
+- return m_authPath;
++ QString XorgDisplayServer::authPath() const {
++ return m_xauth.authPath();
+ }
+
+ QString XorgDisplayServer::sessionType() const {
+ return QStringLiteral("x11");
+ }
+
+- const QString &XorgDisplayServer::cookie() const {
+- return m_cookie;
+- }
+-
+- bool XorgDisplayServer::addCookie(const QString &file) {
+- // log message
+- qDebug() << "Adding cookie to" << file;
+-
+- // Touch file
+- QFile file_handler(file);
+- file_handler.open(QIODevice::Append);
+- file_handler.close();
+-
+- QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file);
+-
+- // execute xauth
+- FILE *fp = popen(qPrintable(cmd), "w");
+-
+- // check file
+- if (!fp)
+- return false;
+- fprintf(fp, "remove %s\n", qPrintable(m_display));
+- fprintf(fp, "add %s . %s\n", qPrintable(m_display), qPrintable(m_cookie));
+- fprintf(fp, "exit\n");
+-
+- // close pipe
+- return pclose(fp) == 0;
++ QString XorgDisplayServer::cookie() const {
++ return m_xauth.cookie();
+ }
+
+ bool XorgDisplayServer::start() {
+@@ -136,7 +89,7 @@ namespace SDDM {
+ // For the X server's copy, the display number doesn't matter.
+ // An empty file would result in no access control!
+ m_display = QStringLiteral(":0");
+- if(!addCookie(m_authPath)) {
++ if(!m_xauth.addCookie(m_display)) {
+ qCritical() << "Failed to write xauth file";
+ return false;
+ }
+@@ -159,18 +112,15 @@ namespace SDDM {
+ process->setProgram(mainConfig.X11.ServerPath.get());
+ args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts)
+ << QStringLiteral("-background") << QStringLiteral("none")
+- << QStringLiteral("-seat") << displayPtr()->seat()->name();
+-
+- if (displayPtr()->seat()->name() == QLatin1String("seat0")) {
+- args << QStringLiteral("vt%1").arg(displayPtr()->terminalId());
+- }
++ << QStringLiteral("-seat") << displayPtr()->seat()->name()
++ << QStringLiteral("vt%1").arg(displayPtr()->terminalId());
+ } else {
+ process->setProgram(mainConfig.X11.XephyrPath.get());
+ args << QStringLiteral("-br")
+ << QStringLiteral("-screen") << QStringLiteral("800x600");
+ }
+
+- args << QStringLiteral("-auth") << m_authPath
++ args << QStringLiteral("-auth") << m_xauth.authPath()
+ << QStringLiteral("-noreset")
+ << QStringLiteral("-displayfd") << QString::number(pipeFds[1]);
+
+@@ -222,13 +172,13 @@ namespace SDDM {
+ // The file is also used by the greeter, which does care about the
+ // display number. Write the proper entry, if it's different.
+ if(m_display != QStringLiteral(":0")) {
+- if(!addCookie(m_authPath)) {
++ if(!m_xauth.addCookie(m_display)) {
+ qCritical() << "Failed to write xauth file";
+ stop();
+ return false;
+ }
+ }
+- changeOwner(m_authPath);
++ changeOwner(m_xauth.authPath());
+
+ emit started();
+
+@@ -297,7 +247,7 @@ namespace SDDM {
+ displayStopScript = nullptr;
+
+ // remove authority file
+- QFile::remove(m_authPath);
++ QFile::remove(m_xauth.authPath());
+
+ // emit signal
+ emit stopped();
+@@ -316,7 +266,7 @@ namespace SDDM {
+ env.insert(QStringLiteral("DISPLAY"), m_display);
+ env.insert(QStringLiteral("HOME"), QStringLiteral("/"));
+ env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get());
+- env.insert(QStringLiteral("XAUTHORITY"), m_authPath);
++ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath());
+ env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh"));
+ env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get());
+ setCursor->setProcessEnvironment(env);
+diff --git a/src/daemon/XorgDisplayServer.h b/src/daemon/XorgDisplayServer.h
+index e97a0b531..ebf189920 100644
+--- a/src/daemon/XorgDisplayServer.h
++++ b/src/daemon/XorgDisplayServer.h
+@@ -22,6 +22,7 @@
+ #define SDDM_XORGDISPLAYSERVER_H
+
+ #include "DisplayServer.h"
++#include "XAuth.h"
+
+ class QProcess;
+
+@@ -34,13 +35,11 @@ namespace SDDM {
+ ~XorgDisplayServer();
+
+ const QString &display() const;
+- const QString &authPath() const;
++ QString authPath() const;
+
+ QString sessionType() const;
+
+- const QString &cookie() const;
+-
+- bool addCookie(const QString &file);
++ QString cookie() const;
+
+ public slots:
+ bool start();
+@@ -49,8 +48,7 @@ namespace SDDM {
+ void setupDisplay();
+
+ private:
+- QString m_authPath;
+- QString m_cookie;
++ XAuth m_xauth;
+
+ QProcess *process { nullptr };
+
+diff --git a/src/daemon/XorgUserDisplayServer.cpp b/src/daemon/XorgUserDisplayServer.cpp
+new file mode 100644
+index 000000000..1e7c008a4
+--- /dev/null
++++ b/src/daemon/XorgUserDisplayServer.cpp
+@@ -0,0 +1,102 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#include "Configuration.h"
++#include "DaemonApp.h"
++#include "Display.h"
++#include "Seat.h"
++#include "XorgUserDisplayServer.h"
++
++namespace SDDM {
++
++XorgUserDisplayServer::XorgUserDisplayServer(Display *parent)
++ : DisplayServer(parent)
++{
++}
++
++XorgUserDisplayServer::~XorgUserDisplayServer()
++{
++ stop();
++}
++
++QString XorgUserDisplayServer::sessionType() const
++{
++ return QStringLiteral("x11");
++}
++
++void XorgUserDisplayServer::setDisplayName(const QString &displayName)
++{
++ m_display = displayName;
++}
++
++QString XorgUserDisplayServer::command(Display *display)
++{
++ QStringList args;
++
++ if (daemonApp->testing()) {
++ args << mainConfig.X11.XephyrPath.get()
++ << QStringLiteral("-br")
++ << QStringLiteral("-screen") << QStringLiteral("800x600");
++ } else {
++ args << mainConfig.X11.ServerPath.get()
++ << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts)
++ << QStringLiteral("-background") << QStringLiteral("none")
++ << QStringLiteral("-seat") << display->seat()->name()
++ << QStringLiteral("-noreset")
++ << QStringLiteral("-keeptty")
++ << QStringLiteral("-novtswitch")
++ << QStringLiteral("-verbose") << QStringLiteral("3");
++ }
++
++ return args.join(QLatin1Char(' '));
++}
++
++bool XorgUserDisplayServer::start()
++{
++ // Check flag
++ if (m_started)
++ return false;
++
++ // Set flag
++ m_started = true;
++ emit started();
++
++ return true;
++}
++
++void XorgUserDisplayServer::stop()
++{
++ // Check flag
++ if (!m_started)
++ return;
++
++ // Reset flag
++ m_started = false;
++ emit stopped();
++}
++
++void XorgUserDisplayServer::finished()
++{
++}
++
++void XorgUserDisplayServer::setupDisplay()
++{
++}
++
++} // namespace SDDM
+diff --git a/src/daemon/XorgUserDisplayServer.h b/src/daemon/XorgUserDisplayServer.h
+new file mode 100644
+index 000000000..aa7cbe4a8
+--- /dev/null
++++ b/src/daemon/XorgUserDisplayServer.h
+@@ -0,0 +1,53 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#ifndef SDDM_XORGUSERDISPLAYSERVER_H
++#define SDDM_XORGUSERDISPLAYSERVER_H
++
++#include "DisplayServer.h"
++#include "XAuth.h"
++
++class QProcess;
++
++namespace SDDM {
++
++class XorgUserDisplayServer : public DisplayServer
++{
++ Q_OBJECT
++ Q_DISABLE_COPY(XorgUserDisplayServer)
++public:
++ explicit XorgUserDisplayServer(Display *parent);
++ ~XorgUserDisplayServer();
++
++ QString sessionType() const;
++
++ void setDisplayName(const QString &displayName);
++
++ static QString command(Display *display);
++
++public Q_SLOTS:
++ bool start();
++ void stop();
++ void finished();
++ void setupDisplay();
++};
++
++} // namespace SDDM
++
++#endif // SDDM_XORGUSERDISPLAYSERVER_H
+diff --git a/src/greeter/GreeterApp.cpp b/src/greeter/GreeterApp.cpp
+index 01f53fafb..b7f740f41 100644
+--- a/src/greeter/GreeterApp.cpp
++++ b/src/greeter/GreeterApp.cpp
+@@ -337,6 +337,15 @@ int main(int argc, char **argv)
+ QSurfaceFormat::setDefaultFormat(format);
+ }
+
++ // Some themes may use KDE components and that will automatically load KDE's
++ // crash handler which we don't want counterintuitively setting this env
++ // disables that handler
++ qputenv("KDE_DEBUG", "1");
++
++ // Qt IM module
++ if (!SDDM::mainConfig.InputMethod.get().isEmpty())
++ qputenv("QT_IM_MODULE", SDDM::mainConfig.InputMethod.get().toLocal8Bit().constData());
++
+ QGuiApplication app(argc, argv);
+
+ QCommandLineParser parser;
+diff --git a/src/helper/Backend.cpp b/src/helper/Backend.cpp
+index a324b39fb..9a36a62ba 100644
+--- a/src/helper/Backend.cpp
++++ b/src/helper/Backend.cpp
+@@ -54,6 +54,11 @@ namespace SDDM {
+ m_autologin = on;
+ }
+
++ void Backend::setDisplayServer(bool on)
++ {
++ m_displayServer = on;
++ }
++
+ void Backend::setGreeter(bool on) {
+ m_greeter = on;
+ }
+diff --git a/src/helper/Backend.h b/src/helper/Backend.h
+index b790e0011..915d09ca8 100644
+--- a/src/helper/Backend.h
++++ b/src/helper/Backend.h
+@@ -36,6 +36,7 @@ namespace SDDM {
+ static Backend *get(HelperApp *parent);
+
+ void setAutologin(bool on = true);
++ void setDisplayServer(bool on = true);
+ void setGreeter(bool on = true);
+
+ public slots:
+@@ -50,6 +51,7 @@ namespace SDDM {
+ Backend(HelperApp *parent);
+ HelperApp *m_app;
+ bool m_autologin { false };
++ bool m_displayServer = false;
+ bool m_greeter { false };
+ };
+ }
+diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt
+index 8914ea757..24f36eb97 100644
+--- a/src/helper/CMakeLists.txt
++++ b/src/helper/CMakeLists.txt
+@@ -10,9 +10,13 @@ set(HELPER_SOURCES
+ ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp
++ ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp
++ ${CMAKE_SOURCE_DIR}/src/common/XAuth.h
+ Backend.cpp
+ HelperApp.cpp
+ UserSession.cpp
++ xorguserhelper.cpp
++ xorguserhelper.h
+ )
+
+ # Different implementations of the VT switching code
+diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp
+index 3f92f1d8f..059e9c156 100644
+--- a/src/helper/HelperApp.cpp
++++ b/src/helper/HelperApp.cpp
+@@ -88,6 +88,16 @@ namespace SDDM {
+ m_user = args[pos + 1];
+ }
+
++ if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) {
++ if (pos >= args.length() - 1) {
++ qCritical() << "This application is not supposed to be executed manually";
++ exit(Auth::HELPER_OTHER_ERROR);
++ return;
++ }
++ m_session->setDisplayServerCommand(args[pos + 1]);
++ m_backend->setDisplayServer(true);
++ }
++
+ if ((pos = args.indexOf(QStringLiteral("--autologin"))) >= 0) {
+ m_backend->setAutologin(true);
+ }
+@@ -103,7 +113,7 @@ namespace SDDM {
+ }
+
+ connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth);
+- connect(m_session, QOverload<int>::of(&QProcess::finished), this, &HelperApp::sessionFinished);
++ connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished);
+ m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered);
+ }
+
+@@ -131,11 +141,6 @@ namespace SDDM {
+
+ if (!m_session->path().isEmpty()) {
+ env.insert(m_session->processEnvironment());
+- // Allocate a new VT for the wayland session
+- if(env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) {
+- int vtNumber = VirtualTerminal::setUpNewVt();
+- env.insert(QStringLiteral("XDG_VTNR"), QString::number(vtNumber));
+- }
+ m_session->setProcessEnvironment(env);
+
+ if (!m_backend->openSession()) {
+@@ -216,6 +221,19 @@ namespace SDDM {
+ }
+ }
+
++ void HelperApp::displayServerStarted(const QString &displayName)
++ {
++ Msg m = Msg::MSG_UNKNOWN;
++ SafeDataStream str(m_socket);
++ str << Msg::DISPLAY_SERVER_STARTED << displayName;
++ str.send();
++ str.receive();
++ str >> m;
++ if (m != DISPLAY_SERVER_STARTED) {
++ qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m;
++ }
++ }
++
+ UserSession *HelperApp::session() {
+ return m_session;
+ }
+diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h
+index 632435ee5..1a75c2d67 100644
+--- a/src/helper/HelperApp.h
++++ b/src/helper/HelperApp.h
+@@ -48,6 +48,7 @@ namespace SDDM {
+ void error(const QString &message, Auth::Error type);
+ QProcessEnvironment authenticated(const QString &user);
+ void sessionOpened(bool success);
++ void displayServerStarted(const QString &displayName);
+
+ private slots:
+ void setUp();
+diff --git a/src/helper/UserSession.cpp b/src/helper/UserSession.cpp
+index 8e36f2113..f02a42c21 100644
+--- a/src/helper/UserSession.cpp
++++ b/src/helper/UserSession.cpp
+@@ -19,10 +19,14 @@
+ *
+ */
+
++#include <QSocketNotifier>
++
+ #include "Configuration.h"
+ #include "UserSession.h"
+ #include "HelperApp.h"
+ #include "VirtualTerminal.h"
++#include "XAuth.h"
++#include "xorguserhelper.h"
+
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+@@ -37,11 +41,19 @@
+
+ namespace SDDM {
+ UserSession::UserSession(HelperApp *parent)
+- : QProcess(parent) {
+- }
+-
+- UserSession::~UserSession() {
+-
++ : QObject(parent)
++ , m_process(new QProcess(this))
++ , m_xorgUser(new XOrgUserHelper(this))
++ {
++ connect(m_process, QOverload<int>::of(&QProcess::finished), this, &UserSession::finished);
++ connect(m_xorgUser, &XOrgUserHelper::displayChanged, this, [this, parent](const QString &display) {
++ auto env = processEnvironment();
++ env.insert(QStringLiteral("DISPLAY"), m_xorgUser->display());
++ env.insert(QStringLiteral("XAUTHORITY"), m_xorgUser->xauthPath());
++ setProcessEnvironment(env);
++
++ parent->displayServerStarted(display);
++ });
+ }
+
+ bool UserSession::start() {
+@@ -49,21 +61,68 @@ namespace SDDM {
+
+ setup();
+
+- if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) {
+- QProcess::start(m_path);
+- } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) {
+- const QString cmd = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path);
+- qInfo() << "Starting:" << cmd;
+- QProcess::start(cmd);
++ if (!m_displayServerCmd.isEmpty()) {
++ m_xorgUser->setEnvironment(env);
++ if (!m_xorgUser->start(m_displayServerCmd))
++ return false;
++ }
++
++ if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) {
++ if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) {
++ qInfo() << "Starting X11 greeter session:" << m_path;
++ auto args = QProcess::splitCommand(m_path);
++ const auto program = args.takeFirst();
++ m_process->start(program, args);
++ } else {
++ const QString cmd = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path);
++ qInfo() << "Starting X11 user session:" << cmd;
++ m_process->start(mainConfig.X11.SessionCommand.get(), QStringList{m_path});
++ }
+ } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) {
+ const QString cmd = QStringLiteral("%1 %2").arg(mainConfig.Wayland.SessionCommand.get()).arg(m_path);
+- qInfo() << "Starting:" << cmd;
+- QProcess::start(cmd);
++ qInfo() << "Starting Wayland user session:" << cmd;
++ m_process->start(mainConfig.Wayland.SessionCommand.get(), QStringList{m_path});
+ } else {
+ qCritical() << "Unable to run user session: unknown session type";
+ }
+
+- return waitForStarted();
++ if (m_process->waitForStarted()) {
++ int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt();
++ VirtualTerminal::jumpToVt(vtNumber, true);
++ return true;
++ }
++
++ return false;
++ }
++
++ void UserSession::stop()
++ {
++ m_process->terminate();
++ if (!m_process->waitForFinished(5000))
++ m_process->kill();
++
++ if (!m_displayServerCmd.isEmpty())
++ m_xorgUser->stop();
++ }
++
++ QProcessEnvironment UserSession::processEnvironment() const
++ {
++ return m_process->processEnvironment();
++ }
++
++ void UserSession::setProcessEnvironment(const QProcessEnvironment &env)
++ {
++ m_process->setProcessEnvironment(env);
++ }
++
++ QString UserSession::displayServerCommand() const
++ {
++ return m_displayServerCmd;
++ }
++
++ void UserSession::setDisplayServerCommand(const QString &command)
++ {
++ m_displayServerCmd = command;
+ }
+
+ void UserSession::setPath(const QString& path) {
+@@ -74,13 +133,22 @@ namespace SDDM {
+ return m_path;
+ }
+
++ qint64 UserSession::processId() const
++ {
++ return m_process->processId();
++ }
++
+ void UserSession::setupChildProcess() {
+ // Session type
+ QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE"));
+-
+- // For Wayland sessions we leak the VT into the session as stdin so
+- // that it stays open without races
+- if (sessionType == QLatin1String("wayland")) {
++ QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS"));
++ const bool hasDisplayServer = !m_displayServerCmd.isEmpty();
++ const bool x11UserSession = sessionType == QLatin1String("x11") && sessionClass == QLatin1String("user");
++ const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user");
++
++ // When the display server is part of the session, we leak the VT into
++ // the session as stdin so that it stays open without races
++ if (hasDisplayServer || waylandUserSession) {
+ // open VT and get the fd
+ int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt();
+ QString ttyString = QStringLiteral("/dev/tty%1").arg(vtNumber);
+@@ -226,74 +294,56 @@ namespace SDDM {
+ qCritical() << "verify directory exist and has sufficient permissions";
+ exit(Auth::HELPER_OTHER_ERROR);
+ }
+- const QString homeDir = QString::fromLocal8Bit(pw.pw_dir);
+-
+- //we cannot use setStandardError file as this code is run in the child process
+- //we want to redirect after we setuid so that the log file is owned by the user
+-
+- // determine stderr log file based on session type
+- QString sessionLog = QStringLiteral("%1/%2")
+- .arg(homeDir)
+- .arg(sessionType == QLatin1String("x11")
+- ? mainConfig.X11.SessionLogFile.get()
+- : mainConfig.Wayland.SessionLogFile.get());
+-
+- // create the path
+- QFileInfo finfo(sessionLog);
+- QDir().mkpath(finfo.absolutePath());
+-
+- //swap the stderr pipe of this subprcess into a file
+- int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600);
+- if (fd >= 0)
+- {
+- dup2 (fd, STDERR_FILENO);
+- ::close(fd);
+- } else {
+- qWarning() << "Could not open stderr to" << sessionLog;
+- }
+-
+- //redirect any stdout to /dev/null
+- fd = ::open("/dev/null", O_WRONLY);
+- if (fd >= 0)
+- {
+- dup2 (fd, STDOUT_FILENO);
+- ::close(fd);
+- } else {
+- qWarning() << "Could not redirect stdout";
+- }
+
+- // set X authority for X11 sessions only
+- if (sessionType != QLatin1String("x11"))
+- return;
+- QString cookie = qobject_cast<HelperApp*>(parent())->cookie();
+- if (!cookie.isEmpty()) {
+- QString file = processEnvironment().value(QStringLiteral("XAUTHORITY"));
+- QString display = processEnvironment().value(QStringLiteral("DISPLAY"));
+- qDebug() << "Adding cookie to" << file;
++ if (sessionClass != QLatin1String("greeter")) {
++ //we cannot use setStandardError file as this code is run in the child process
++ //we want to redirect after we setuid so that the log file is owned by the user
+
++ // determine stderr log file based on session type
++ QString sessionLog = QStringLiteral("%1/%2")
++ .arg(QString::fromLocal8Bit(pw.pw_dir))
++ .arg(sessionType == QLatin1String("x11")
++ ? mainConfig.X11.SessionLogFile.get()
++ : mainConfig.Wayland.SessionLogFile.get());
+
+ // create the path
+- QFileInfo finfo(file);
++ QFileInfo finfo(sessionLog);
+ QDir().mkpath(finfo.absolutePath());
+
+- QFile file_handler(file);
+- file_handler.open(QIODevice::Append);
+- file_handler.close();
++ //swap the stderr pipe of this subprcess into a file
++ int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600);
++ if (fd >= 0)
++ {
++ dup2 (fd, STDERR_FILENO);
++ ::close(fd);
++ } else {
++ qWarning() << "Could not open stderr to" << sessionLog;
++ }
+
+- QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file);
++ //redirect any stdout to /dev/null
++ fd = ::open("/dev/null", O_WRONLY);
++ if (fd >= 0)
++ {
++ dup2 (fd, STDOUT_FILENO);
++ ::close(fd);
++ } else {
++ qWarning() << "Could not redirect stdout";
++ }
++ }
+
+- // execute xauth
+- FILE *fp = popen(qPrintable(cmd), "w");
++ // set X authority for X11 sessions only
++ if (x11UserSession) {
++ QString cookie = qobject_cast<HelperApp*>(parent())->cookie();
++ if (!cookie.isEmpty()) {
++ QString file = processEnvironment().value(QStringLiteral("XAUTHORITY"));
++ QString display = processEnvironment().value(QStringLiteral("DISPLAY"));
+
+- // check file
+- if (!fp)
+- return;
+- fprintf(fp, "remove %s\n", qPrintable(display));
+- fprintf(fp, "add %s . %s\n", qPrintable(display), qPrintable(cookie));
+- fprintf(fp, "exit\n");
++ // Create the path
++ QFileInfo finfo(file);
++ QDir().mkpath(finfo.absolutePath());
+
+- // close pipe
+- pclose(fp);
++ XAuth::addCookieToFile(display, file, cookie);
++ }
+ }
+ }
+ }
+diff --git a/src/helper/UserSession.h b/src/helper/UserSession.h
+index c2383f068..11bff1d1b 100644
+--- a/src/helper/UserSession.h
++++ b/src/helper/UserSession.h
+@@ -23,41 +23,55 @@
+ #define SDDM_AUTH_SESSION_H
+
+ #include <QtCore/QObject>
+-#include <QtCore/QString>
+ #include <QtCore/QProcess>
+
+ namespace SDDM {
+ class HelperApp;
+- class UserSession : public QProcess
++ class XOrgUserHelper;
++ class UserSession : public QObject
+ {
+ Q_OBJECT
+ public:
+ explicit UserSession(HelperApp *parent);
+- virtual ~UserSession();
+
+ bool start();
++ void stop();
++
++ QProcessEnvironment processEnvironment() const;
++ void setProcessEnvironment(const QProcessEnvironment &env);
++
++ QString displayServerCommand() const;
++ void setDisplayServerCommand(const QString &command);
+
+ void setPath(const QString &path);
+ QString path() const;
+
+ /*!
+ \brief Sets m_cachedProcessId. Needed for getting the PID of a finished UserSession
+ and calling HelperApp::utmpLogout
+ \param pid The process ID
+ */
+ void setCachedProcessId(qint64 pid);
+
+ /*!
+ \brief Gets m_cachedProcessId
+ \return The cached process ID
+ */
+ qint64 cachedProcessId();
+
++ qint64 processId() const;
++
++ Q_SIGNALS:
++ void finished(int exitCode);
++
+ protected:
+ void setupChildProcess();
+
+ private:
+ QString m_path { };
+ qint64 m_cachedProcessId;
++ QProcess *m_process = nullptr;
++ XOrgUserHelper *m_xorgUser = nullptr;
++ QString m_displayServerCmd;
+ };
+ }
+
+diff --git a/src/helper/backend/PamBackend.cpp b/src/helper/backend/PamBackend.cpp
+index f86d77d63..c97056a66 100644
+--- a/src/helper/backend/PamBackend.cpp
++++ b/src/helper/backend/PamBackend.cpp
+@@ -248,7 +248,9 @@ namespace SDDM {
+ }
+
+ QProcessEnvironment sessionEnv = m_app->session()->processEnvironment();
+- if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) {
++ const auto sessionType = sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE"));
++ const auto sessionClass = sessionEnv.value(QStringLiteral("XDG_SESSION_CLASS"));
++ if (sessionType == QLatin1String("x11") && (sessionClass == QLatin1String("user") || !m_displayServer)) {
+ QString display = sessionEnv.value(QStringLiteral("DISPLAY"));
+ if (!display.isEmpty()) {
+ #ifdef PAM_XDISPLAY
+@@ -256,7 +258,7 @@ namespace SDDM {
+ #endif
+ m_pam->setItem(PAM_TTY, qPrintable(display));
+ }
+- } else if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) {
++ } else {
+ QString tty = QStringLiteral("/dev/tty%1").arg(sessionEnv.value(QStringLiteral("XDG_VTNR")));
+ m_pam->setItem(PAM_TTY, qPrintable(tty));
+ }
+diff --git a/src/helper/xorguserhelper.cpp b/src/helper/xorguserhelper.cpp
+new file mode 100644
+index 000000000..573ed101f
+--- /dev/null
++++ b/src/helper/xorguserhelper.cpp
+@@ -0,0 +1,252 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#include <QCoreApplication>
++#include <QFile>
++#include <QStandardPaths>
++
++#include "Configuration.h"
++
++#include "xorguserhelper.h"
++
++#include <fcntl.h>
++#include <unistd.h>
++
++namespace SDDM {
++
++XOrgUserHelper::XOrgUserHelper(QObject *parent)
++ : QObject(parent)
++{
++}
++
++QProcessEnvironment XOrgUserHelper::environment() const
++{
++ return m_environment;
++}
++
++void XOrgUserHelper::setEnvironment(const QProcessEnvironment &env)
++{
++ m_environment = env;
++}
++
++QString XOrgUserHelper::display() const
++{
++ return m_display;
++}
++
++QString XOrgUserHelper::xauthPath() const
++{
++ return m_xauth.authPath();
++}
++
++bool XOrgUserHelper::start(const QString &cmd)
++{
++ // Create xauthority
++ m_xauth.setAuthDirectory(m_environment.value(QStringLiteral("XDG_RUNTIME_DIR")));
++ m_xauth.setup();
++
++ // Start server process
++ if (!startServer(cmd))
++ return false;
++
++ // Setup display
++ startDisplayCommand();
++
++ return true;
++}
++
++void XOrgUserHelper::stop()
++{
++ if (m_serverProcess) {
++ qInfo("Stopping server...");
++ m_serverProcess->terminate();
++ if (!m_serverProcess->waitForFinished(5000))
++ m_serverProcess->kill();
++ m_serverProcess->deleteLater();
++ m_serverProcess = nullptr;
++
++ displayFinished();
++ }
++}
++
++bool XOrgUserHelper::startProcess(const QString &cmd,
++ const QProcessEnvironment &env,
++ QProcess **p)
++{
++ auto args = QProcess::splitCommand(cmd);
++ const auto program = args.takeFirst();
++
++ // Make sure to forward the input of this process into the Xorg
++ // server, otherwise it will complain that only console users are allowed
++ auto *process = new QProcess(this);
++ process->setInputChannelMode(QProcess::ForwardedInputChannel);
++ process->setProcessChannelMode(QProcess::ForwardedChannels);
++ process->setProcessEnvironment(env);
++
++ connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
++ process, [](int exitCode, QProcess::ExitStatus exitStatus) {
++ if (exitCode != 0 || exitStatus != QProcess::NormalExit)
++ QCoreApplication::instance()->quit();
++ });
++
++ process->start(program, args);
++ if (!process->waitForStarted(10000)) {
++ qWarning("Failed to start \"%s\": %s",
++ qPrintable(cmd),
++ qPrintable(process->errorString()));
++ return false;
++ }
++
++ if (p)
++ *p = process;
++
++ return true;
++}
++
++bool XOrgUserHelper::startServer(const QString &cmd)
++{
++ QString serverCmd = cmd;
++
++ // Create pipe for communicating with X server
++ // 0 == read from X, 1 == write to X
++ int pipeFds[2];
++ if (::pipe(pipeFds) != 0) {
++ qCritical("Could not create pipe to start X server");
++ return false;
++ }
++
++ // Do not leak the read endpoint to the X server process
++ fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
++
++ // Server environment
++ // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges
++ // under Fedora and all distros that use their patch.
++ // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch
++ // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights
++ QProcessEnvironment serverEnv = m_environment;
++ serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1"));
++
++ // Append xauth and display fd to the command
++ auto args = QStringList()
++ << QStringLiteral("-auth") << m_xauth.authPath()
++ << QStringLiteral("-displayfd") << QString::number(pipeFds[1]);
++
++ // Append VT from environment
++ args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR")));
++
++ // Log to stdout
++ args << QStringLiteral("-logfile") << QStringLiteral("/dev/null");
++
++ // Command string
++ serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' '));
++
++ // Start the server process
++ qInfo("Running server: %s", qPrintable(serverCmd));
++ if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) {
++ ::close(pipeFds[0]);
++ return false;
++ }
++
++ // Close the other side of pipe in our process, otherwise reading
++ // from it may stuck even X server exit
++ ::close(pipeFds[1]);
++
++ // Read the display number from the pipe
++ QFile readPipe;
++ if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) {
++ qCritical("Failed to open pipe to start X Server");
++ ::close(pipeFds[0]);
++ return false;
++ }
++ QByteArray displayNumber = readPipe.readLine();
++ if (displayNumber.size() < 2) {
++ // X server gave nothing (or a whitespace)
++ qCritical("Failed to read display number from pipe");
++ ::close(pipeFds[0]);
++ return false;
++ }
++ displayNumber.prepend(QByteArray(":"));
++ displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace
++ m_display = QString::fromLocal8Bit(displayNumber);
++ qDebug("X11 display: %s", qPrintable(m_display));
++ Q_EMIT displayChanged(m_display);
++
++ // Generate xauthority file
++ // For the X server's copy, the display number doesn't matter.
++ // An empty file would result in no access control!
++ if (!m_xauth.addCookie(m_display)) {
++ qCritical("Failed to write xauth file");
++ return false;
++ }
++
++ // Close our pipe
++ ::close(pipeFds[0]);
++
++ return true;
++}
++
++void XOrgUserHelper::startDisplayCommand()
++{
++ auto env = QProcessEnvironment::systemEnvironment();
++ env.insert(QStringLiteral("DISPLAY"), m_display);
++ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath());
++
++ // Set cursor
++ qInfo("Setting default cursor...");
++ QProcess *setCursor = nullptr;
++ if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) {
++ if (!setCursor->waitForFinished(1000)) {
++ qWarning() << "Could not setup default cursor";
++ setCursor->kill();
++ }
++ setCursor->deleteLater();
++ }
++
++ // Display setup script
++ auto cmd = mainConfig.X11.DisplayCommand.get();
++ qInfo("Running display setup script: %s", qPrintable(cmd));
++ QProcess *displayScript = nullptr;
++ if (startProcess(cmd, env, &displayScript)) {
++ if (!displayScript->waitForFinished(30000))
++ displayScript->kill();
++ displayScript->deleteLater();
++ }
++}
++
++void XOrgUserHelper::displayFinished()
++{
++ auto env = QProcessEnvironment::systemEnvironment();
++ env.insert(QStringLiteral("DISPLAY"), m_display);
++ env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath());
++ env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb"));
++
++ auto cmd = mainConfig.X11.DisplayStopCommand.get();
++ qInfo("Running display stop script: %s", qPrintable(cmd));
++ QProcess *displayStopScript = nullptr;
++ if (startProcess(cmd, env, &displayStopScript)) {
++ if (!displayStopScript->waitForFinished(5000))
++ displayStopScript->kill();
++ displayStopScript->deleteLater();
++ }
++
++ // Remove xauthority file
++ QFile::remove(m_xauth.authPath());
++}
++
++} // namespace SDDM
+diff --git a/src/helper/xorguserhelper.h b/src/helper/xorguserhelper.h
+new file mode 100644
+index 000000000..73536965a
+--- /dev/null
++++ b/src/helper/xorguserhelper.h
+@@ -0,0 +1,64 @@
++/***************************************************************************
++* Copyright (c) 2021 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the
++* Free Software Foundation, Inc.,
++* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++***************************************************************************/
++
++#ifndef XORGUSERHELPER_H
++#define XORGUSERHELPER_H
++
++#include <QProcess>
++
++#include "XAuth.h"
++
++namespace SDDM {
++
++class XOrgUserHelper : public QObject
++{
++ Q_OBJECT
++ Q_PROPERTY(QString display READ display NOTIFY displayChanged)
++public:
++ explicit XOrgUserHelper(QObject *parent = nullptr);
++
++ QProcessEnvironment environment() const;
++ void setEnvironment(const QProcessEnvironment &env);
++
++ QString display() const;
++
++ QString xauthPath() const;
++
++ bool start(const QString &cmd);
++ void stop();
++
++Q_SIGNALS:
++ void displayChanged(const QString &display);
++
++private:
++ QString m_display = QStringLiteral(":0");
++ XAuth m_xauth;
++ QProcessEnvironment m_environment;
++ QProcess *m_serverProcess = nullptr;
++
++ bool startProcess(const QString &cmd, const QProcessEnvironment &env,
++ QProcess **p = nullptr);
++ bool startServer(const QString &cmd);
++ void startDisplayCommand();
++ void displayFinished();
++};
++
++} // namespace SDDM
++
++#endif // XORGUSERHELPER_H
diff --git a/user/sddm/session-env.patch b/user/sddm/session-env.patch
new file mode 100644
index 000000000..e73ab8fc4
--- /dev/null
+++ b/user/sddm/session-env.patch
@@ -0,0 +1,122 @@
+From 093e8e54c7de33c2d7332cc3a126041263e1aa71 Mon Sep 17 00:00:00 2001
+From: davidedmundson <kde@davidedmundson.co.uk>
+Date: Mon, 8 Mar 2021 07:35:55 +0000
+Subject: [PATCH] Allow addition env vars to be defined in session files
+ (#1370)
+
+We have a continual problem with developers using Plasma in custom
+prefixes. We need to set mulitple additional envs and it is a constant
+pain point.
+
+Setting XDG_DATA_DIRS and alike in a script in the Exec line of the
+desktop file doesn't work properly, as this is after systemd and dbus
+have already been started during pam_logind. It is currently very bodged
+with more manual steps.
+
+Things will work much better if we can set the env before pam_logind is
+run.
+
+Using pam_env or profile.d doesn't work as that affects all the
+sessions.
+
+It is not intended to be used for anything other than dev setups.
+---
+ src/common/Session.cpp | 24 ++++++++++++++++++++++++
+ src/common/Session.h | 5 +++++
+ src/daemon/Display.cpp | 1 +
+ 3 files changed, 30 insertions(+)
+
+diff --git a/src/common/Session.cpp b/src/common/Session.cpp
+index 3de28ef13..0aa540221 100644
+--- a/src/common/Session.cpp
++++ b/src/common/Session.cpp
+@@ -107,6 +107,10 @@ namespace SDDM {
+ return m_isNoDisplay;
+ }
+
++ QProcessEnvironment Session::additionalEnv() const {
++ return m_additionalEnv;
++ }
++
+ void Session::setTo(Type type, const QString &_fileName)
+ {
+ QString fileName(_fileName);
+@@ -178,6 +182,8 @@ namespace SDDM {
+ m_isHidden = line.mid(7).toLower() == QLatin1String("true");
+ if (line.startsWith(QLatin1String("NoDisplay=")))
+ m_isNoDisplay = line.mid(10).toLower() == QLatin1String("true");
++ if (line.startsWith(QLatin1String("X-SDDM-Env=")))
++ m_additionalEnv = parseEnv(line.mid(strlen("X-SDDM-Env=")));
+ }
+
+ file.close();
+@@ -191,4 +197,22 @@ namespace SDDM {
+ setTo(other.type(), other.fileName());
+ return *this;
+ }
++
++ QProcessEnvironment SDDM::Session::parseEnv(const QString &list)
++ {
++ QProcessEnvironment env;
++
++ const QVector<QStringRef> entryList = list.splitRef(QLatin1Char(','));
++ for (const auto &entry: entryList) {
++ int midPoint = entry.indexOf(QLatin1Char('='));
++ if (midPoint < 0) {
++ qWarning() << "Malformed entry in" << fileName() << ":" << entry;
++ continue;
++ }
++ env.insert(entry.left(midPoint).toString(), entry.mid(midPoint+1).toString());
++ }
++ return env;
++ }
++
++
+ }
+diff --git a/src/common/Session.h b/src/common/Session.h
+index 9fd86cd02..aa196e9c6 100644
+--- a/src/common/Session.h
++++ b/src/common/Session.h
+@@ -23,6 +23,7 @@
+ #include <QDataStream>
+ #include <QDir>
+ #include <QSharedPointer>
++#include <QProcessEnvironment>
+
+ namespace SDDM {
+ class SessionModel;
+@@ -59,11 +60,14 @@ namespace SDDM {
+ bool isHidden() const;
+ bool isNoDisplay() const;
+
++ QProcessEnvironment additionalEnv() const;
++
+ void setTo(Type type, const QString &name);
+
+ Session &operator=(const Session &other);
+
+ private:
++ QProcessEnvironment parseEnv(const QString &list);
+ bool m_valid;
+ Type m_type;
+ QDir m_dir;
+@@ -75,6 +79,7 @@ namespace SDDM {
+ QString m_tryExec;
+ QString m_xdgSessionType;
+ QString m_desktopNames;
++ QProcessEnvironment m_additionalEnv;
+ bool m_isHidden;
+ bool m_isNoDisplay;
+
+diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
+index 9f1fabc68..a65df3f0b 100644
+--- a/src/daemon/Display.cpp
++++ b/src/daemon/Display.cpp
+@@ -313,6 +313,7 @@ namespace SDDM {
+ qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec();
+
+ QProcessEnvironment env;
++ env.insert(session.additionalEnv());
+
+ if (seat()->name() == QLatin1String("seat0")) {
+ // Use the greeter VT, for wayland sessions the helper overwrites this
diff --git a/user/sddm/utmpx.patch b/user/sddm/utmpx.patch
index 1edd72d7d..4402fd861 100644
--- a/user/sddm/utmpx.patch
+++ b/user/sddm/utmpx.patch
@@ -1,10 +1,12 @@
---- sddm-0.18.0/src/helper/HelperApp.cpp.old 2018-07-18 10:31:40.000000000 +0000
-+++ sddm-0.18.0/src/helper/HelperApp.cpp 2018-07-25 23:11:09.840000000 +0000
-@@ -35,7 +35,6 @@
+--- sddm-0.19.0/src/helper/HelperApp.cpp.old 2020-11-03 03:47:51.000000000 -0600
++++ sddm-0.19.0/src/helper/HelperApp.cpp 2022-08-19 21:59:19.977071884 -0500
+@@ -36,9 +36,6 @@
#include <sys/socket.h>
#include <sys/time.h>
+-#if defined(Q_OS_LINUX)
-#include <utmp.h>
+-#endif
#include <utmpx.h>
#include <QByteArray>
diff --git a/user/sddm/xephyr.patch b/user/sddm/xephyr.patch
new file mode 100644
index 000000000..142520ad4
--- /dev/null
+++ b/user/sddm/xephyr.patch
@@ -0,0 +1,171 @@
+From 68cc9e31d1a4c4609f42114782fc485cb07353a4 Mon Sep 17 00:00:00 2001
+From: Fabian Vogt <fabian@ritter-vogt.de>
+Date: Fri, 9 Oct 2020 21:06:01 +0200
+Subject: [PATCH] Merge normal and testing paths in XorgDisplayServer::start
+
+They have much in common and this means that Xephyr can also make use use
+of -displayfd now.
+---
+ src/daemon/XorgDisplayServer.cpp | 132 ++++++++++++++-----------------
+ 1 file changed, 60 insertions(+), 72 deletions(-)
+
+diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp
+index d5f29a94a..e60c02210 100644
+--- a/src/daemon/XorgDisplayServer.cpp
++++ b/src/daemon/XorgDisplayServer.cpp
+@@ -136,95 +136,83 @@ namespace SDDM {
+ return false;
+ }
+
+- if (daemonApp->testing()) {
+- QStringList args;
+- QDir x11socketDir(QStringLiteral("/tmp/.X11-unix"));
+- int display = 100;
+- while (x11socketDir.exists(QStringLiteral("X%1").arg(display))) {
+- ++display;
+- }
+- m_display = QStringLiteral(":%1").arg(display);
+- args << m_display << QStringLiteral("-auth") << m_authPath << QStringLiteral("-br") << QStringLiteral("-noreset") << QStringLiteral("-screen") << QStringLiteral("800x600");
+- process->start(mainConfig.X11.XephyrPath.get(), args);
+-
+-
+- // wait for display server to start
+- if (!process->waitForStarted()) {
+- // log message
+- qCritical() << "Failed to start display server process.";
++ // set process environment
++ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
++ env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get());
++ process->setProcessEnvironment(env);
+
+- // return fail
+- return false;
+- }
+- emit started();
+- } else {
+- // set process environment
+- QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+- env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get());
+- process->setProcessEnvironment(env);
+-
+- //create pipe for communicating with X server
+- //0 == read from X, 1== write to from X
+- int pipeFds[2];
+- if (pipe(pipeFds) != 0) {
+- qCritical("Could not create pipe to start X server");
+- }
++ //create pipe for communicating with X server
++ //0 == read from X, 1== write to from X
++ int pipeFds[2];
++ if (pipe(pipeFds) != 0) {
++ qCritical("Could not create pipe to start X server");
++ }
+
+- // start display server
+- QStringList args = mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts);
+- args << QStringLiteral("-auth") << m_authPath
++ // start display server
++ QStringList args;
++ if (!daemonApp->testing()) {
++ process->setProgram(mainConfig.X11.ServerPath.get());
++ args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts)
+ << QStringLiteral("-background") << QStringLiteral("none")
+- << QStringLiteral("-noreset")
+- << QStringLiteral("-displayfd") << QString::number(pipeFds[1])
+ << QStringLiteral("-seat") << displayPtr()->seat()->name();
+
+ if (displayPtr()->seat()->name() == QLatin1String("seat0")) {
+ args << QStringLiteral("vt%1").arg(displayPtr()->terminalId());
+ }
+- qDebug() << "Running:"
+- << qPrintable(mainConfig.X11.ServerPath.get())
+- << qPrintable(args.join(QLatin1Char(' ')));
+- process->start(mainConfig.X11.ServerPath.get(), args);
+-
+- // wait for display server to start
+- if (!process->waitForStarted()) {
+- // log message
+- qCritical() << "Failed to start display server process.";
+-
+- // return fail
+- close(pipeFds[0]);
+- return false;
+- }
++ } else {
++ process->setProgram(mainConfig.X11.XephyrPath.get());
++ args << QStringLiteral("-br")
++ << QStringLiteral("-screen") << QStringLiteral("800x600");
++ }
+
+- // close the other side of pipe in our process, otherwise reading
+- // from it may stuck even X server exit.
+- close(pipeFds[1]);
++ args << QStringLiteral("-auth") << m_authPath
++ << QStringLiteral("-noreset")
++ << QStringLiteral("-displayfd") << QString::number(pipeFds[1]);
+
+- QFile readPipe;
++ process->setArguments(args);
++ qDebug() << "Running:"
++ << qPrintable(process->program())
++ << qPrintable(process->arguments().join(QLatin1Char(' ')));
++ process->start();
+
+- if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) {
+- qCritical("Failed to open pipe to start X Server");
++ // wait for display server to start
++ if (!process->waitForStarted()) {
++ // log message
++ qCritical() << "Failed to start display server process.";
+
+- close(pipeFds[0]);
+- return false;
+- }
+- QByteArray displayNumber = readPipe.readLine();
+- if (displayNumber.size() < 2) {
+- // X server gave nothing (or a whitespace).
+- qCritical("Failed to read display number from pipe");
++ // return fail
++ close(pipeFds[0]);
++ return false;
++ }
+
+- close(pipeFds[0]);
+- return false;
+- }
+- displayNumber.prepend(QByteArray(":"));
+- displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace
+- m_display = QString::fromLocal8Bit(displayNumber);
++ // close the other side of pipe in our process, otherwise reading
++ // from it may stuck even X server exit.
++ close(pipeFds[1]);
++
++ QFile readPipe;
++
++ if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) {
++ qCritical("Failed to open pipe to start X Server");
+
+- // close our pipe
+ close(pipeFds[0]);
++ return false;
++ }
++ QByteArray displayNumber = readPipe.readLine();
++ if (displayNumber.size() < 2) {
++ // X server gave nothing (or a whitespace).
++ qCritical("Failed to read display number from pipe");
+
+- emit started();
++ close(pipeFds[0]);
++ return false;
+ }
++ displayNumber.prepend(QByteArray(":"));
++ displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace
++ m_display = QString::fromLocal8Bit(displayNumber);
++
++ // close our pipe
++ close(pipeFds[0]);
++
++ emit started();
+
+ // The file is also used by the greeter, which does care about the
+ // display number. Write the proper entry, if it's different.