File indexing completed on 2024-05-19 16:35:36

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
0007     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 #include "xwayland.h"
0012 
0013 #include <config-kwin.h>
0014 
0015 #include "databridge.h"
0016 #include "dnd.h"
0017 #include "window.h"
0018 #include "xwaylandlauncher.h"
0019 #include "xwldrophandler.h"
0020 
0021 #include "core/output.h"
0022 #include "input_event_spy.h"
0023 #include "keyboard_input.h"
0024 #include "main_wayland.h"
0025 #include "utils/common.h"
0026 #include "utils/xcbutils.h"
0027 #include "wayland_server.h"
0028 #include "waylandwindow.h"
0029 #include "workspace.h"
0030 #include "x11eventfilter.h"
0031 #include "xkb.h"
0032 #include "xwayland_logging.h"
0033 
0034 #include <KSelectionOwner>
0035 #include <wayland/keyboard_interface.h>
0036 #include <wayland/seat_interface.h>
0037 #include <wayland/surface_interface.h>
0038 
0039 #include <QAbstractEventDispatcher>
0040 #include <QDataStream>
0041 #include <QFile>
0042 #include <QHostInfo>
0043 #include <QRandomGenerator>
0044 #include <QScopeGuard>
0045 #include <QSocketNotifier>
0046 #include <QTimer>
0047 #include <QtConcurrentRun>
0048 
0049 #include <cerrno>
0050 #include <cstring>
0051 #include <input_event.h>
0052 #include <sys/socket.h>
0053 #include <unistd.h>
0054 #include <xkbcommon/xkbcommon-keysyms.h>
0055 
0056 namespace KWin
0057 {
0058 namespace Xwl
0059 {
0060 
0061 class XrandrEventFilter : public X11EventFilter
0062 {
0063 public:
0064     explicit XrandrEventFilter(Xwayland *backend);
0065 
0066     bool event(xcb_generic_event_t *event) override;
0067 
0068 private:
0069     Xwayland *const m_backend;
0070 };
0071 
0072 XrandrEventFilter::XrandrEventFilter(Xwayland *backend)
0073     : X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent())
0074     , m_backend(backend)
0075 {
0076 }
0077 
0078 bool XrandrEventFilter::event(xcb_generic_event_t *event)
0079 {
0080     Q_ASSERT((event->response_type & ~0x80) == Xcb::Extensions::self()->randrNotifyEvent());
0081     m_backend->updatePrimary();
0082     return false;
0083 }
0084 
0085 class XwaylandInputSpy : public QObject, public KWin::InputEventSpy
0086 {
0087 public:
0088     XwaylandInputSpy()
0089     {
0090         connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::focusedKeyboardSurfaceAboutToChange,
0091                 this, [this](KWaylandServer::SurfaceInterface *newSurface) {
0092                     auto keyboard = waylandServer()->seat()->keyboard();
0093                     if (!newSurface) {
0094                         return;
0095                     }
0096 
0097                     if (waylandServer()->xWaylandConnection() == newSurface->client()) {
0098                         // Since this is a spy but the keyboard interface gets its normal sendKey calls through filters,
0099                         // there can be a mismatch in both states.
0100                         // This loop makes sure all key press events are reset before we switch back to the
0101                         // Xwayland client and the state is correctly restored.
0102                         for (auto it = m_states.constBegin(); it != m_states.constEnd(); ++it) {
0103                             if (it.value() == KWaylandServer::KeyboardKeyState::Pressed) {
0104                                 keyboard->sendKey(it.key(), KWaylandServer::KeyboardKeyState::Released, waylandServer()->xWaylandConnection());
0105                             }
0106                         }
0107                         m_states.clear();
0108                     }
0109                 });
0110     }
0111 
0112     void setMode(XwaylandEavesdropsMode mode)
0113     {
0114         static const QSet<quint32> modifierKeys = {
0115             Qt::Key_Control,
0116             Qt::Key_Shift,
0117             Qt::Key_Alt,
0118             Qt::Key_Meta,
0119         };
0120 
0121         switch (mode) {
0122         case None:
0123             m_filter = {};
0124             break;
0125         case Modifiers:
0126             m_filter = [](int key, Qt::KeyboardModifiers) {
0127                 return modifierKeys.contains(key);
0128             };
0129             break;
0130         case Combinations:
0131             m_filter = [](int key, Qt::KeyboardModifiers m) {
0132                 return m != Qt::NoModifier || modifierKeys.contains(key);
0133             };
0134             break;
0135         case All:
0136             m_filter = [](int, Qt::KeyboardModifiers) {
0137                 return true;
0138             };
0139             break;
0140         }
0141     }
0142 
0143     void keyEvent(KWin::KeyEvent *event) override
0144     {
0145         if (event->isAutoRepeat()) {
0146             return;
0147         }
0148 
0149         Window *window = workspace()->activeWindow();
0150         if (!m_filter || !m_filter(event->key(), event->modifiers()) || (window && window->isLockScreen())) {
0151             return;
0152         }
0153 
0154         auto keyboard = waylandServer()->seat()->keyboard();
0155         auto surface = keyboard->focusedSurface();
0156         if (!surface) {
0157             return;
0158         }
0159 
0160         auto client = surface->client();
0161         if (waylandServer()->xWaylandConnection() != client) {
0162             KWaylandServer::KeyboardKeyState state{event->type() == QEvent::KeyPress};
0163             if (!updateKey(event->nativeScanCode(), state)) {
0164                 return;
0165             }
0166 
0167             auto xkb = input()->keyboard()->xkb();
0168             keyboard->sendModifiers(xkb->modifierState().depressed,
0169                                     xkb->modifierState().latched,
0170                                     xkb->modifierState().locked,
0171                                     xkb->currentLayout());
0172 
0173             waylandServer()->seat()->keyboard()->sendKey(event->nativeScanCode(), state, waylandServer()->xWaylandConnection());
0174         }
0175     }
0176 
0177     bool updateKey(quint32 key, KWaylandServer::KeyboardKeyState state)
0178     {
0179         auto it = m_states.find(key);
0180         if (it == m_states.end()) {
0181             m_states.insert(key, state);
0182             return true;
0183         }
0184         if (it.value() == state) {
0185             return false;
0186         }
0187         it.value() = state;
0188         return true;
0189     }
0190 
0191     QHash<quint32, KWaylandServer::KeyboardKeyState> m_states;
0192     std::function<bool(int key, Qt::KeyboardModifiers)> m_filter;
0193 };
0194 
0195 Xwayland::Xwayland(Application *app)
0196     : m_app(app)
0197     , m_launcher(new XwaylandLauncher(this))
0198 {
0199     connect(m_launcher, &XwaylandLauncher::started, this, &Xwayland::handleXwaylandReady);
0200     connect(m_launcher, &XwaylandLauncher::finished, this, &Xwayland::handleXwaylandFinished);
0201     connect(m_launcher, &XwaylandLauncher::errorOccurred, this, &Xwayland::errorOccurred);
0202 }
0203 
0204 Xwayland::~Xwayland()
0205 {
0206     m_launcher->stop();
0207 }
0208 
0209 void Xwayland::start()
0210 {
0211     m_launcher->start();
0212 }
0213 
0214 XwaylandLauncher *Xwayland::xwaylandLauncher() const
0215 {
0216     return m_launcher;
0217 }
0218 
0219 void Xwayland::dispatchEvents(DispatchEventsMode mode)
0220 {
0221     xcb_connection_t *connection = kwinApp()->x11Connection();
0222     if (!connection) {
0223         qCWarning(KWIN_XWL, "Attempting to dispatch X11 events with no connection");
0224         return;
0225     }
0226 
0227     const int connectionError = xcb_connection_has_error(connection);
0228     if (connectionError) {
0229         qCWarning(KWIN_XWL, "The X11 connection broke (error %d)", connectionError);
0230         m_launcher->stop();
0231         return;
0232     }
0233 
0234     auto pollEventFunc = mode == DispatchEventsMode::Poll ? xcb_poll_for_event : xcb_poll_for_queued_event;
0235 
0236     while (xcb_generic_event_t *event = pollEventFunc(connection)) {
0237 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0238         long result = 0;
0239 #else
0240         qintptr result = 0;
0241 #endif
0242 
0243         QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
0244         dispatcher->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result);
0245         free(event);
0246     }
0247 
0248     xcb_flush(connection);
0249 }
0250 
0251 void Xwayland::installSocketNotifier()
0252 {
0253     const int fileDescriptor = xcb_get_file_descriptor(kwinApp()->x11Connection());
0254 
0255     m_socketNotifier = new QSocketNotifier(fileDescriptor, QSocketNotifier::Read, this);
0256     connect(m_socketNotifier, &QSocketNotifier::activated, this, [this]() {
0257         dispatchEvents(DispatchEventsMode::Poll);
0258     });
0259 
0260     QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
0261     connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, [this]() {
0262         dispatchEvents(DispatchEventsMode::EventQueue);
0263     });
0264     connect(dispatcher, &QAbstractEventDispatcher::awake, this, [this]() {
0265         dispatchEvents(DispatchEventsMode::EventQueue);
0266     });
0267 }
0268 
0269 void Xwayland::uninstallSocketNotifier()
0270 {
0271     QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
0272     disconnect(dispatcher, nullptr, this, nullptr);
0273 
0274     delete m_socketNotifier;
0275     m_socketNotifier = nullptr;
0276 }
0277 
0278 void Xwayland::handleXwaylandFinished()
0279 {
0280     disconnect(workspace(), &Workspace::outputOrderChanged, this, &Xwayland::updatePrimary);
0281 
0282     delete m_xrandrEventsFilter;
0283     m_xrandrEventsFilter = nullptr;
0284 
0285     // If Xwayland has crashed, we must deactivate the socket notifier and ensure that no X11
0286     // events will be dispatched before blocking; otherwise we will simply hang...
0287     uninstallSocketNotifier();
0288 
0289     m_dataBridge.reset();
0290     m_selectionOwner.reset();
0291 
0292     destroyX11Connection();
0293 }
0294 
0295 void Xwayland::handleXwaylandReady()
0296 {
0297     if (!createX11Connection()) {
0298         Q_EMIT errorOccurred();
0299         return;
0300     }
0301 
0302     qCInfo(KWIN_XWL) << "Xwayland server started on display" << m_launcher->displayName();
0303 
0304     // create selection owner for WM_S0 - magic X display number expected by XWayland
0305     m_selectionOwner.reset(new KSelectionOwner("WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow()));
0306     connect(m_selectionOwner.get(), &KSelectionOwner::lostOwnership,
0307             this, &Xwayland::handleSelectionLostOwnership);
0308     connect(m_selectionOwner.get(), &KSelectionOwner::claimedOwnership,
0309             this, &Xwayland::handleSelectionClaimedOwnership);
0310     connect(m_selectionOwner.get(), &KSelectionOwner::failedToClaimOwnership,
0311             this, &Xwayland::handleSelectionFailedToClaimOwnership);
0312     m_selectionOwner->claim(true);
0313 
0314     m_dataBridge = std::make_unique<DataBridge>();
0315 
0316     auto env = m_app->processStartupEnvironment();
0317     env.insert(QStringLiteral("DISPLAY"), m_launcher->displayName());
0318     env.insert(QStringLiteral("XAUTHORITY"), m_launcher->xauthority());
0319     qputenv("DISPLAY", m_launcher->displayName().toLatin1());
0320     qputenv("XAUTHORITY", m_launcher->xauthority().toLatin1());
0321     m_app->setProcessStartupEnvironment(env);
0322 
0323     connect(workspace(), &Workspace::outputOrderChanged, this, &Xwayland::updatePrimary);
0324     updatePrimary();
0325 
0326     Xcb::sync(); // Trigger possible errors, there's still a chance to abort
0327 
0328     delete m_xrandrEventsFilter;
0329     m_xrandrEventsFilter = new XrandrEventFilter(this);
0330 
0331     refreshEavesdropping();
0332     connect(options, &Options::xwaylandEavesdropsChanged, this, &Xwayland::refreshEavesdropping);
0333 }
0334 
0335 void Xwayland::refreshEavesdropping()
0336 {
0337     if (!waylandServer()->seat()->keyboard()) {
0338         return;
0339     }
0340 
0341     const bool enabled = options->xwaylandEavesdrops() != None;
0342     if (enabled == bool(m_inputSpy)) {
0343         if (m_inputSpy) {
0344             m_inputSpy->setMode(options->xwaylandEavesdrops());
0345         }
0346         return;
0347     }
0348 
0349     if (enabled) {
0350         m_inputSpy.reset(new XwaylandInputSpy);
0351         input()->installInputEventSpy(m_inputSpy.get());
0352         m_inputSpy->setMode(options->xwaylandEavesdrops());
0353     } else {
0354         input()->uninstallInputEventSpy(m_inputSpy.get());
0355         m_inputSpy.reset();
0356     }
0357 }
0358 
0359 void Xwayland::updatePrimary()
0360 {
0361     if (workspace()->outputOrder().empty()) {
0362         return;
0363     }
0364     Xcb::RandR::ScreenResources resources(kwinApp()->x11RootWindow());
0365     xcb_randr_crtc_t *crtcs = resources.crtcs();
0366     if (!crtcs) {
0367         return;
0368     }
0369 
0370     Output *const primaryOutput = workspace()->outputOrder().front();
0371     for (int i = 0; i < resources->num_crtcs; ++i) {
0372         Xcb::RandR::CrtcInfo crtcInfo(crtcs[i], resources->config_timestamp);
0373         const QRect geometry = crtcInfo.rect();
0374         if (geometry.topLeft() == primaryOutput->geometry().topLeft()) {
0375             auto outputs = crtcInfo.outputs();
0376             if (outputs && crtcInfo->num_outputs > 0) {
0377                 qCDebug(KWIN_XWL) << "Setting primary" << primaryOutput << outputs[0];
0378                 xcb_randr_set_output_primary(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), outputs[0]);
0379                 break;
0380             }
0381         }
0382     }
0383 }
0384 
0385 void Xwayland::handleSelectionLostOwnership()
0386 {
0387     qCWarning(KWIN_XWL) << "Somebody else claimed ownership of WM_S0. This should never happen!";
0388     m_launcher->stop();
0389 }
0390 
0391 void Xwayland::handleSelectionFailedToClaimOwnership()
0392 {
0393     qCWarning(KWIN_XWL) << "Failed to claim ownership of WM_S0. This should never happen!";
0394     m_launcher->stop();
0395 }
0396 
0397 void Xwayland::handleSelectionClaimedOwnership()
0398 {
0399     Q_EMIT started();
0400 }
0401 
0402 bool Xwayland::createX11Connection()
0403 {
0404     xcb_connection_t *connection = xcb_connect_to_fd(m_launcher->xcbConnectionFd(), nullptr);
0405 
0406     const int errorCode = xcb_connection_has_error(connection);
0407     if (errorCode) {
0408         qCDebug(KWIN_XWL, "Failed to establish the XCB connection (error %d)", errorCode);
0409         return false;
0410     }
0411 
0412     xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
0413     Q_ASSERT(screen);
0414 
0415     m_app->setX11Connection(connection);
0416     m_app->setX11RootWindow(screen->root);
0417 
0418     m_app->createAtoms();
0419     m_app->installNativeX11EventFilter();
0420 
0421     installSocketNotifier();
0422 
0423     // Note that it's very important to have valid x11RootWindow(), and atoms when the
0424     // rest of kwin is notified about the new X11 connection.
0425     Q_EMIT m_app->x11ConnectionChanged();
0426 
0427     return true;
0428 }
0429 
0430 void Xwayland::destroyX11Connection()
0431 {
0432     if (!m_app->x11Connection()) {
0433         return;
0434     }
0435 
0436     Q_EMIT m_app->x11ConnectionAboutToBeDestroyed();
0437 
0438     Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
0439     m_app->destroyAtoms();
0440     m_app->removeNativeX11EventFilter();
0441 
0442     xcb_disconnect(m_app->x11Connection());
0443 
0444     m_app->setX11Connection(nullptr);
0445     m_app->setX11RootWindow(XCB_WINDOW_NONE);
0446 
0447     Q_EMIT m_app->x11ConnectionChanged();
0448 }
0449 
0450 DragEventReply Xwayland::dragMoveFilter(Window *target, const QPoint &pos)
0451 {
0452     if (m_dataBridge) {
0453         return m_dataBridge->dragMoveFilter(target, pos);
0454     } else {
0455         return DragEventReply::Wayland;
0456     }
0457 }
0458 
0459 KWaylandServer::AbstractDropHandler *Xwayland::xwlDropHandler()
0460 {
0461     if (m_dataBridge) {
0462         return m_dataBridge->dnd()->dropHandler();
0463     } else {
0464         return nullptr;
0465     }
0466 }
0467 
0468 } // namespace Xwl
0469 } // namespace KWin