From 64e01ac2ca63de04fcf35d0f7beb8e95066688af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 21 May 2015 15:11:11 +0200 Subject: [PATCH] [wayland] Use QProcess to start XWayland server The code didn't use QProcess as in the early days of main_wayland.cpp Xwayland had to be started before the QGuiApplication was constructed. As this requirement doesn't exist any more we can use QProcess and improve the interaction by e.g. provide useful error messages if Xwayland couldn't be started. --- main_wayland.cpp | 118 +++++++++++++++++++++++++---------------------- main_wayland.h | 4 ++ 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/main_wayland.cpp b/main_wayland.cpp index 664ffe7149..dd3dcee711 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -60,7 +60,6 @@ static void sighandler(int) QApplication::exit(); } -static int startXServer(int waylandSocket, int wmFd); static void readDisplay(int pipe); //************************************ @@ -79,6 +78,10 @@ ApplicationWayland::~ApplicationWayland() Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); xcb_disconnect(x11Connection()); } + if (m_xwaylandProcess) { + m_xwaylandProcess->terminate(); + m_xwaylandProcess->waitForFinished(); + } } void ApplicationWayland::performStartup() @@ -118,26 +121,7 @@ void ApplicationWayland::continueStartupWithScreens() } createCompositor(); - int sx[2]; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { - std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; - exit(1); - return; - } - - const int fd = waylandServer()->createXWaylandConnection(); - if (fd == -1) { - std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; - exit(1); - return; - } - - m_xcbConnectionFd = sx[0]; - const int xDisplayPipe = startXServer(fd, sx[1]); - QFutureWatcher *watcher = new QFutureWatcher(this); - QObject::connect(watcher, &QFutureWatcher::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection); - QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection); - watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); + startXwaylandServer(); } void ApplicationWayland::continueStartupWithX() @@ -264,49 +248,73 @@ bool ApplicationWayland::notify(QObject *o, QEvent *e) return Application::notify(o, e); } -/** - * Starts the Xwayland-Server. - * The new process is started by forking into it. - **/ -static int startXServer(int waylandSocket, int wmFd) +void ApplicationWayland::startXwaylandServer() { int pipeFds[2]; if (pipe(pipeFds) != 0) { std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl; exit(1); + return; } - - pid_t pid = fork(); - if (pid == 0) { - // child process - should be turned into X-Server - // writes to pipe, closes read side - close(pipeFds[0]); - char fdbuf[16]; - sprintf(fdbuf, "%d", pipeFds[1]); - char wmfdbuf[16]; - int fd = dup(wmFd); - if (fd < 0) { - std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; - exit(20); - return -1; - } - sprintf(wmfdbuf, "%d", fd); - - int wlfd = dup(waylandSocket); - if (wlfd < 0) { - std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; - exit(20); - return -1; - } - qputenv("WAYLAND_SOCKET", QByteArray::number(wlfd)); - execlp("Xwayland", "Xwayland", "-displayfd", fdbuf, "-rootless", "-wm", wmfdbuf, (char *)0); - close(pipeFds[1]); + int sx[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { + std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; + exit(1); + return; + } + int fd = dup(sx[1]); + if (fd < 0) { + std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; exit(20); + return; } - // parent process - this is KWin - // reads from pipe, closes write side + + const int waylandSocket = waylandServer()->createXWaylandConnection(); + if (waylandSocket == -1) { + std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; + exit(1); + return; + } + const int wlfd = dup(waylandSocket); + if (wlfd < 0) { + std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; + exit(20); + return; + } + + m_xcbConnectionFd = sx[0]; + + m_xwaylandProcess = new QProcess(kwinApp()); + m_xwaylandProcess->setProgram(QStringLiteral("Xwayland")); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd)); + m_xwaylandProcess->setProcessEnvironment(env); + m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), + QString::number(pipeFds[1]), + QStringLiteral("-rootless"), + QStringLiteral("-wm"), + QString::number(fd)}); + connect(m_xwaylandProcess, static_cast(&QProcess::error), this, + [] (QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) { + std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl; + } else { + std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl; + } + exit(1); + } + ); + const int xDisplayPipe = pipeFds[0]; + connect(m_xwaylandProcess, &QProcess::started, this, + [this, xDisplayPipe] { + QFutureWatcher *watcher = new QFutureWatcher(this); + QObject::connect(watcher, &QFutureWatcher::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection); + QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection); + watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); + } + ); + m_xwaylandProcess->start(); close(pipeFds[1]); - return pipeFds[0]; } static void readDisplay(int pipe) diff --git a/main_wayland.h b/main_wayland.h index dc5257239f..f77ebd62b1 100644 --- a/main_wayland.h +++ b/main_wayland.h @@ -22,6 +22,8 @@ along with this program. If not, see . #include "main.h" #include +class QProcess; + namespace KWin { @@ -49,10 +51,12 @@ private: void createX11Connection(); void continueStartupWithScreens(); void continueStartupWithX(); + void startXwaylandServer(); bool m_startXWayland = false; int m_xcbConnectionFd = -1; QStringList m_applicationsToStart; + QProcess *m_xwaylandProcess = nullptr; }; class EventDispatcher : public QEventDispatcherUNIX