File indexing completed on 2025-03-16 11:21:58
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