KWin
Loading...
Searching...
No Matches
x11_standalone_xinputintegration.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
10#include "core/outputbackend.h"
11#include "gestures.h"
12#include "keyboard_input.h"
13#include "main.h"
14#include "pointer_input.h"
15#include "screenedge.h"
18
20
21#include "effect/globals.h"
22#include "effect/xcb.h"
23#include "input.h"
25#include "workspace.h"
26#include "x11eventfilter.h"
27
28#include <X11/extensions/XI2proto.h>
29#include <X11/extensions/XInput2.h>
30
31#include <linux/input.h>
32
33namespace KWin
34{
35
36static inline qreal fixed1616ToReal(FP1616 val)
37{
38 return (val)*1.0 / (1 << 16);
39}
40
42{
43public:
44 XInputEventFilter(int xi_opcode)
45 : 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})
46 {
47 }
48 ~XInputEventFilter() override = default;
49
50 bool event(xcb_generic_event_t *event) override
51 {
53 switch (ge->event_type) {
54 case XI_RawKeyPress: {
55 auto re = reinterpret_cast<xXIRawEvent *>(event);
56 input()->keyboard()->processKey(re->detail - 8, InputRedirection::KeyboardKeyPressed, std::chrono::milliseconds(re->time));
57 break;
58 }
59 case XI_RawKeyRelease: {
60 auto re = reinterpret_cast<xXIRawEvent *>(event);
61 input()->keyboard()->processKey(re->detail - 8, InputRedirection::KeyboardKeyReleased, std::chrono::milliseconds(re->time));
62 break;
63 }
64 case XI_RawButtonPress: {
65 auto e = reinterpret_cast<xXIRawEvent *>(event);
66 switch (e->detail) {
67 // TODO: this currently ignores left handed settings, for current usage not needed
68 // if we want to use also for global mouse shortcuts, this needs to reflect state correctly
69 case XCB_BUTTON_INDEX_1:
70 input()->pointer()->processButton(BTN_LEFT, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
71 break;
72 case XCB_BUTTON_INDEX_2:
73 input()->pointer()->processButton(BTN_MIDDLE, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
74 break;
75 case XCB_BUTTON_INDEX_3:
76 input()->pointer()->processButton(BTN_RIGHT, InputRedirection::PointerButtonPressed, std::chrono::milliseconds(e->time));
77 break;
78 case XCB_BUTTON_INDEX_4:
79 case XCB_BUTTON_INDEX_5:
80 // vertical axis, ignore on press
81 break;
82 // TODO: further buttons, horizontal scrolling?
83 }
84 }
85 if (m_x11Cursor) {
86 m_x11Cursor->schedulePoll();
87 }
88 break;
89 case XI_RawButtonRelease: {
90 auto e = reinterpret_cast<xXIRawEvent *>(event);
91 switch (e->detail) {
92 // TODO: this currently ignores left handed settings, for current usage not needed
93 // if we want to use also for global mouse shortcuts, this needs to reflect state correctly
94 case XCB_BUTTON_INDEX_1:
95 input()->pointer()->processButton(BTN_LEFT, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
96 break;
97 case XCB_BUTTON_INDEX_2:
98 input()->pointer()->processButton(BTN_MIDDLE, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
99 break;
100 case XCB_BUTTON_INDEX_3:
101 input()->pointer()->processButton(BTN_RIGHT, InputRedirection::PointerButtonReleased, std::chrono::milliseconds(e->time));
102 break;
103 case XCB_BUTTON_INDEX_4:
105 break;
106 case XCB_BUTTON_INDEX_5:
108 break;
109 // TODO: further buttons, horizontal scrolling?
110 }
111 }
112 if (m_x11Cursor) {
113 m_x11Cursor->schedulePoll();
114 }
115 break;
116 case XI_TouchBegin: {
117 auto e = reinterpret_cast<xXIDeviceEvent *>(event);
118 m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)));
119 break;
120 }
121 case XI_TouchUpdate: {
122 auto e = reinterpret_cast<xXIDeviceEvent *>(event);
123 const QPointF touchPosition = QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y));
124 if (e->detail == m_trackingTouchId) {
125 const auto last = m_lastTouchPositions.value(e->detail);
126 workspace()->screenEdges()->gestureRecognizer()->updateSwipeGesture(touchPosition - last);
127 }
128 m_lastTouchPositions.insert(e->detail, touchPosition);
129 break;
130 }
131 case XI_TouchEnd: {
132 auto e = reinterpret_cast<xXIDeviceEvent *>(event);
133 if (e->detail == m_trackingTouchId) {
135 }
136 m_lastTouchPositions.remove(e->detail);
137 m_trackingTouchId = 0;
138 break;
139 }
140 case XI_TouchOwnership: {
141 auto e = reinterpret_cast<xXITouchOwnershipEvent *>(event);
142 auto it = m_lastTouchPositions.constFind(e->touchid);
143 if (it == m_lastTouchPositions.constEnd()) {
144 XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, XIRejectTouch);
145 } else {
146 if (workspace()->screenEdges()->gestureRecognizer()->startSwipeGesture(it.value()) > 0) {
147 m_trackingTouchId = e->touchid;
148 }
149 XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, m_trackingTouchId == e->touchid ? XIAcceptTouch : XIRejectTouch);
150 }
151 break;
152 }
153 default:
154 if (m_x11Cursor) {
155 m_x11Cursor->schedulePoll();
156 }
157 break;
158 }
159 return false;
160 }
161
162 void setCursor(const QPointer<X11Cursor> &cursor)
163 {
164 m_x11Cursor = cursor;
165 }
166 void setDisplay(::Display *display)
167 {
168 m_x11Display = display;
169 }
170
171private:
172 ::Display *display() const
173 {
174 return m_x11Display;
175 }
176
177 QPointer<X11Cursor> m_x11Cursor;
178 ::Display *m_x11Display = nullptr;
179 uint32_t m_trackingTouchId = 0;
180 QHash<uint32_t, QPointF> m_lastTouchPositions;
181};
182
184{
185public:
190 ~XKeyPressReleaseEventFilter() override = default;
191
192 bool event(xcb_generic_event_t *event) override
193 {
194 xcb_key_press_event_t *ke = reinterpret_cast<xcb_key_press_event_t *>(event);
195 if (ke->event == ke->root) {
196 const uint8_t eventType = event->response_type & ~0x80;
197 if (eventType == XCB_KEY_PRESS) {
198 input()->keyboard()->processKey(ke->detail - 8, InputRedirection::KeyboardKeyPressed, std::chrono::milliseconds(ke->time));
199 } else {
200 input()->keyboard()->processKey(ke->detail - 8, InputRedirection::KeyboardKeyReleased, std::chrono::milliseconds(ke->time));
201 }
202 }
203 return false;
204 }
205};
206
208 : QObject(parent)
209 , m_x11Display(display)
210{
211}
212
214
216{
217 ::Display *dpy = display();
218 int xi_opcode, event, error;
219 // init XInput extension
220 if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
221 qCDebug(KWIN_X11STANDALONE) << "XInputExtension not present";
222 return;
223 }
224
225 // verify that the XInput extension is at at least version 2.0
226 int major = 2, minor = 2;
227 int result = XIQueryVersion(dpy, &major, &minor);
228 if (result != Success) {
229 qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput 2.2, trying 2.0";
230 minor = 0;
231 if (XIQueryVersion(dpy, &major, &minor) != Success) {
232 qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
233 return;
234 }
235 }
236 m_hasXInput = true;
237 m_xiOpcode = xi_opcode;
238 m_majorVersion = major;
239 m_minorVersion = minor;
240 qCDebug(KWIN_X11STANDALONE) << "Has XInput support" << m_majorVersion << "." << m_minorVersion;
241}
242
244{
245 m_x11Cursor = QPointer<X11Cursor>(cursor);
246}
247
249{
250 // this assumes KWin is the only one setting events on the root window
251 // given Qt's source code this seems to be true. If it breaks, we need to change
252 XIEventMask evmasks[1];
253 unsigned char mask1[XIMaskLen(XI_LASTEVENT)];
254
255 memset(mask1, 0, sizeof(mask1));
256
257 XISetMask(mask1, XI_RawMotion);
258 XISetMask(mask1, XI_RawButtonPress);
259 XISetMask(mask1, XI_RawButtonRelease);
260 if (m_majorVersion >= 2 && m_minorVersion >= 1) {
261 // we need to listen to all events, which is only available with XInput 2.1
262 XISetMask(mask1, XI_RawKeyPress);
263 XISetMask(mask1, XI_RawKeyRelease);
264 }
265 if (m_majorVersion >= 2 && m_minorVersion >= 2) {
266 // touch events since 2.2
267 XISetMask(mask1, XI_TouchBegin);
268 XISetMask(mask1, XI_TouchUpdate);
269 XISetMask(mask1, XI_TouchOwnership);
270 XISetMask(mask1, XI_TouchEnd);
271 }
272
273 evmasks[0].deviceid = XIAllMasterDevices;
274 evmasks[0].mask_len = sizeof(mask1);
275 evmasks[0].mask = mask1;
276 XISelectEvents(display(), rootWindow(), evmasks, 1);
277
278 m_xiEventFilter = std::make_unique<XInputEventFilter>(m_xiOpcode);
279 m_xiEventFilter->setCursor(m_x11Cursor);
280 m_xiEventFilter->setDisplay(display());
281 m_keyPressFilter = std::make_unique<XKeyPressReleaseEventFilter>(XCB_KEY_PRESS);
282 m_keyReleaseFilter = std::make_unique<XKeyPressReleaseEventFilter>(XCB_KEY_RELEASE);
283
284 // install the input event spies also relevant for X11 platform
286}
287
288}
289
290#include "moc_x11_standalone_xinputintegration.cpp"
void updateSwipeGesture(const QPointF &delta)
Definition gestures.cpp:199
void installInputEventSpy(InputEventSpy *spy)
KeyboardInputRedirection * keyboard() const
Definition input.h:216
PointerInputRedirection * pointer() const
Definition input.h:220
void processKey(uint32_t key, InputRedirection::KeyboardKeyState state, std::chrono::microseconds time, InputDevice *device=nullptr)
void processAxis(InputRedirection::PointerAxis axis, qreal delta, qint32 deltaV120, InputRedirection::PointerAxisSource source, std::chrono::microseconds time, InputDevice *device=nullptr)
void processButton(uint32_t button, InputRedirection::PointerButtonState state, std::chrono::microseconds time, InputDevice *device=nullptr)
GestureRecognizer * gestureRecognizer() const
Definition screenedge.h:362
ScreenEdges * screenEdges() const
~XInputEventFilter() override=default
bool event(xcb_generic_event_t *event) override
void setCursor(const QPointer< X11Cursor > &cursor)
~XInputIntegration() override
XInputIntegration(::Display *display, QObject *parent)
bool event(xcb_generic_event_t *event) override
~XKeyPressReleaseEventFilter() override=default
#define XCB_GE_GENERIC
Definition events.cpp:57
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
Session::Type type
Definition session.cpp:17
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549
struct _XDisplay Display