File indexing completed on 2025-04-27 11:33:01

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "x11_standalone_xinputintegration.h"
0010 #include "core/outputbackend.h"
0011 #include "gestures.h"
0012 #include "keyboard_input.h"
0013 #include "main.h"
0014 #include "pointer_input.h"
0015 #include "screenedge.h"
0016 #include "x11_standalone_cursor.h"
0017 #include "x11_standalone_logging.h"
0018 
0019 #include "../common/ge_event_mem_mover.h"
0020 
0021 #include "input.h"
0022 #include "modifier_only_shortcuts.h"
0023 #include "workspace.h"
0024 #include "x11eventfilter.h"
0025 #include <kwinglobals.h>
0026 
0027 #include <X11/extensions/XI2proto.h>
0028 #include <X11/extensions/XInput2.h>
0029 
0030 #include <linux/input.h>
0031 
0032 namespace KWin
0033 {
0034 
0035 static inline qreal fixed1616ToReal(FP1616 val)
0036 {
0037     return (val)*1.0 / (1 << 16);
0038 }
0039 
0040 class XInputEventFilter : public X11EventFilter
0041 {
0042 public:
0043     XInputEventFilter(int xi_opcode)
0044         : X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector<int>{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease, XI_TouchBegin, XI_TouchUpdate, XI_TouchOwnership, XI_TouchEnd})
0045     {
0046     }
0047     ~XInputEventFilter() override = default;
0048 
0049     bool event(xcb_generic_event_t *event) override
0050     {
0051         GeEventMemMover ge(event);
0052         switch (ge->event_type) {
0053         case XI_RawKeyPress: {
0054             auto re = reinterpret_cast<xXIRawEvent *>(event);
0055             input()->keyboard()->processKey(re->detail - 8, InputRedirection::KeyboardKeyPressed, std::chrono::milliseconds(re->time));
0056             break;
0057         }
0058         case XI_RawKeyRelease: {
0059             auto re = reinterpret_cast<xXIRawEvent *>(event);
0060             input()->keyboard()->processKey(re->detail - 8, InputRedirection::KeyboardKeyReleased, std::chrono::milliseconds(re->time));
0061             break;
0062         }
0063         case XI_RawButtonPress: {
0064             auto e = reinterpret_cast<xXIRawEvent *>(event);
0065             switch (e->detail) {
0066             // TODO: this currently ignores left handed settings, for current usage not needed
0067             // if we want to use also for global mouse shortcuts, this needs to reflect state correctly
0068             case XCB_BUTTON_INDEX_1:
0069                 input()->pointer()->processButton(BTN_LEFT, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
0070                 break;
0071             case XCB_BUTTON_INDEX_2:
0072                 input()->pointer()->processButton(BTN_MIDDLE, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
0073                 break;
0074             case XCB_BUTTON_INDEX_3:
0075                 input()->pointer()->processButton(BTN_RIGHT, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
0076                 break;
0077             case XCB_BUTTON_INDEX_4:
0078             case XCB_BUTTON_INDEX_5:
0079                 // vertical axis, ignore on press
0080                 break;
0081                 // TODO: further buttons, horizontal scrolling?
0082             }
0083         }
0084             if (m_x11Cursor) {
0085                 m_x11Cursor->schedulePoll();
0086             }
0087             break;
0088         case XI_RawButtonRelease: {
0089             auto e = reinterpret_cast<xXIRawEvent *>(event);
0090             switch (e->detail) {
0091             // TODO: this currently ignores left handed settings, for current usage not needed
0092             // if we want to use also for global mouse shortcuts, this needs to reflect state correctly
0093             case XCB_BUTTON_INDEX_1:
0094                 input()->pointer()->processButton(BTN_LEFT, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
0095                 break;
0096             case XCB_BUTTON_INDEX_2:
0097                 input()->pointer()->processButton(BTN_MIDDLE, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
0098                 break;
0099             case XCB_BUTTON_INDEX_3:
0100                 input()->pointer()->processButton(BTN_RIGHT, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
0101                 break;
0102             case XCB_BUTTON_INDEX_4:
0103                 input()->pointer()->processAxis(InputRedirection::PointerAxisVertical, 120, 1, InputRedirection::PointerAxisSourceWheel, std::chrono::milliseconds(e->time));
0104                 break;
0105             case XCB_BUTTON_INDEX_5:
0106                 input()->pointer()->processAxis(InputRedirection::PointerAxisVertical, -120, 1, InputRedirection::PointerAxisSourceWheel, std::chrono::milliseconds(e->time));
0107                 break;
0108                 // TODO: further buttons, horizontal scrolling?
0109             }
0110         }
0111             if (m_x11Cursor) {
0112                 m_x11Cursor->schedulePoll();
0113             }
0114             break;
0115         case XI_TouchBegin: {
0116             auto e = reinterpret_cast<xXIDeviceEvent *>(event);
0117             m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)));
0118             break;
0119         }
0120         case XI_TouchUpdate: {
0121             auto e = reinterpret_cast<xXIDeviceEvent *>(event);
0122             const QPointF touchPosition = QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y));
0123             if (e->detail == m_trackingTouchId) {
0124                 const auto last = m_lastTouchPositions.value(e->detail);
0125                 workspace()->screenEdges()->gestureRecognizer()->updateSwipeGesture(touchPosition - last);
0126             }
0127             m_lastTouchPositions.insert(e->detail, touchPosition);
0128             break;
0129         }
0130         case XI_TouchEnd: {
0131             auto e = reinterpret_cast<xXIDeviceEvent *>(event);
0132             if (e->detail == m_trackingTouchId) {
0133                 workspace()->screenEdges()->gestureRecognizer()->endSwipeGesture();
0134             }
0135             m_lastTouchPositions.remove(e->detail);
0136             m_trackingTouchId = 0;
0137             break;
0138         }
0139         case XI_TouchOwnership: {
0140             auto e = reinterpret_cast<xXITouchOwnershipEvent *>(event);
0141             auto it = m_lastTouchPositions.constFind(e->touchid);
0142             if (it == m_lastTouchPositions.constEnd()) {
0143                 XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, XIRejectTouch);
0144             } else {
0145                 if (workspace()->screenEdges()->gestureRecognizer()->startSwipeGesture(it.value()) > 0) {
0146                     m_trackingTouchId = e->touchid;
0147                 }
0148                 XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, m_trackingTouchId == e->touchid ? XIAcceptTouch : XIRejectTouch);
0149             }
0150             break;
0151         }
0152         default:
0153             if (m_x11Cursor) {
0154                 m_x11Cursor->schedulePoll();
0155             }
0156             break;
0157         }
0158         return false;
0159     }
0160 
0161     void setCursor(const QPointer<X11Cursor> &cursor)
0162     {
0163         m_x11Cursor = cursor;
0164     }
0165     void setDisplay(Display *display)
0166     {
0167         m_x11Display = display;
0168     }
0169 
0170 private:
0171     Display *display() const
0172     {
0173         return m_x11Display;
0174     }
0175 
0176     QPointer<X11Cursor> m_x11Cursor;
0177     Display *m_x11Display = nullptr;
0178     uint32_t m_trackingTouchId = 0;
0179     QHash<uint32_t, QPointF> m_lastTouchPositions;
0180 };
0181 
0182 class XKeyPressReleaseEventFilter : public X11EventFilter
0183 {
0184 public:
0185     XKeyPressReleaseEventFilter(uint32_t type)
0186         : X11EventFilter(type)
0187     {
0188     }
0189     ~XKeyPressReleaseEventFilter() override = default;
0190 
0191     bool event(xcb_generic_event_t *event) override
0192     {
0193         xcb_key_press_event_t *ke = reinterpret_cast<xcb_key_press_event_t *>(event);
0194         if (ke->event == ke->root) {
0195             const uint8_t eventType = event->response_type & ~0x80;
0196             if (eventType == XCB_KEY_PRESS) {
0197                 input()->keyboard()->processKey(ke->detail - 8, InputRedirection::KeyboardKeyPressed, std::chrono::milliseconds(ke->time));
0198             } else {
0199                 input()->keyboard()->processKey(ke->detail - 8, InputRedirection::KeyboardKeyReleased, std::chrono::milliseconds(ke->time));
0200             }
0201         }
0202         return false;
0203     }
0204 };
0205 
0206 XInputIntegration::XInputIntegration(Display *display, QObject *parent)
0207     : QObject(parent)
0208     , m_x11Display(display)
0209 {
0210 }
0211 
0212 XInputIntegration::~XInputIntegration() = default;
0213 
0214 void XInputIntegration::init()
0215 {
0216     Display *dpy = display();
0217     int xi_opcode, event, error;
0218     // init XInput extension
0219     if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
0220         qCDebug(KWIN_X11STANDALONE) << "XInputExtension not present";
0221         return;
0222     }
0223 
0224     // verify that the XInput extension is at at least version 2.0
0225     int major = 2, minor = 2;
0226     int result = XIQueryVersion(dpy, &major, &minor);
0227     if (result != Success) {
0228         qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput 2.2, trying 2.0";
0229         minor = 0;
0230         if (XIQueryVersion(dpy, &major, &minor) != Success) {
0231             qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
0232             return;
0233         }
0234     }
0235     m_hasXInput = true;
0236     m_xiOpcode = xi_opcode;
0237     m_majorVersion = major;
0238     m_minorVersion = minor;
0239     qCDebug(KWIN_X11STANDALONE) << "Has XInput support" << m_majorVersion << "." << m_minorVersion;
0240 }
0241 
0242 void XInputIntegration::setCursor(X11Cursor *cursor)
0243 {
0244     m_x11Cursor = QPointer<X11Cursor>(cursor);
0245 }
0246 
0247 void XInputIntegration::startListening()
0248 {
0249     // this assumes KWin is the only one setting events on the root window
0250     // given Qt's source code this seems to be true. If it breaks, we need to change
0251     XIEventMask evmasks[1];
0252     unsigned char mask1[XIMaskLen(XI_LASTEVENT)];
0253 
0254     memset(mask1, 0, sizeof(mask1));
0255 
0256     XISetMask(mask1, XI_RawMotion);
0257     XISetMask(mask1, XI_RawButtonPress);
0258     XISetMask(mask1, XI_RawButtonRelease);
0259     if (m_majorVersion >= 2 && m_minorVersion >= 1) {
0260         // we need to listen to all events, which is only available with XInput 2.1
0261         XISetMask(mask1, XI_RawKeyPress);
0262         XISetMask(mask1, XI_RawKeyRelease);
0263     }
0264     if (m_majorVersion >= 2 && m_minorVersion >= 2) {
0265         // touch events since 2.2
0266         XISetMask(mask1, XI_TouchBegin);
0267         XISetMask(mask1, XI_TouchUpdate);
0268         XISetMask(mask1, XI_TouchOwnership);
0269         XISetMask(mask1, XI_TouchEnd);
0270     }
0271 
0272     evmasks[0].deviceid = XIAllMasterDevices;
0273     evmasks[0].mask_len = sizeof(mask1);
0274     evmasks[0].mask = mask1;
0275     XISelectEvents(display(), rootWindow(), evmasks, 1);
0276 
0277     m_xiEventFilter = std::make_unique<XInputEventFilter>(m_xiOpcode);
0278     m_xiEventFilter->setCursor(m_x11Cursor);
0279     m_xiEventFilter->setDisplay(display());
0280     m_keyPressFilter = std::make_unique<XKeyPressReleaseEventFilter>(XCB_KEY_PRESS);
0281     m_keyReleaseFilter = std::make_unique<XKeyPressReleaseEventFilter>(XCB_KEY_RELEASE);
0282 
0283     // install the input event spies also relevant for X11 platform
0284     input()->installInputEventSpy(new ModifierOnlyShortcuts);
0285 }
0286 
0287 }