kwin/autotests/wayland/kwin_wayland_test.cpp
Martin Gräßlin 7996d954c5 Provide the platform() -> AbstractBackend* in KWin::Application
Summary:
This is the first change in a refactoring series. The aim is to:
* rename AbstractBackend to Platform
* move backends/ to plugins/platforms/
* don't bind platforms to Wayland only
* provide a platform plugin for "normal" X11
* share more code between X11 and Wayland

This change moves the platform/backend from waylandServer to Application.
The init of the plugin happens directly in the Application from the
KPluginMetaData. There is no need to externally init it and set the
parent.

WaylandServer::backend() currently just delegates to
kwinApp()->platform(), the idea is to drop this method completely.

The test infrastructure is also adjusted to this change.

Test Plan: kwin_wayland still works, all tests pass

Reviewers: #plasma, sebas

Subscribers: plasma-devel

Projects: #plasma

Differential Revision: https://phabricator.kde.org/D1331
2016-04-07 08:00:12 +02:00

279 lines
9.7 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
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, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "kwin_wayland_test.h"
#include "../../abstract_backend.h"
#include "../../effects.h"
#include "../../wayland_server.h"
#include "../../workspace.h"
#include "../../xcbutils.h"
#include <KPluginMetaData>
#include <QAbstractEventDispatcher>
#include <QPluginLoader>
#include <QSocketNotifier>
#include <QThread>
#include <QtConcurrentRun>
// system
#include <unistd.h>
#include <sys/socket.h>
#include <iostream>
namespace KWin
{
static void readDisplay(int pipe);
WaylandTestApplication::WaylandTestApplication(int &argc, char **argv)
: Application(OperationModeXwayland, argc, argv)
{
#ifdef KWIN_BUILD_ACTIVITIES
setUseKActivities(false);
#endif
qputenv("KWIN_COMPOSE", QByteArrayLiteral("Q"));
initPlatform(KPluginMetaData(QStringLiteral(KWINBACKENDPATH)));
WaylandServer::create(this);
}
WaylandTestApplication::~WaylandTestApplication()
{
waylandServer()->backend()->setOutputsEnabled(false);
destroyWorkspace();
waylandServer()->dispatch();
// need to unload all effects prior to destroying X connection as they might do X calls
if (effects) {
static_cast<EffectsHandlerImpl*>(effects)->unloadAllEffects();
}
disconnect(m_xwaylandFailConnection);
if (x11Connection()) {
Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
destroyAtoms();
emit x11ConnectionAboutToBeDestroyed();
xcb_disconnect(x11Connection());
setX11Connection(nullptr);
}
if (m_xwaylandProcess) {
m_xwaylandProcess->terminate();
while (m_xwaylandProcess->state() != QProcess::NotRunning) {
processEvents(QEventLoop::WaitForMoreEvents);
}
waylandServer()->destroyXWaylandConnection();
}
waylandServer()->terminateClientConnections();
destroyCompositor();
}
void WaylandTestApplication::performStartup()
{
// first load options - done internally by a different thread
createOptions();
waylandServer()->createInternalConnection();
// try creating the Wayland Backend
createInput();
createBackend();
}
void WaylandTestApplication::createBackend()
{
AbstractBackend *backend = waylandServer()->backend();
connect(backend, &AbstractBackend::screensQueried, this, &WaylandTestApplication::continueStartupWithScreens);
connect(backend, &AbstractBackend::initFailed, this,
[] () {
std::cerr << "FATAL ERROR: backend failed to initialize, exiting now" << std::endl;
::exit(1);
}
);
backend->init();
}
void WaylandTestApplication::continueStartupWithScreens()
{
disconnect(waylandServer()->backend(), &AbstractBackend::screensQueried, this, &WaylandTestApplication::continueStartupWithScreens);
createScreens();
waylandServer()->initOutputs();
createCompositor();
startXwaylandServer();
}
void WaylandTestApplication::continueStartupWithX()
{
createX11Connection();
xcb_connection_t *c = x11Connection();
if (!c) {
// about to quit
return;
}
QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(c), QSocketNotifier::Read, this);
auto processXcbEvents = [this, c] {
while (auto event = xcb_poll_for_event(c)) {
updateX11Time(event);
long result = 0;
if (QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result)) {
free(event);
continue;
}
if (Workspace::self()) {
Workspace::self()->workspaceEvent(event);
}
free(event);
}
xcb_flush(c);
};
connect(notifier, &QSocketNotifier::activated, this, processXcbEvents);
connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
// create selection owner for WM_S0 - magic X display number expected by XWayland
KSelectionOwner owner("WM_S0", c, x11RootWindow());
owner.claim(true);
createAtoms();
setupEventFilters();
// Check whether another windowmanager is running
const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
ScopedCPointer<xcb_generic_error_t> redirectCheck(xcb_request_check(connection(),
xcb_change_window_attributes_checked(connection(),
rootWindow(),
XCB_CW_EVENT_MASK,
maskValues)));
if (!redirectCheck.isNull()) {
::exit(1);
}
createWorkspace();
Xcb::sync(); // Trigger possible errors, there's still a chance to abort
}
void WaylandTestApplication::createX11Connection()
{
int screenNumber = 0;
xcb_connection_t *c = nullptr;
if (m_xcbConnectionFd == -1) {
c = xcb_connect(nullptr, &screenNumber);
} else {
c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr);
}
if (int error = xcb_connection_has_error(c)) {
std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl;
exit(1);
return;
}
setX11Connection(c);
// we don't support X11 multi-head in Wayland
setX11ScreenNumber(screenNumber);
setX11RootWindow(defaultScreen()->root);
}
void WaylandTestApplication::startXwaylandServer()
{
int pipeFds[2];
if (pipe(pipeFds) != 0) {
std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl;
exit(1);
return;
}
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;
}
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)});
m_xwaylandFailConnection = 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, &WaylandTestApplication::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]);
}
static void readDisplay(int pipe)
{
QFile readPipe;
if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl;
exit(1);
}
QByteArray displayNumber = readPipe.readLine();
displayNumber.prepend(QByteArray(":"));
displayNumber.remove(displayNumber.size() -1, 1);
std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
setenv("DISPLAY", displayNumber.constData(), true);
// close our pipe
close(pipe);
}
}