13#include <config-kwin.h>
32#include "xwayland_logging.h"
34#include <KSelectionOwner>
39#include <QAbstractEventDispatcher>
42#include <QRandomGenerator>
44#include <QSocketNotifier>
46#include <QtConcurrentRun>
51#include <sys/socket.h>
53#include <xkbcommon/xkbcommon-keysyms.h>
65 bool event(xcb_generic_event_t *
event)
override;
80 m_backend->updatePrimary();
113 static const Qt::KeyboardModifiers modifierKeys = {
119 static const QSet<quint32> characterKeys = {
182 Qt::Key_BracketRight,
190 Qt::Key_nobreakspace,
201 Qt::Key_guillemotleft,
209 Qt::Key_threesuperior,
213 Qt::Key_periodcentered,
217 Qt::Key_guillemotright,
220 Qt::Key_threequarters,
221 Qt::Key_questiondown,
258 Qt::Key_SingleCandidate,
259 Qt::Key_MultipleCandidate,
260 Qt::Key_PreviousCandidate,
268 Qt::Key_Hiragana_Katakana,
271 Qt::Key_Zenkaku_Hankaku,
279 Qt::Key_Hangul_Start,
281 Qt::Key_Hangul_Hanja,
283 Qt::Key_Hangul_Romaja,
284 Qt::Key_Hangul_Jeonja,
285 Qt::Key_Hangul_Banja,
286 Qt::Key_Hangul_PreHanja,
287 Qt::Key_Hangul_PostHanja,
288 Qt::Key_Hangul_Special,
291 Qt::Key_Dead_Circumflex,
295 Qt::Key_Dead_Abovedot,
296 Qt::Key_Dead_Diaeresis,
297 Qt::Key_Dead_Abovering,
298 Qt::Key_Dead_Doubleacute,
300 Qt::Key_Dead_Cedilla,
303 Qt::Key_Dead_Voiced_Sound,
304 Qt::Key_Dead_Semivoiced_Sound,
305 Qt::Key_Dead_Belowdot,
309 Qt::Key_Dead_Abovecomma,
310 Qt::Key_Dead_Abovereversedcomma,
311 Qt::Key_Dead_Doublegrave,
312 Qt::Key_Dead_Belowring,
313 Qt::Key_Dead_Belowmacron,
314 Qt::Key_Dead_Belowcircumflex,
315 Qt::Key_Dead_Belowtilde,
316 Qt::Key_Dead_Belowbreve,
317 Qt::Key_Dead_Belowdiaeresis,
318 Qt::Key_Dead_Invertedbreve,
319 Qt::Key_Dead_Belowcomma,
320 Qt::Key_Dead_Currency,
331 Qt::Key_Dead_Small_Schwa,
332 Qt::Key_Dead_Capital_Schwa,
334 Qt::Key_Dead_Lowline,
335 Qt::Key_Dead_Aboveverticalline,
336 Qt::Key_Dead_Belowverticalline};
343 m_filter = [](
int key, Qt::KeyboardModifiers) {
344 return !characterKeys.contains(key);
348 m_filter = [](
int key, Qt::KeyboardModifiers m) {
349 return m.testAnyFlags(modifierKeys) || !characterKeys.contains(key);
353 m_filter = [](int, Qt::KeyboardModifiers) {
362 if (event->isAutoRepeat()) {
379 if (xwaylandClient && xwaylandClient != client) {
381 if (!
updateKey(event->nativeScanCode(), state)) {
386 keyboard->sendModifiers(xkb->modifierState().depressed,
387 xkb->modifierState().latched,
388 xkb->modifierState().locked,
389 xkb->currentLayout());
402 if (it.value() == state) {
410 std::function<bool(
int key, Qt::KeyboardModifiers)>
m_filter;
432 env.insert(QStringLiteral(
"DISPLAY"), m_launcher->
displayName());
433 env.insert(QStringLiteral(
"XAUTHORITY"), m_launcher->
xauthority());
434 qputenv(
"DISPLAY", m_launcher->
displayName().toLatin1());
435 qputenv(
"XAUTHORITY", m_launcher->
xauthority().toLatin1());
444void Xwayland::dispatchEvents(DispatchEventsMode mode)
446 xcb_connection_t *
connection = kwinApp()->x11Connection();
448 qCWarning(KWIN_XWL,
"Attempting to dispatch X11 events with no connection");
452 const int connectionError = xcb_connection_has_error(
connection);
453 if (connectionError) {
454 qCWarning(KWIN_XWL,
"The X11 connection broke (error %d)", connectionError);
459 auto pollEventFunc = mode == DispatchEventsMode::Poll ? xcb_poll_for_event : xcb_poll_for_queued_event;
461 while (xcb_generic_event_t *event = pollEventFunc(
connection)) {
464 QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
465 dispatcher->filterNativeEvent(QByteArrayLiteral(
"xcb_generic_event_t"), event, &result);
472void Xwayland::installSocketNotifier()
474 const int fileDescriptor = xcb_get_file_descriptor(kwinApp()->x11Connection());
476 m_socketNotifier =
new QSocketNotifier(fileDescriptor, QSocketNotifier::Read,
this);
477 connect(m_socketNotifier, &QSocketNotifier::activated,
this, [
this]() {
478 dispatchEvents(DispatchEventsMode::Poll);
481 QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
482 connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock,
this, [
this]() {
483 dispatchEvents(DispatchEventsMode::EventQueue);
485 connect(dispatcher, &QAbstractEventDispatcher::awake,
this, [
this]() {
486 dispatchEvents(DispatchEventsMode::EventQueue);
490void Xwayland::uninstallSocketNotifier()
492 QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
493 disconnect(dispatcher,
nullptr,
this,
nullptr);
495 delete m_socketNotifier;
496 m_socketNotifier =
nullptr;
499void Xwayland::handleXwaylandFinished()
503 delete m_xrandrEventsFilter;
504 m_xrandrEventsFilter =
nullptr;
508 uninstallSocketNotifier();
510 m_dataBridge.reset();
511 m_compositingManagerSelectionOwner.reset();
512 m_windowManagerSelectionOwner.reset();
517 destroyX11Connection();
520void Xwayland::handleXwaylandReady()
522 if (!createX11Connection()) {
527 qCInfo(KWIN_XWL) <<
"Xwayland server started on display" << m_launcher->
displayName();
529 m_compositingManagerSelectionOwner = std::make_unique<KSelectionOwner>(
"_NET_WM_CM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
530 m_compositingManagerSelectionOwner->claim(
true);
532 xcb_composite_redirect_subwindows(kwinApp()->x11Connection(),
533 kwinApp()->x11RootWindow(),
534 XCB_COMPOSITE_REDIRECT_MANUAL);
537 m_windowManagerSelectionOwner = std::make_unique<KSelectionOwner>(
"WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow());
538 connect(m_windowManagerSelectionOwner.get(), &KSelectionOwner::lostOwnership,
539 this, &Xwayland::handleSelectionLostOwnership);
540 connect(m_windowManagerSelectionOwner.get(), &KSelectionOwner::claimedOwnership,
541 this, &Xwayland::handleSelectionClaimedOwnership);
542 connect(m_windowManagerSelectionOwner.get(), &KSelectionOwner::failedToClaimOwnership,
543 this, &Xwayland::handleSelectionFailedToClaimOwnership);
544 m_windowManagerSelectionOwner->claim(
true);
546 m_dataBridge = std::make_unique<DataBridge>();
553 delete m_xrandrEventsFilter;
556 refreshEavesdropping();
560void Xwayland::refreshEavesdropping()
567 if (enabled ==
bool(m_inputSpy)) {
575 m_inputSpy = std::make_unique<XwaylandInputSpy>();
584void Xwayland::updatePrimary()
586 if (
workspace()->outputOrder().empty()) {
589 Xcb::RandR::ScreenResources resources(kwinApp()->x11RootWindow());
590 xcb_randr_crtc_t *crtcs = resources.crtcs();
596 const QRect primaryOutputGeometry =
Xcb::toXNative(primaryOutput->geometryF());
597 for (
int i = 0; i < resources->num_crtcs; ++i) {
598 Xcb::RandR::CrtcInfo crtcInfo(crtcs[i], resources->config_timestamp);
599 const QRect geometry = crtcInfo.rect();
600 if (geometry.topLeft() == primaryOutputGeometry.topLeft()) {
601 auto outputs = crtcInfo.outputs();
602 if (outputs && crtcInfo->num_outputs > 0) {
603 qCDebug(KWIN_XWL) <<
"Setting primary" << primaryOutput <<
outputs[0];
604 xcb_randr_set_output_primary(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), outputs[0]);
611void Xwayland::handleSelectionLostOwnership()
613 qCWarning(KWIN_XWL) <<
"Somebody else claimed ownership of WM_S0. This should never happen!";
617void Xwayland::handleSelectionFailedToClaimOwnership()
619 qCWarning(KWIN_XWL) <<
"Failed to claim ownership of WM_S0. This should never happen!";
623void Xwayland::handleSelectionClaimedOwnership()
628bool Xwayland::createX11Connection()
632 const int errorCode = xcb_connection_has_error(
connection);
634 qCDebug(KWIN_XWL,
"Failed to establish the XCB connection (error %d)", errorCode);
638 xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(
connection)).data;
647 installSocketNotifier();
656void Xwayland::destroyX11Connection()
664 Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
679 return m_dataBridge->dragMoveFilter(target);
685AbstractDropHandler *Xwayland::xwlDropHandler()
688 return m_dataBridge->dnd()->dropHandler();
697#include "moc_xwayland.cpp"
void installNativeX11EventFilter()
void setX11RootWindow(xcb_window_t root)
void setProcessStartupEnvironment(const QProcessEnvironment &environment)
QProcessEnvironment processStartupEnvironment() const
void removeNativeX11EventFilter()
void x11ConnectionChanged()
void x11ConnectionAboutToBeDestroyed()
void setX11Connection(xcb_connection_t *c)
Convenient Class which represents a wl_client.
wl_client * client() const
SurfaceInterface * focusedSurface() const
void sendKey(quint32 key, KeyboardKeyState state)
XwaylandEavesdropsMode xwaylandEavesdrops() const
void xwaylandEavesdropsChanged()
void focusedKeyboardSurfaceAboutToChange(SurfaceInterface *nextSurface)
KeyboardInterface * keyboard() const
Resource representing a wl_surface.
ClientConnection * client() const
SeatInterface * seat() const
ClientConnection * xWaylandConnection() const
virtual bool isLockScreen() const
void outputOrderChanged()
Window * activeWindow() const
QList< Output * > outputOrder() const
static Extensions * self()
int randrNotifyEvent() const
bool event(xcb_generic_event_t *event) override
XrandrEventFilter(Xwayland *backend)
Xwayland(Application *app)
XwaylandLauncher * xwaylandLauncher() const
friend class XrandrEventFilter
int xcbConnectionFd() const
QString displayName() const
QString xauthority() const
KWayland::Client::Seat * seat
QList< KWayland::Client::Output * > outputs
uint32_t toXNative(qreal value)
WaylandServer * waylandServer()
KWIN_EXPORT xcb_connection_t * connection()
InputRedirection * input()