[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.
icc-effect-5.14.5
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();
}
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<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));
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<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]);
return pipeFds[0];
}
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 <QtCore/private/qeventdispatcher_unix_p.h>
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