2014-08-12 11:08:48 +04:00
|
|
|
/********************************************************************
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2014 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 "main_wayland.h"
|
2015-02-20 11:33:36 +03:00
|
|
|
#include "workspace.h"
|
2014-08-12 11:08:48 +04:00
|
|
|
#include <config-kwin.h>
|
|
|
|
// kwin
|
2014-08-12 11:45:43 +04:00
|
|
|
#include "wayland_backend.h"
|
2015-02-09 15:28:37 +03:00
|
|
|
#include "wayland_server.h"
|
2014-08-12 11:08:48 +04:00
|
|
|
#include "xcbutils.h"
|
|
|
|
|
2015-02-09 15:28:37 +03:00
|
|
|
// KWayland
|
|
|
|
#include <KWayland/Server/display.h>
|
2014-08-12 11:08:48 +04:00
|
|
|
// KDE
|
|
|
|
#include <KLocalizedString>
|
|
|
|
// Qt
|
|
|
|
#include <qplatformdefs.h>
|
|
|
|
#include <QCommandLineParser>
|
2015-02-20 11:33:36 +03:00
|
|
|
#include <QtConcurrentRun>
|
2014-08-21 13:30:56 +04:00
|
|
|
#include <QFile>
|
2015-02-20 11:33:36 +03:00
|
|
|
#include <QFutureWatcher>
|
|
|
|
#include <QtCore/private/qeventdispatcher_unix_p.h>
|
|
|
|
#include <QSocketNotifier>
|
|
|
|
#include <QThread>
|
|
|
|
#include <QDebug>
|
2015-02-25 13:42:56 +03:00
|
|
|
#include <QWindow>
|
2014-08-12 11:08:48 +04:00
|
|
|
|
|
|
|
// system
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif // HAVE_UNISTD_H
|
|
|
|
|
2014-08-12 17:27:35 +04:00
|
|
|
#include <iostream>
|
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2014-08-13 15:13:44 +04:00
|
|
|
static void sighandler(int)
|
|
|
|
{
|
|
|
|
QApplication::exit();
|
|
|
|
}
|
|
|
|
|
2015-02-25 16:15:32 +03:00
|
|
|
static int startXServer(int waylandSocket, int wmFd);
|
2015-02-20 13:53:25 +03:00
|
|
|
static void readDisplay(int pipe);
|
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
//************************************
|
|
|
|
// ApplicationWayland
|
|
|
|
//************************************
|
|
|
|
|
|
|
|
ApplicationWayland::ApplicationWayland(int &argc, char **argv)
|
|
|
|
: Application(OperationModeWaylandAndX11, argc, argv)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplicationWayland::~ApplicationWayland()
|
|
|
|
{
|
|
|
|
destroyWorkspace();
|
2014-08-12 11:45:43 +04:00
|
|
|
delete Wayland::WaylandBackend::self();
|
2015-02-20 11:33:36 +03:00
|
|
|
if (x11Connection()) {
|
|
|
|
Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
|
|
|
|
xcb_disconnect(x11Connection());
|
|
|
|
}
|
2014-08-12 11:08:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ApplicationWayland::performStartup()
|
|
|
|
{
|
2015-02-20 16:57:06 +03:00
|
|
|
setOperationMode(m_startXWayland ? OperationModeXwayland : OperationModeWaylandAndX11);
|
2015-02-23 17:57:00 +03:00
|
|
|
// first load options - done internally by a different thread
|
|
|
|
createOptions();
|
2015-02-20 16:57:06 +03:00
|
|
|
|
|
|
|
// try creating the Wayland Backend
|
|
|
|
createInput();
|
|
|
|
Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create();
|
|
|
|
connect(backend, &Wayland::WaylandBackend::connectionFailed, this,
|
|
|
|
[] () {
|
|
|
|
fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr);
|
|
|
|
::exit(1);
|
2015-02-20 13:53:25 +03:00
|
|
|
}
|
2015-02-20 16:57:06 +03:00
|
|
|
);
|
|
|
|
connect(backend, &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
|
|
|
}
|
2015-02-20 13:53:25 +03:00
|
|
|
|
2015-02-20 16:57:06 +03:00
|
|
|
void ApplicationWayland::continueStartupWithScreens()
|
|
|
|
{
|
|
|
|
disconnect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
|
|
|
createScreens();
|
|
|
|
waylandServer()->initOutputs();
|
|
|
|
|
|
|
|
if (!m_startXWayland) {
|
2015-02-20 13:53:25 +03:00
|
|
|
continueStartupWithX();
|
2015-02-20 16:57:06 +03:00
|
|
|
return;
|
2015-02-20 13:53:25 +03:00
|
|
|
}
|
2015-02-23 17:57:00 +03:00
|
|
|
createCompositor();
|
2015-02-20 16:57:06 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-25 16:15:32 +03:00
|
|
|
const int fd = waylandServer()->createXWaylandConnection();
|
|
|
|
if (fd == -1) {
|
|
|
|
std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl;
|
|
|
|
exit(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-20 16:57:06 +03:00
|
|
|
m_xcbConnectionFd = sx[0];
|
2015-02-25 16:15:32 +03:00
|
|
|
const int xDisplayPipe = startXServer(fd, sx[1]);
|
2015-02-20 16:57:06 +03:00
|
|
|
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));
|
2015-02-20 13:53:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ApplicationWayland::continueStartupWithX()
|
|
|
|
{
|
|
|
|
createX11Connection();
|
2015-02-20 11:33:36 +03:00
|
|
|
xcb_connection_t *c = x11Connection();
|
2015-02-20 13:53:25 +03:00
|
|
|
if (!c) {
|
|
|
|
// about to quit
|
|
|
|
return;
|
|
|
|
}
|
2015-02-20 11:33:36 +03:00
|
|
|
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);
|
|
|
|
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);
|
2014-08-12 11:08:48 +04:00
|
|
|
|
2015-02-20 13:53:25 +03:00
|
|
|
// create selection owner for WM_S0 - magic X display number expected by XWayland
|
|
|
|
KSelectionOwner owner("WM_S0", c, x11RootWindow());
|
|
|
|
owner.claim(true);
|
2014-08-12 11:08:48 +04:00
|
|
|
|
|
|
|
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()) {
|
2014-08-12 11:45:43 +04:00
|
|
|
fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr);
|
|
|
|
::exit(1);
|
|
|
|
}
|
|
|
|
|
2015-02-25 13:42:56 +03:00
|
|
|
// HACK: create a QWindow in a thread to force QtWayland to create the client buffer integration
|
|
|
|
// this performs an eglInitialize which would block as it does a roundtrip to the Wayland server
|
|
|
|
// in the main thread. By moving into a thread we get the initialize without hitting the problem
|
|
|
|
// This needs to be done before creating the Workspace as from inside Workspace the dangerous code
|
|
|
|
// gets hit in the main thread
|
|
|
|
QFutureWatcher<void> *eglInitWatcher = new QFutureWatcher<void>(this);
|
|
|
|
connect(eglInitWatcher, &QFutureWatcher<void>::finished, this,
|
|
|
|
[this, eglInitWatcher] {
|
|
|
|
eglInitWatcher->deleteLater();
|
2014-08-12 11:08:48 +04:00
|
|
|
|
2015-02-25 13:42:56 +03:00
|
|
|
createWorkspace();
|
2014-08-12 11:08:48 +04:00
|
|
|
|
2015-02-25 13:42:56 +03:00
|
|
|
Xcb::sync(); // Trigger possible errors, there's still a chance to abort
|
|
|
|
|
|
|
|
notifyKSplash();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
eglInitWatcher->setFuture(QtConcurrent::run([] {
|
|
|
|
QWindow w;
|
|
|
|
w.setSurfaceType(QSurface::RasterGLSurface);
|
|
|
|
w.create();
|
|
|
|
}));
|
2014-08-12 11:08:48 +04:00
|
|
|
}
|
|
|
|
|
2015-02-20 13:53:25 +03:00
|
|
|
void ApplicationWayland::createX11Connection()
|
2015-02-20 11:33:36 +03:00
|
|
|
{
|
|
|
|
int screenNumber = 0;
|
|
|
|
xcb_connection_t *c = nullptr;
|
2015-02-20 13:53:25 +03:00
|
|
|
if (m_xcbConnectionFd == -1) {
|
2015-02-20 11:33:36 +03:00
|
|
|
c = xcb_connect(nullptr, &screenNumber);
|
|
|
|
} else {
|
2015-02-20 13:53:25 +03:00
|
|
|
c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr);
|
2015-02-20 11:33:36 +03:00
|
|
|
}
|
2015-02-20 13:53:25 +03:00
|
|
|
if (int error = xcb_connection_has_error(c)) {
|
|
|
|
std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl;
|
2015-02-20 11:33:36 +03:00
|
|
|
exit(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setX11Connection(c);
|
|
|
|
// we don't support X11 multi-head in Wayland
|
|
|
|
setX11ScreenNumber(screenNumber);
|
|
|
|
setX11RootWindow(defaultScreen()->root);
|
|
|
|
}
|
|
|
|
|
2014-08-12 17:27:35 +04:00
|
|
|
/**
|
2015-02-09 15:40:01 +03:00
|
|
|
* Starts the Xwayland-Server.
|
2014-08-12 17:27:35 +04:00
|
|
|
* The new process is started by forking into it.
|
|
|
|
**/
|
2015-02-25 16:15:32 +03:00
|
|
|
static int startXServer(int waylandSocket, int wmFd)
|
2014-08-12 17:27:35 +04:00
|
|
|
{
|
|
|
|
int pipeFds[2];
|
|
|
|
if (pipe(pipeFds) != 0) {
|
2015-02-09 15:40:01 +03:00
|
|
|
std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl;
|
2014-08-12 17:27:35 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
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]);
|
2015-02-20 11:33:36 +03:00
|
|
|
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);
|
2015-02-25 16:15:32 +03:00
|
|
|
|
|
|
|
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));
|
2015-02-20 11:33:36 +03:00
|
|
|
execlp("Xwayland", "Xwayland", "-displayfd", fdbuf, "-rootless", "-wm", wmfdbuf, (char *)0);
|
2014-08-12 17:27:35 +04:00
|
|
|
close(pipeFds[1]);
|
|
|
|
exit(20);
|
|
|
|
}
|
|
|
|
// parent process - this is KWin
|
|
|
|
// reads from pipe, closes write side
|
|
|
|
close(pipeFds[1]);
|
2015-02-09 15:28:37 +03:00
|
|
|
return pipeFds[0];
|
|
|
|
}
|
2014-08-12 17:27:35 +04:00
|
|
|
|
2015-02-09 15:28:37 +03:00
|
|
|
static void readDisplay(int pipe)
|
|
|
|
{
|
2014-08-12 17:27:35 +04:00
|
|
|
QFile readPipe;
|
2015-02-09 15:28:37 +03:00
|
|
|
if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
|
|
|
|
std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl;
|
2014-08-12 17:27:35 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
QByteArray displayNumber = readPipe.readLine();
|
|
|
|
|
|
|
|
displayNumber.prepend(QByteArray(":"));
|
|
|
|
displayNumber.remove(displayNumber.size() -1, 1);
|
2014-11-07 16:58:21 +03:00
|
|
|
std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
|
2014-08-12 17:27:35 +04:00
|
|
|
|
|
|
|
setenv("DISPLAY", displayNumber.constData(), true);
|
|
|
|
|
|
|
|
// close our pipe
|
2015-02-09 15:28:37 +03:00
|
|
|
close(pipe);
|
2014-08-12 17:27:35 +04:00
|
|
|
}
|
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
KWIN_EXPORT int kdemain(int argc, char * argv[])
|
|
|
|
{
|
2015-02-09 15:40:01 +03:00
|
|
|
// process command line arguments to figure out whether we have to start Xwayland and the Wayland socket
|
2015-02-09 15:28:37 +03:00
|
|
|
QByteArray waylandSocket;
|
2014-08-12 17:27:35 +04:00
|
|
|
for (int i = 1; i < argc; ++i) {
|
2015-01-24 05:13:45 +03:00
|
|
|
QByteArray arg = QByteArray::fromRawData(argv[i], qstrlen(argv[i]));
|
2015-02-09 15:28:37 +03:00
|
|
|
if (arg == "--socket" || arg == "-s") {
|
|
|
|
if (++i < argc) {
|
|
|
|
waylandSocket = QByteArray::fromRawData(argv[i], qstrlen(argv[i]));
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (arg.startsWith("--socket=")) {
|
|
|
|
waylandSocket = arg.mid(9);
|
|
|
|
}
|
2014-08-12 17:27:35 +04:00
|
|
|
}
|
|
|
|
|
2015-02-20 11:33:36 +03:00
|
|
|
// set our own event dispatcher to be able to dispatch events before the event loop is started
|
|
|
|
QAbstractEventDispatcher *eventDispatcher = new QEventDispatcherUNIX();
|
|
|
|
QCoreApplication::setEventDispatcher(eventDispatcher);
|
2015-02-09 15:28:37 +03:00
|
|
|
KWin::WaylandServer *server = KWin::WaylandServer::create(nullptr);
|
|
|
|
server->init(waylandSocket);
|
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
KWin::Application::setupMalloc();
|
|
|
|
KWin::Application::setupLocalizedString();
|
|
|
|
KWin::Application::setupLoggingCategoryFilters();
|
|
|
|
|
2014-08-13 15:13:44 +04:00
|
|
|
if (signal(SIGTERM, KWin::sighandler) == SIG_IGN)
|
|
|
|
signal(SIGTERM, SIG_IGN);
|
|
|
|
if (signal(SIGINT, KWin::sighandler) == SIG_IGN)
|
|
|
|
signal(SIGINT, SIG_IGN);
|
|
|
|
if (signal(SIGHUP, KWin::sighandler) == SIG_IGN)
|
|
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
|
2015-03-17 12:31:18 +03:00
|
|
|
// enforce wayland plugin, unfortunately command line switch has precedence
|
|
|
|
setenv("QT_QPA_PLATFORM", "wayland", true);
|
|
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 2))
|
|
|
|
// TODO: remove warning once we depend on Qt 5.5
|
|
|
|
qWarning() << "QtWayland 5.4.2 required, application might freeze if not present!";
|
|
|
|
#endif
|
|
|
|
|
2015-02-25 16:46:30 +03:00
|
|
|
qputenv("WAYLAND_SOCKET", QByteArray::number(server->createQtConnection()));
|
2014-08-12 11:08:48 +04:00
|
|
|
KWin::ApplicationWayland a(argc, argv);
|
|
|
|
a.setupTranslator();
|
|
|
|
|
2015-02-09 15:28:37 +03:00
|
|
|
server->setParent(&a);
|
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
KWin::Application::createAboutData();
|
|
|
|
|
2015-01-09 18:00:16 +03:00
|
|
|
QCommandLineOption xwaylandOption(QStringLiteral("xwayland"),
|
2015-02-09 15:40:01 +03:00
|
|
|
i18n("Start a rootless Xwayland server."));
|
2015-02-09 15:28:37 +03:00
|
|
|
QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")},
|
|
|
|
i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."),
|
|
|
|
QStringLiteral("socket"));
|
2014-08-12 17:27:35 +04:00
|
|
|
|
2014-08-12 11:08:48 +04:00
|
|
|
QCommandLineParser parser;
|
|
|
|
a.setupCommandLine(&parser);
|
2015-01-09 18:00:16 +03:00
|
|
|
parser.addOption(xwaylandOption);
|
2015-02-09 15:28:37 +03:00
|
|
|
parser.addOption(waylandSocketOption);
|
2014-08-15 16:03:52 +04:00
|
|
|
#if HAVE_INPUT
|
|
|
|
QCommandLineOption libinputOption(QStringLiteral("libinput"),
|
|
|
|
i18n("Enable libinput support for input events processing. Note: never use in a nested session."));
|
|
|
|
parser.addOption(libinputOption);
|
|
|
|
#endif
|
2014-08-12 11:08:48 +04:00
|
|
|
|
|
|
|
parser.process(a);
|
|
|
|
a.processCommandLine(&parser);
|
|
|
|
|
2014-08-15 16:03:52 +04:00
|
|
|
#if HAVE_INPUT
|
|
|
|
KWin::Application::setUseLibinput(parser.isSet(libinputOption));
|
|
|
|
#endif
|
|
|
|
|
2015-02-20 13:53:25 +03:00
|
|
|
a.setStartXwayland(parser.isSet(xwaylandOption));
|
|
|
|
a.start();
|
2014-08-12 11:08:48 +04:00
|
|
|
|
|
|
|
return a.exec();
|
|
|
|
}
|