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 --- /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 --- a/plugins/platforms/x11/standalone/xinputintegration.cpp +++ b/plugins/platforms/x11/standalone/xinputintegration.cpp @@ -24,6 +24,7 @@ #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 @@ 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 --- a/plugins/platforms/x11/windowed/CMakeLists.txt +++ b/plugins/platforms/x11/windowed/CMakeLists.txt @@ -9,6 +9,9 @@ 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.h b/plugins/platforms/x11/windowed/x11windowed_backend.h --- a/plugins/platforms/x11/windowed/x11windowed_backend.h +++ b/plugins/platforms/x11/windowed/x11windowed_backend.h @@ -87,6 +87,8 @@ 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 @@ 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; }; } diff --git a/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/plugins/platforms/x11/windowed/x11windowed_backend.cpp --- a/plugins/platforms/x11/windowed/x11windowed_backend.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_backend.cpp @@ -39,6 +39,12 @@ #include // xcb #include +// X11 +#if HAVE_X11_XINPUT +#include "ge_event_mem_mover.h" +#include +#include +#endif // system #include #include @@ -92,6 +98,7 @@ m_screen = it.data; } } + initXInput(); XRenderUtils::init(m_connection, m_screen->root); createWindow(); connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading); @@ -103,12 +110,43 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; }