File indexing completed on 2024-11-10 04:56:36

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