2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2019-02-19 08:50:20 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2019-02-19 08:50:20 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2022-02-25 09:38:26 +00:00
|
|
|
#include <config-kwin.h>
|
2019-02-19 08:50:20 +00:00
|
|
|
#include "xwayland.h"
|
2021-09-23 09:38:25 +00:00
|
|
|
#include "cursor.h"
|
2018-08-21 20:06:42 +00:00
|
|
|
#include "databridge.h"
|
2021-09-03 11:47:57 +00:00
|
|
|
#include "dnd.h"
|
|
|
|
#include "xwldrophandler.h"
|
2022-03-12 14:09:57 +00:00
|
|
|
#include "xwaylandlauncher.h"
|
2018-08-21 20:06:42 +00:00
|
|
|
|
2021-11-09 17:01:13 +00:00
|
|
|
#include "abstract_output.h"
|
2019-02-19 08:50:20 +00:00
|
|
|
#include "main_wayland.h"
|
2022-01-18 08:35:52 +00:00
|
|
|
#include "utils/common.h"
|
2021-11-09 17:01:13 +00:00
|
|
|
#include "platform.h"
|
2022-01-13 13:54:03 +00:00
|
|
|
#include "utils/xcbutils.h"
|
2019-07-02 19:56:03 +00:00
|
|
|
#include "wayland_server.h"
|
2020-07-20 08:07:08 +00:00
|
|
|
#include "xwayland_logging.h"
|
2022-01-25 14:17:51 +00:00
|
|
|
#include "x11eventfilter.h"
|
2019-02-19 08:50:20 +00:00
|
|
|
|
2019-06-22 10:40:12 +00:00
|
|
|
#include <KSelectionOwner>
|
2019-02-19 08:50:20 +00:00
|
|
|
|
|
|
|
#include <QAbstractEventDispatcher>
|
2021-02-08 11:57:12 +00:00
|
|
|
#include <QDataStream>
|
2019-02-19 08:50:20 +00:00
|
|
|
#include <QFile>
|
2021-02-08 11:57:12 +00:00
|
|
|
#include <QHostInfo>
|
|
|
|
#include <QRandomGenerator>
|
2021-02-08 15:36:41 +00:00
|
|
|
#include <QScopeGuard>
|
2020-08-27 09:05:28 +00:00
|
|
|
#include <QTimer>
|
2019-07-02 19:56:03 +00:00
|
|
|
#include <QtConcurrentRun>
|
2022-03-12 14:09:57 +00:00
|
|
|
#include <QSocketNotifier>
|
2019-02-19 08:50:20 +00:00
|
|
|
|
2020-09-08 08:27:42 +00:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring>
|
2022-03-17 09:51:32 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
2019-02-19 08:50:20 +00:00
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
2019-02-19 08:50:20 +00:00
|
|
|
namespace Xwl
|
|
|
|
{
|
|
|
|
|
2022-01-25 14:17:51 +00:00
|
|
|
class XrandrEventFilter : public X11EventFilter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit XrandrEventFilter(Xwayland *backend);
|
|
|
|
|
|
|
|
bool event(xcb_generic_event_t *event) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Xwayland *const m_backend;
|
|
|
|
};
|
|
|
|
|
|
|
|
XrandrEventFilter::XrandrEventFilter(Xwayland *backend)
|
|
|
|
: X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent())
|
|
|
|
, m_backend(backend)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XrandrEventFilter::event(xcb_generic_event_t *event)
|
|
|
|
{
|
|
|
|
Q_ASSERT((event->response_type & ~0x80) == Xcb::Extensions::self()->randrNotifyEvent());
|
|
|
|
m_backend->updatePrimary(kwinApp()->platform()->primaryOutput());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-22 00:20:16 +00:00
|
|
|
Xwayland::Xwayland(ApplicationWaylandAbstract *app, QObject *parent)
|
2019-07-02 19:56:03 +00:00
|
|
|
: XwaylandInterface(parent)
|
|
|
|
, m_app(app)
|
2022-03-12 14:09:57 +00:00
|
|
|
, m_launcher(new XwaylandLauncher(this))
|
2019-02-19 08:50:20 +00:00
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
connect(m_launcher, &XwaylandLauncher::started, this, &Xwayland::handleXwaylandReady);
|
|
|
|
connect(m_launcher, &XwaylandLauncher::finished, this, &Xwayland::handleXwaylandFinished);
|
|
|
|
connect(m_launcher, &XwaylandLauncher::errorOccurred, this, &Xwayland::errorOccurred);
|
2019-02-19 08:50:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Xwayland::~Xwayland()
|
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
m_launcher->stop();
|
2020-07-20 07:44:13 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 10:03:10 +00:00
|
|
|
void Xwayland::start()
|
2021-02-10 14:28:34 +00:00
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
m_launcher->start();
|
2020-07-20 08:07:08 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:09:57 +00:00
|
|
|
XwaylandLauncher *Xwayland::xwaylandLauncher() const
|
2020-08-27 09:05:28 +00:00
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
return m_launcher;
|
2020-08-27 09:05:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-20 08:07:08 +00:00
|
|
|
void Xwayland::dispatchEvents()
|
|
|
|
{
|
|
|
|
xcb_connection_t *connection = kwinApp()->x11Connection();
|
|
|
|
if (!connection) {
|
|
|
|
qCWarning(KWIN_XWL, "Attempting to dispatch X11 events with no connection");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-22 06:03:27 +00:00
|
|
|
const int connectionError = xcb_connection_has_error(connection);
|
|
|
|
if (connectionError) {
|
|
|
|
qCWarning(KWIN_XWL, "The X11 connection broke (error %d)", connectionError);
|
2022-03-12 14:09:57 +00:00
|
|
|
m_launcher->stop();
|
2020-07-22 06:03:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-20 08:07:08 +00:00
|
|
|
while (xcb_generic_event_t *event = xcb_poll_for_event(connection)) {
|
2022-03-10 18:03:59 +00:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
2020-07-20 08:07:08 +00:00
|
|
|
long result = 0;
|
2022-03-10 18:03:59 +00:00
|
|
|
#else
|
|
|
|
qintptr result = 0;
|
|
|
|
#endif
|
2020-07-20 08:07:08 +00:00
|
|
|
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
|
|
|
|
dispatcher->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result);
|
|
|
|
free(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_flush(connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Xwayland::installSocketNotifier()
|
|
|
|
{
|
|
|
|
const int fileDescriptor = xcb_get_file_descriptor(kwinApp()->x11Connection());
|
|
|
|
|
|
|
|
m_socketNotifier = new QSocketNotifier(fileDescriptor, QSocketNotifier::Read, this);
|
|
|
|
connect(m_socketNotifier, &QSocketNotifier::activated, this, &Xwayland::dispatchEvents);
|
|
|
|
|
|
|
|
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
|
|
|
|
connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &Xwayland::dispatchEvents);
|
|
|
|
connect(dispatcher, &QAbstractEventDispatcher::awake, this, &Xwayland::dispatchEvents);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Xwayland::uninstallSocketNotifier()
|
|
|
|
{
|
|
|
|
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
|
|
|
|
disconnect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &Xwayland::dispatchEvents);
|
|
|
|
disconnect(dispatcher, &QAbstractEventDispatcher::awake, this, &Xwayland::dispatchEvents);
|
|
|
|
|
|
|
|
delete m_socketNotifier;
|
|
|
|
m_socketNotifier = nullptr;
|
|
|
|
}
|
|
|
|
|
2020-08-27 09:05:28 +00:00
|
|
|
|
|
|
|
|
2022-03-12 14:09:57 +00:00
|
|
|
void Xwayland::handleXwaylandFinished()
|
2020-08-27 09:05:28 +00:00
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
disconnect(kwinApp()->platform(), &Platform::primaryOutputChanged, this, &Xwayland::updatePrimary);
|
|
|
|
m_app->setClosingX11Connection(true);
|
2020-07-20 08:07:08 +00:00
|
|
|
|
2022-03-12 14:09:57 +00:00
|
|
|
delete m_xrandrEventsFilter;
|
|
|
|
m_xrandrEventsFilter = nullptr;
|
|
|
|
|
|
|
|
// If Xwayland has crashed, we must deactivate the socket notifier and ensure that no X11
|
|
|
|
// events will be dispatched before blocking; otherwise we will simply hang...
|
|
|
|
uninstallSocketNotifier();
|
|
|
|
|
|
|
|
DataBridge::destroy();
|
|
|
|
m_selectionOwner.reset();
|
|
|
|
|
|
|
|
destroyX11Connection();
|
|
|
|
|
|
|
|
m_app->setClosingX11Connection(false);
|
2019-08-07 08:21:30 +00:00
|
|
|
}
|
|
|
|
|
2022-03-12 14:09:57 +00:00
|
|
|
|
2020-09-08 09:35:47 +00:00
|
|
|
void Xwayland::handleXwaylandReady()
|
|
|
|
{
|
2021-02-10 14:28:34 +00:00
|
|
|
if (!createX11Connection()) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT errorOccurred();
|
2020-09-08 09:35:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-12 14:09:57 +00:00
|
|
|
qCInfo(KWIN_XWL) << "Xwayland server started on display" << m_launcher->displayName();
|
2020-09-08 09:35:47 +00:00
|
|
|
|
|
|
|
// create selection owner for WM_S0 - magic X display number expected by XWayland
|
2021-02-08 22:00:08 +00:00
|
|
|
m_selectionOwner.reset(new KSelectionOwner("WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow()));
|
2021-02-09 08:17:48 +00:00
|
|
|
connect(m_selectionOwner.data(), &KSelectionOwner::lostOwnership,
|
|
|
|
this, &Xwayland::handleSelectionLostOwnership);
|
|
|
|
connect(m_selectionOwner.data(), &KSelectionOwner::claimedOwnership,
|
|
|
|
this, &Xwayland::handleSelectionClaimedOwnership);
|
|
|
|
connect(m_selectionOwner.data(), &KSelectionOwner::failedToClaimOwnership,
|
|
|
|
this, &Xwayland::handleSelectionFailedToClaimOwnership);
|
2021-02-08 22:00:08 +00:00
|
|
|
m_selectionOwner->claim(true);
|
2020-09-08 09:35:47 +00:00
|
|
|
|
2021-09-23 09:38:25 +00:00
|
|
|
Cursor *mouseCursor = Cursors::self()->mouse();
|
|
|
|
if (mouseCursor) {
|
|
|
|
Xcb::defineCursor(kwinApp()->x11RootWindow(), mouseCursor->x11Cursor(Qt::ArrowCursor));
|
|
|
|
}
|
|
|
|
|
2020-09-08 09:35:47 +00:00
|
|
|
DataBridge::create(this);
|
|
|
|
|
|
|
|
auto env = m_app->processStartupEnvironment();
|
2022-03-12 14:09:57 +00:00
|
|
|
env.insert(QStringLiteral("DISPLAY"), m_launcher->displayName());
|
|
|
|
env.insert(QStringLiteral("XAUTHORITY"), m_launcher->xauthority());
|
|
|
|
qputenv("DISPLAY", m_launcher->displayName().toLatin1());
|
|
|
|
qputenv("XAUTHORITY", m_launcher->xauthority().toLatin1());
|
2020-09-08 09:35:47 +00:00
|
|
|
m_app->setProcessStartupEnvironment(env);
|
|
|
|
|
2021-11-09 17:01:13 +00:00
|
|
|
connect(kwinApp()->platform(), &Platform::primaryOutputChanged, this, &Xwayland::updatePrimary);
|
|
|
|
updatePrimary(kwinApp()->platform()->primaryOutput());
|
|
|
|
|
2020-09-08 09:35:47 +00:00
|
|
|
Xcb::sync(); // Trigger possible errors, there's still a chance to abort
|
2022-01-25 14:17:51 +00:00
|
|
|
|
|
|
|
delete m_xrandrEventsFilter;
|
|
|
|
m_xrandrEventsFilter = new XrandrEventFilter(this);
|
2020-09-08 09:35:47 +00:00
|
|
|
}
|
|
|
|
|
2021-11-09 17:01:13 +00:00
|
|
|
void Xwayland::updatePrimary(AbstractOutput *primaryOutput)
|
|
|
|
{
|
2022-02-08 22:28:03 +00:00
|
|
|
Xcb::RandR::ScreenResources resources(kwinApp()->x11RootWindow());
|
2021-11-09 17:01:13 +00:00
|
|
|
xcb_randr_crtc_t *crtcs = resources.crtcs();
|
|
|
|
if (!crtcs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < resources->num_crtcs; ++i) {
|
|
|
|
Xcb::RandR::CrtcInfo crtcInfo(crtcs[i], resources->config_timestamp);
|
|
|
|
const QRect geometry = crtcInfo.rect();
|
|
|
|
if (geometry.topLeft() == primaryOutput->geometry().topLeft()) {
|
|
|
|
auto outputs = crtcInfo.outputs();
|
|
|
|
if (outputs && crtcInfo->num_outputs > 0) {
|
|
|
|
qCDebug(KWIN_XWL) << "Setting primary" << primaryOutput << outputs[0];
|
2022-02-08 22:28:03 +00:00
|
|
|
xcb_randr_set_output_primary(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), outputs[0]);
|
2021-11-09 17:01:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 08:17:48 +00:00
|
|
|
void Xwayland::handleSelectionLostOwnership()
|
|
|
|
{
|
|
|
|
qCWarning(KWIN_XWL) << "Somebody else claimed ownership of WM_S0. This should never happen!";
|
2022-03-12 14:09:57 +00:00
|
|
|
m_launcher->stop();
|
2021-02-09 08:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Xwayland::handleSelectionFailedToClaimOwnership()
|
|
|
|
{
|
|
|
|
qCWarning(KWIN_XWL) << "Failed to claim ownership of WM_S0. This should never happen!";
|
2022-03-12 14:09:57 +00:00
|
|
|
m_launcher->stop();
|
2021-02-09 08:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Xwayland::handleSelectionClaimedOwnership()
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT started();
|
2021-02-09 08:17:48 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 16:01:19 +00:00
|
|
|
bool Xwayland::createX11Connection()
|
2019-02-19 08:50:20 +00:00
|
|
|
{
|
2022-03-12 14:09:57 +00:00
|
|
|
xcb_connection_t *connection = xcb_connect_to_fd(m_launcher->xcbConnectionFd(), nullptr);
|
2020-09-01 16:01:19 +00:00
|
|
|
|
|
|
|
const int errorCode = xcb_connection_has_error(connection);
|
|
|
|
if (errorCode) {
|
|
|
|
qCDebug(KWIN_XWL, "Failed to establish the XCB connection (error %d)", errorCode);
|
|
|
|
return false;
|
2019-02-19 08:50:20 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 07:58:35 +00:00
|
|
|
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
|
|
|
|
Q_ASSERT(screen);
|
2018-08-21 20:06:42 +00:00
|
|
|
|
2020-07-20 12:16:42 +00:00
|
|
|
m_app->setX11Connection(connection);
|
2020-07-28 07:58:35 +00:00
|
|
|
m_app->setX11DefaultScreen(screen);
|
2020-07-20 12:16:42 +00:00
|
|
|
m_app->setX11ScreenNumber(0);
|
2020-07-28 07:58:35 +00:00
|
|
|
m_app->setX11RootWindow(screen->root);
|
2020-07-07 10:05:39 +00:00
|
|
|
|
|
|
|
m_app->createAtoms();
|
2020-07-20 08:07:08 +00:00
|
|
|
m_app->installNativeX11EventFilter();
|
|
|
|
|
|
|
|
installSocketNotifier();
|
2020-07-07 10:05:39 +00:00
|
|
|
|
|
|
|
// Note that it's very important to have valid x11RootWindow(), x11ScreenNumber(), and
|
|
|
|
// atoms when the rest of kwin is notified about the new X11 connection.
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_app->x11ConnectionChanged();
|
2020-09-01 16:01:19 +00:00
|
|
|
|
|
|
|
return true;
|
2020-07-07 10:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Xwayland::destroyX11Connection()
|
|
|
|
{
|
|
|
|
if (!m_app->x11Connection()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_app->x11ConnectionAboutToBeDestroyed();
|
2020-07-20 08:07:08 +00:00
|
|
|
|
2020-07-07 10:05:39 +00:00
|
|
|
Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
|
|
|
|
m_app->destroyAtoms();
|
2020-07-20 08:07:08 +00:00
|
|
|
m_app->removeNativeX11EventFilter();
|
2020-07-07 10:05:39 +00:00
|
|
|
|
|
|
|
xcb_disconnect(m_app->x11Connection());
|
2020-07-20 08:07:08 +00:00
|
|
|
|
2020-07-07 10:05:39 +00:00
|
|
|
m_app->setX11Connection(nullptr);
|
2020-07-28 07:58:35 +00:00
|
|
|
m_app->setX11DefaultScreen(nullptr);
|
2020-07-07 10:05:39 +00:00
|
|
|
m_app->setX11ScreenNumber(-1);
|
|
|
|
m_app->setX11RootWindow(XCB_WINDOW_NONE);
|
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_app->x11ConnectionChanged();
|
2019-02-19 08:50:20 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
DragEventReply Xwayland::dragMoveFilter(Toplevel *target, const QPoint &pos)
|
2018-08-22 12:56:48 +00:00
|
|
|
{
|
2020-08-11 16:36:48 +00:00
|
|
|
DataBridge *bridge = DataBridge::self();
|
|
|
|
if (!bridge) {
|
2018-08-22 12:56:48 +00:00
|
|
|
return DragEventReply::Wayland;
|
|
|
|
}
|
2020-08-11 16:36:48 +00:00
|
|
|
return bridge->dragMoveFilter(target, pos);
|
2018-08-22 12:56:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 11:47:57 +00:00
|
|
|
KWaylandServer::AbstractDropHandler *Xwayland::xwlDropHandler()
|
|
|
|
{
|
|
|
|
DataBridge *bridge = DataBridge::self();
|
|
|
|
if (bridge) {
|
|
|
|
return bridge->dnd()->dropHandler();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:56:03 +00:00
|
|
|
} // namespace Xwl
|
|
|
|
} // namespace KWin
|