From d49642ca151f4d793d7da70f879b00d3ed9d5b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Fl=C3=B6ser?= Date: Wed, 5 Dec 2018 18:46:35 +0100 Subject: [PATCH] Add touch support to x11 windowed platform Summary: This change inits XInput extension, listens for touch events and forwards them to our platform API. Thus touch events are forwarded on a nested wayland session on X11. Please note that I only tested this change on Xwayland. Test Plan: Run nested kwin_wayland with two outputs and looked into debug console Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D17369 --- .../platforms/x11/common/ge_event_mem_mover.h | 55 +++++++++ .../x11/standalone/xinputintegration.cpp | 27 +---- plugins/platforms/x11/windowed/CMakeLists.txt | 3 + .../x11/windowed/x11windowed_backend.cpp | 112 ++++++++++++++++++ .../x11/windowed/x11windowed_backend.h | 7 ++ 5 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 plugins/platforms/x11/common/ge_event_mem_mover.h diff --git a/plugins/platforms/x11/common/ge_event_mem_mover.h b/plugins/platforms/x11/common/ge_event_mem_mover.h new file mode 100644 index 0000000000..8b6b96db1f --- /dev/null +++ b/plugins/platforms/x11/common/ge_event_mem_mover.h @@ -0,0 +1,55 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2018 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#pragma once + +#include + +#include + +namespace KWin +{ + +class GeEventMemMover +{ +public: + GeEventMemMover(xcb_generic_event_t *event) + : m_event(reinterpret_cast(event)) + { + // xcb event structs contain stuff that wasn't on the wire, the full_sequence field + // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. + // Move this data back to have the same layout in memory as it was on the wire + // and allow casting, overwriting the full_sequence field. + memmove((char*) m_event + 32, (char*) m_event + 36, m_event->length * 4); + } + ~GeEventMemMover() + { + // move memory layout back, so that Qt can do the same without breaking + memmove((char*) m_event + 36, (char *) m_event + 32, m_event->length * 4); + } + + xcb_ge_generic_event_t *operator->() const { + return m_event; + } + +private: + xcb_ge_generic_event_t *m_event; +}; + +} diff --git a/plugins/platforms/x11/standalone/xinputintegration.cpp b/plugins/platforms/x11/standalone/xinputintegration.cpp index 32250708fa..01b6e55b9a 100644 --- a/plugins/platforms/x11/standalone/xinputintegration.cpp +++ b/plugins/platforms/x11/standalone/xinputintegration.cpp @@ -24,6 +24,7 @@ along with this program. If not, see . #include "platform.h" #include "screenedge.h" #include "x11cursor.h" +#include "ge_event_mem_mover.h" #include "input.h" #include "x11eventfilter.h" @@ -43,32 +44,6 @@ static inline qreal fixed1616ToReal(FP1616 val) return (val) * 1.0 / (1 << 16); } -class GeEventMemMover -{ -public: - GeEventMemMover(xcb_generic_event_t *event) - : m_event(reinterpret_cast(event)) - { - // xcb event structs contain stuff that wasn't on the wire, the full_sequence field - // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. - // Move this data back to have the same layout in memory as it was on the wire - // and allow casting, overwriting the full_sequence field. - memmove((char*) m_event + 32, (char*) m_event + 36, m_event->length * 4); - } - ~GeEventMemMover() - { - // move memory layout back, so that Qt can do the same without breaking - memmove((char*) m_event + 36, (char *) m_event + 32, m_event->length * 4); - } - - xcb_ge_generic_event_t *operator->() const { - return m_event; - } - -private: - xcb_ge_generic_event_t *m_event; -}; - class XInputEventFilter : public X11EventFilter { public: diff --git a/plugins/platforms/x11/windowed/CMakeLists.txt b/plugins/platforms/x11/windowed/CMakeLists.txt index 280c0b9f09..51ebf364e0 100644 --- a/plugins/platforms/x11/windowed/CMakeLists.txt +++ b/plugins/platforms/x11/windowed/CMakeLists.txt @@ -9,6 +9,9 @@ include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl) add_library(KWinWaylandX11Backend MODULE ${X11BACKEND_SOURCES}) set_target_properties(KWinWaylandX11Backend PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.waylandbackends/") target_link_libraries(KWinWaylandX11Backend eglx11common kwin kwinxrenderutils X11::XCB SceneQPainterBackend SceneOpenGLBackend) +if(X11_Xinput_FOUND) + target_link_libraries(KWinWaylandX11Backend ${X11_Xinput_LIB}) +endif() install( TARGETS diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/plugins/platforms/x11/windowed/x11windowed_backend.cpp index 2e26a00923..22d47fb3ef 100644 --- a/plugins/platforms/x11/windowed/x11windowed_backend.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_backend.cpp @@ -39,6 +39,12 @@ along with this program. If not, see . #include // xcb #include +// X11 +#if HAVE_X11_XINPUT +#include "ge_event_mem_mover.h" +#include +#include +#endif // system #include #include @@ -92,6 +98,7 @@ void X11WindowedBackend::init() m_screen = it.data; } } + initXInput(); XRenderUtils::init(m_connection, m_screen->root); createWindow(); connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); @@ -103,12 +110,43 @@ void X11WindowedBackend::init() setReady(true); waylandServer()->seat()->setHasPointer(true); waylandServer()->seat()->setHasKeyboard(true); + if (m_hasXInput) { + waylandServer()->seat()->setHasTouch(true); + } emit screensQueried(); } else { emit initFailed(); } } +void X11WindowedBackend::initXInput() +{ +#if HAVE_X11_XINPUT + int xi_opcode, event, error; + // init XInput extension + if (!XQueryExtension(m_display, "XInputExtension", &xi_opcode, &event, &error)) { + qCDebug(KWIN_X11WINDOWED) << "XInputExtension not present"; + return; + } + + // verify that the XInput extension is at at least version 2.0 + int major = 2, minor = 2; + int result = XIQueryVersion(m_display, &major, &minor); + if (result != Success) { + qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput 2.2, trying 2.0"; + minor = 0; + if (XIQueryVersion(m_display, &major, &minor) != Success) { + qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput"; + return; + } + } + m_xiOpcode = xi_opcode; + m_majorVersion = major; + m_minorVersion = minor; + m_hasXInput = m_majorVersion >=2 && m_minorVersion >= 2; +#endif +} + void X11WindowedBackend::createWindow() { Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection); @@ -139,6 +177,9 @@ void X11WindowedBackend::createWindow() 0, 0, o.size.width(), o.size.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); + // select xinput 2 events + initXInputForWindow(o.window); + o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2()); o.winInfo->setWindowType(NET::Normal); o.winInfo->setPid(QCoreApplication::applicationPid()); @@ -171,6 +212,29 @@ void X11WindowedBackend::createWindow() xcb_flush(m_connection); } +void X11WindowedBackend::initXInputForWindow(xcb_window_t window) +{ + if (!m_hasXInput) { + return; + } +#if HAVE_X11_XINPUT + XIEventMask evmasks[1]; + unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; + + memset(mask1, 0, sizeof(mask1)); + XISetMask(mask1, XI_TouchBegin); + XISetMask(mask1, XI_TouchUpdate); + XISetMask(mask1, XI_TouchOwnership); + XISetMask(mask1, XI_TouchEnd); + evmasks[0].deviceid = XIAllMasterDevices; + evmasks[0].mask_len = sizeof(mask1); + evmasks[0].mask = mask1; + XISelectEvents(m_display, window, evmasks, 1); +#else + Q_UNUSED(window) +#endif +} + void X11WindowedBackend::startEventReading() { QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); @@ -186,6 +250,14 @@ void X11WindowedBackend::startEventReading() connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); } +#if HAVE_X11_XINPUT + +static inline qreal fixed1616ToReal(FP1616 val) +{ + return (val) * 1.0 / (1 << 16); +} +#endif + void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) { const uint8_t eventType = e->response_type & ~0x80; @@ -249,6 +321,46 @@ void X11WindowedBackend::handleEvent(xcb_generic_event_t *e) xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast(e)); } break; +#if HAVE_X11_XINPUT + case XCB_GE_GENERIC: { + GeEventMemMover ge(e); + auto te = reinterpret_cast(e); + auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [te] (const Output &o) { return o.window == te->event; }); + if (it == m_windows.constEnd()) { + break; + } + QPointF position{ + fixed1616ToReal(te->root_x) - (*it).xPosition.x() + (*it).internalPosition.x(), + fixed1616ToReal(te->root_y) - (*it).xPosition.y() + (*it).internalPosition.y() + }; + position /= it->scale; + + switch (ge->event_type) { + + case XI_TouchBegin: { + touchDown(te->detail, position, te->time); + touchFrame(); + break; + } + case XI_TouchUpdate: { + touchMotion(te->detail, position, te->time); + touchFrame(); + break; + } + case XI_TouchEnd: { + touchUp(te->detail, te->time); + touchFrame(); + break; + } + case XI_TouchOwnership: { + auto te = reinterpret_cast(e); + XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch); + break; + } + } + break; + } +#endif default: break; } diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.h b/plugins/platforms/x11/windowed/x11windowed_backend.h index e17149a790..b34392cf91 100644 --- a/plugins/platforms/x11/windowed/x11windowed_backend.h +++ b/plugins/platforms/x11/windowed/x11windowed_backend.h @@ -87,6 +87,8 @@ private: void handleExpose(xcb_expose_event_t *event); void updateSize(xcb_configure_notify_event_t *event); void createCursor(const QImage &img, const QPoint &hotspot); + void initXInputForWindow(xcb_window_t window); + void initXInput(); xcb_connection_t *m_connection = nullptr; xcb_screen_t *m_screen = nullptr; @@ -106,6 +108,11 @@ private: xcb_cursor_t m_cursor = XCB_CURSOR_NONE; Display *m_display = nullptr; bool m_keyboardGrabbed = false; + + bool m_hasXInput = false; + int m_xiOpcode = 0; + int m_majorVersion = 0; + int m_minorVersion = 0; }; }