[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.
This commit is contained in:
Martin Gräßlin 2015-05-21 15:11:11 +02:00
parent 9954f1167f
commit 64e01ac2ca
2 changed files with 67 additions and 55 deletions

View file

@ -60,7 +60,6 @@ static void sighandler(int)
QApplication::exit(); QApplication::exit();
} }
static int startXServer(int waylandSocket, int wmFd);
static void readDisplay(int pipe); static void readDisplay(int pipe);
//************************************ //************************************
@ -79,6 +78,10 @@ ApplicationWayland::~ApplicationWayland()
Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
xcb_disconnect(x11Connection()); xcb_disconnect(x11Connection());
} }
if (m_xwaylandProcess) {
m_xwaylandProcess->terminate();
m_xwaylandProcess->waitForFinished();
}
} }
void ApplicationWayland::performStartup() void ApplicationWayland::performStartup()
@ -118,26 +121,7 @@ void ApplicationWayland::continueStartupWithScreens()
} }
createCompositor(); createCompositor();
int sx[2]; startXwaylandServer();
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<void> *watcher = new QFutureWatcher<void>(this);
QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection);
QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater, Qt::QueuedConnection);
watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe));
} }
void ApplicationWayland::continueStartupWithX() void ApplicationWayland::continueStartupWithX()
@ -264,49 +248,73 @@ bool ApplicationWayland::notify(QObject *o, QEvent *e)
return Application::notify(o, e); return Application::notify(o, e);
} }
/** void ApplicationWayland::startXwaylandServer()
* Starts the Xwayland-Server.
* The new process is started by forking into it.
**/
static int startXServer(int waylandSocket, int wmFd)
{ {
int pipeFds[2]; int pipeFds[2];
if (pipe(pipeFds) != 0) { if (pipe(pipeFds) != 0) {
std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl; std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl;
exit(1); exit(1);
return;
} }
int sx[2];
pid_t pid = fork(); if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
if (pid == 0) { std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
// child process - should be turned into X-Server exit(1);
// writes to pipe, closes read side return;
close(pipeFds[0]); }
char fdbuf[16]; int fd = dup(sx[1]);
sprintf(fdbuf, "%d", pipeFds[1]); if (fd < 0) {
char wmfdbuf[16]; std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
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]);
exit(20); 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<void (QProcess::*)(QProcess::ProcessError)>(&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<void> *watcher = new QFutureWatcher<void>(this);
QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection);
QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::deleteLater, Qt::QueuedConnection);
watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe));
}
);
m_xwaylandProcess->start();
close(pipeFds[1]); close(pipeFds[1]);
return pipeFds[0];
} }
static void readDisplay(int pipe) static void readDisplay(int pipe)

View file

@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h" #include "main.h"
#include <QtCore/private/qeventdispatcher_unix_p.h> #include <QtCore/private/qeventdispatcher_unix_p.h>
class QProcess;
namespace KWin namespace KWin
{ {
@ -49,10 +51,12 @@ private:
void createX11Connection(); void createX11Connection();
void continueStartupWithScreens(); void continueStartupWithScreens();
void continueStartupWithX(); void continueStartupWithX();
void startXwaylandServer();
bool m_startXWayland = false; bool m_startXWayland = false;
int m_xcbConnectionFd = -1; int m_xcbConnectionFd = -1;
QStringList m_applicationsToStart; QStringList m_applicationsToStart;
QProcess *m_xwaylandProcess = nullptr;
}; };
class EventDispatcher : public QEventDispatcherUNIX class EventDispatcher : public QEventDispatcherUNIX