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"