Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/x11/windowed/x11windowed_backend.cpp
Show All 33 Lines | |||||
34 | #include <QSocketNotifier> | 34 | #include <QSocketNotifier> | ||
35 | // kwayland | 35 | // kwayland | ||
36 | #include <KWayland/Server/buffer_interface.h> | 36 | #include <KWayland/Server/buffer_interface.h> | ||
37 | #include <KWayland/Server/display.h> | 37 | #include <KWayland/Server/display.h> | ||
38 | #include <KWayland/Server/seat_interface.h> | 38 | #include <KWayland/Server/seat_interface.h> | ||
39 | #include <KWayland/Server/surface_interface.h> | 39 | #include <KWayland/Server/surface_interface.h> | ||
40 | // xcb | 40 | // xcb | ||
41 | #include <xcb/xcb_keysyms.h> | 41 | #include <xcb/xcb_keysyms.h> | ||
42 | // X11 | ||||
43 | #if HAVE_X11_XINPUT | ||||
44 | #include "ge_event_mem_mover.h" | ||||
45 | #include <X11/extensions/XInput2.h> | ||||
46 | #include <X11/extensions/XI2proto.h> | ||||
47 | #endif | ||||
42 | // system | 48 | // system | ||
43 | #include <linux/input.h> | 49 | #include <linux/input.h> | ||
44 | #include <X11/Xlib-xcb.h> | 50 | #include <X11/Xlib-xcb.h> | ||
45 | 51 | | |||
46 | namespace KWin | 52 | namespace KWin | ||
47 | { | 53 | { | ||
48 | 54 | | |||
49 | X11WindowedBackend::X11WindowedBackend(QObject *parent) | 55 | X11WindowedBackend::X11WindowedBackend(QObject *parent) | ||
Show All 37 Lines | 90 | if (c && !xcb_connection_has_error(c)) { | |||
87 | m_display = xDisplay; | 93 | m_display = xDisplay; | ||
88 | for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); | 94 | for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); | ||
89 | it.rem; | 95 | it.rem; | ||
90 | --screen, xcb_screen_next(&it)) { | 96 | --screen, xcb_screen_next(&it)) { | ||
91 | if (screen == m_screenNumber) { | 97 | if (screen == m_screenNumber) { | ||
92 | m_screen = it.data; | 98 | m_screen = it.data; | ||
93 | } | 99 | } | ||
94 | } | 100 | } | ||
101 | initXInput(); | ||||
95 | XRenderUtils::init(m_connection, m_screen->root); | 102 | XRenderUtils::init(m_connection, m_screen->root); | ||
96 | createWindow(); | 103 | createWindow(); | ||
97 | connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); | 104 | connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); | ||
98 | connect(this, &X11WindowedBackend::cursorChanged, this, | 105 | connect(this, &X11WindowedBackend::cursorChanged, this, | ||
99 | [this] { | 106 | [this] { | ||
100 | createCursor(softwareCursor(), softwareCursorHotspot()); | 107 | createCursor(softwareCursor(), softwareCursorHotspot()); | ||
101 | } | 108 | } | ||
102 | ); | 109 | ); | ||
103 | setReady(true); | 110 | setReady(true); | ||
104 | waylandServer()->seat()->setHasPointer(true); | 111 | waylandServer()->seat()->setHasPointer(true); | ||
105 | waylandServer()->seat()->setHasKeyboard(true); | 112 | waylandServer()->seat()->setHasKeyboard(true); | ||
113 | if (m_hasXInput) { | ||||
114 | waylandServer()->seat()->setHasTouch(true); | ||||
115 | } | ||||
106 | emit screensQueried(); | 116 | emit screensQueried(); | ||
107 | } else { | 117 | } else { | ||
108 | emit initFailed(); | 118 | emit initFailed(); | ||
109 | } | 119 | } | ||
110 | } | 120 | } | ||
111 | 121 | | |||
122 | void X11WindowedBackend::initXInput() | ||||
123 | { | ||||
124 | #if HAVE_X11_XINPUT | ||||
125 | int xi_opcode, event, error; | ||||
126 | // init XInput extension | ||||
127 | if (!XQueryExtension(m_display, "XInputExtension", &xi_opcode, &event, &error)) { | ||||
128 | qCDebug(KWIN_X11WINDOWED) << "XInputExtension not present"; | ||||
129 | return; | ||||
130 | } | ||||
131 | | ||||
132 | // verify that the XInput extension is at at least version 2.0 | ||||
133 | int major = 2, minor = 2; | ||||
134 | int result = XIQueryVersion(m_display, &major, &minor); | ||||
135 | if (result != Success) { | ||||
136 | qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput 2.2, trying 2.0"; | ||||
137 | minor = 0; | ||||
138 | if (XIQueryVersion(m_display, &major, &minor) != Success) { | ||||
139 | qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput"; | ||||
140 | return; | ||||
141 | } | ||||
142 | } | ||||
143 | m_xiOpcode = xi_opcode; | ||||
144 | m_majorVersion = major; | ||||
145 | m_minorVersion = minor; | ||||
146 | m_hasXInput = m_majorVersion >=2 && m_minorVersion >= 2; | ||||
zzag: Shouldn't it be just m_hasXInput = true? | |||||
No, that was on purpose. It's the minimum version supporting touch. graesslin: No, that was on purpose. It's the minimum version supporting touch. | |||||
147 | #endif | ||||
148 | } | ||||
149 | | ||||
112 | void X11WindowedBackend::createWindow() | 150 | void X11WindowedBackend::createWindow() | ||
113 | { | 151 | { | ||
114 | Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); | 152 | Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); | ||
115 | Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection); | 153 | Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection); | ||
116 | for (int i = 0; i < initialOutputCount(); ++i) { | 154 | for (int i = 0; i < initialOutputCount(); ++i) { | ||
117 | Output o; | 155 | Output o; | ||
118 | o.window = xcb_generate_id(m_connection); | 156 | o.window = xcb_generate_id(m_connection); | ||
119 | uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | 157 | uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | ||
Show All 14 Lines | |||||
134 | if (!m_windows.isEmpty()) { | 172 | if (!m_windows.isEmpty()) { | ||
135 | const auto &p = m_windows.last(); | 173 | const auto &p = m_windows.last(); | ||
136 | o.internalPosition = QPoint(p.internalPosition.x() + p.size.width() / p.scale, 0); | 174 | o.internalPosition = QPoint(p.internalPosition.x() + p.size.width() / p.scale, 0); | ||
137 | } | 175 | } | ||
138 | xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, o.window, m_screen->root, | 176 | xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, o.window, m_screen->root, | ||
139 | 0, 0, o.size.width(), o.size.height(), | 177 | 0, 0, o.size.width(), o.size.height(), | ||
140 | 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); | 178 | 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); | ||
141 | 179 | | |||
180 | // select xinput 2 events | ||||
181 | initXInputForWindow(o.window); | ||||
182 | | ||||
142 | o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2()); | 183 | o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2()); | ||
143 | o.winInfo->setWindowType(NET::Normal); | 184 | o.winInfo->setWindowType(NET::Normal); | ||
144 | o.winInfo->setPid(QCoreApplication::applicationPid()); | 185 | o.winInfo->setPid(QCoreApplication::applicationPid()); | ||
145 | QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); | 186 | QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); | ||
146 | auto addIcon = [&o, &windowIcon] (const QSize &size) { | 187 | auto addIcon = [&o, &windowIcon] (const QSize &size) { | ||
147 | if (windowIcon.actualSize(size) != size) { | 188 | if (windowIcon.actualSize(size) != size) { | ||
148 | return; | 189 | return; | ||
149 | } | 190 | } | ||
Show All 16 Lines | |||||
166 | m_windows << o; | 207 | m_windows << o; | ||
167 | } | 208 | } | ||
168 | 209 | | |||
169 | updateWindowTitle(); | 210 | updateWindowTitle(); | ||
170 | 211 | | |||
171 | xcb_flush(m_connection); | 212 | xcb_flush(m_connection); | ||
172 | } | 213 | } | ||
173 | 214 | | |||
215 | void X11WindowedBackend::initXInputForWindow(xcb_window_t window) | ||||
216 | { | ||||
217 | if (!m_hasXInput) { | ||||
218 | return; | ||||
219 | } | ||||
220 | #if HAVE_X11_XINPUT | ||||
221 | XIEventMask evmasks[1]; | ||||
222 | unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; | ||||
223 | | ||||
224 | memset(mask1, 0, sizeof(mask1)); | ||||
225 | XISetMask(mask1, XI_TouchBegin); | ||||
226 | XISetMask(mask1, XI_TouchUpdate); | ||||
227 | XISetMask(mask1, XI_TouchOwnership); | ||||
228 | XISetMask(mask1, XI_TouchEnd); | ||||
229 | evmasks[0].deviceid = XIAllMasterDevices; | ||||
230 | evmasks[0].mask_len = sizeof(mask1); | ||||
231 | evmasks[0].mask = mask1; | ||||
232 | XISelectEvents(m_display, window, evmasks, 1); | ||||
233 | #else | ||||
zzag: Missing Q_UNUSED. | |||||
234 | Q_UNUSED(window) | ||||
235 | #endif | ||||
236 | } | ||||
237 | | ||||
174 | void X11WindowedBackend::startEventReading() | 238 | void X11WindowedBackend::startEventReading() | ||
175 | { | 239 | { | ||
176 | QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); | 240 | QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); | ||
177 | auto processXcbEvents = [this] { | 241 | auto processXcbEvents = [this] { | ||
178 | while (auto event = xcb_poll_for_event(m_connection)) { | 242 | while (auto event = xcb_poll_for_event(m_connection)) { | ||
179 | handleEvent(event); | 243 | handleEvent(event); | ||
180 | free(event); | 244 | free(event); | ||
181 | } | 245 | } | ||
182 | xcb_flush(m_connection); | 246 | xcb_flush(m_connection); | ||
183 | }; | 247 | }; | ||
184 | connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); | 248 | connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); | ||
185 | connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); | 249 | connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); | ||
186 | connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); | 250 | connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); | ||
187 | } | 251 | } | ||
188 | 252 | | |||
253 | #if HAVE_X11_XINPUT | ||||
would be best in x11/common as a new file rather than duplicating it davidedmundson: would be best in x11/common as a new file rather than duplicating it | |||||
Sure can do that. I didn't want to touch the standalone code as I don't test that any more. graesslin: Sure can do that. I didn't want to touch the standalone code as I don't test that any more. | |||||
254 | | ||||
255 | static inline qreal fixed1616ToReal(FP1616 val) | ||||
256 | { | ||||
257 | return (val) * 1.0 / (1 << 16); | ||||
258 | } | ||||
259 | #endif | ||||
260 | | ||||
189 | void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) | 261 | void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) | ||
190 | { | 262 | { | ||
191 | const uint8_t eventType = e->response_type & ~0x80; | 263 | const uint8_t eventType = e->response_type & ~0x80; | ||
192 | switch (eventType) { | 264 | switch (eventType) { | ||
193 | case XCB_BUTTON_PRESS: | 265 | case XCB_BUTTON_PRESS: | ||
194 | case XCB_BUTTON_RELEASE: | 266 | case XCB_BUTTON_RELEASE: | ||
195 | handleButtonPress(reinterpret_cast<xcb_button_press_event_t*>(e)); | 267 | handleButtonPress(reinterpret_cast<xcb_button_press_event_t*>(e)); | ||
196 | break; | 268 | break; | ||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | |||||
244 | case XCB_EXPOSE: | 316 | case XCB_EXPOSE: | ||
245 | handleExpose(reinterpret_cast<xcb_expose_event_t*>(e)); | 317 | handleExpose(reinterpret_cast<xcb_expose_event_t*>(e)); | ||
246 | break; | 318 | break; | ||
247 | case XCB_MAPPING_NOTIFY: | 319 | case XCB_MAPPING_NOTIFY: | ||
248 | if (m_keySymbols) { | 320 | if (m_keySymbols) { | ||
249 | xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast<xcb_mapping_notify_event_t*>(e)); | 321 | xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast<xcb_mapping_notify_event_t*>(e)); | ||
250 | } | 322 | } | ||
251 | break; | 323 | break; | ||
324 | #if HAVE_X11_XINPUT | ||||
325 | case XCB_GE_GENERIC: { | ||||
326 | GeEventMemMover ge(e); | ||||
327 | auto te = reinterpret_cast<xXIDeviceEvent*>(e); | ||||
328 | auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [te] (const Output &o) { return o.window == te->event; }); | ||||
329 | if (it == m_windows.constEnd()) { | ||||
330 | break; | ||||
331 | } | ||||
332 | QPointF position{ | ||||
333 | fixed1616ToReal(te->root_x) - (*it).xPosition.x() + (*it).internalPosition.x(), | ||||
334 | fixed1616ToReal(te->root_y) - (*it).xPosition.y() + (*it).internalPosition.y() | ||||
335 | }; | ||||
336 | position /= it->scale; | ||||
337 | | ||||
338 | switch (ge->event_type) { | ||||
339 | | ||||
340 | case XI_TouchBegin: { | ||||
341 | touchDown(te->detail, position, te->time); | ||||
342 | touchFrame(); | ||||
343 | break; | ||||
344 | } | ||||
345 | case XI_TouchUpdate: { | ||||
346 | touchMotion(te->detail, position, te->time); | ||||
347 | touchFrame(); | ||||
348 | break; | ||||
349 | } | ||||
350 | case XI_TouchEnd: { | ||||
351 | touchUp(te->detail, te->time); | ||||
352 | touchFrame(); | ||||
353 | break; | ||||
354 | } | ||||
355 | case XI_TouchOwnership: { | ||||
356 | auto te = reinterpret_cast<xXITouchOwnershipEvent*>(e); | ||||
357 | XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch); | ||||
358 | break; | ||||
359 | } | ||||
360 | } | ||||
361 | break; | ||||
362 | } | ||||
363 | #endif | ||||
252 | default: | 364 | default: | ||
253 | break; | 365 | break; | ||
254 | } | 366 | } | ||
255 | } | 367 | } | ||
256 | 368 | | |||
257 | void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time) | 369 | void X11WindowedBackend::grabKeyboard(xcb_timestamp_t time) | ||
258 | { | 370 | { | ||
259 | const bool oldState = m_keyboardGrabbed; | 371 | const bool oldState = m_keyboardGrabbed; | ||
▲ Show 20 Lines • Show All 236 Lines • Show Last 20 Lines |
Shouldn't it be just m_hasXInput = true?