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 }