From 5a3161846177797d4a8d7c28c6ad4f00e2177206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 13 Jan 2016 15:40:02 +0100 Subject: [PATCH] Use XInput for "polling" the mouse positing Replaces the timer based polling approach. If XInput is available we listen for the RawMotion event on the root window and use this to trigger a mouse pointer position. BUG: 357692 FIXED-IN: 5.6.0 REVIEW: 126733 --- CMakeLists.txt | 3 ++ config-kwin.h.cmake | 1 + cursor.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++- cursor.h | 13 +++++ 4 files changed, 135 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6800a1ddf2..bdac6c578e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,8 @@ set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED ) +add_feature_info("XInput" X11_Xinput_FOUND "Required for poll-free mouse cursor updates") +set(HAVE_X11_XINPUT ${X11_Xinput_FOUND}) # All the required XCB components find_package(XCB 1.10 @@ -480,6 +482,7 @@ set(kwin_XLIB_LIBS ${X11_X11_LIB} ${X11_ICE_LIB} ${X11_SM_LIB} + ${X11_Xinput_LIB} ) set(kwin_XCB_LIBS diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake index f055a159f2..0996173d89 100644 --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -10,6 +10,7 @@ #define KWIN_RULES_DIALOG_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_rules_dialog" #cmakedefine01 HAVE_INPUT #cmakedefine01 HAVE_X11_XCB +#cmakedefine01 HAVE_X11_XINPUT #cmakedefine01 HAVE_DRM #cmakedefine01 HAVE_GBM #cmakedefine01 HAVE_LIBHYBRIS diff --git a/cursor.cpp b/cursor.cpp index db68ce7fef..de5c5a2606 100644 --- a/cursor.cpp +++ b/cursor.cpp @@ -24,18 +24,28 @@ along with this program. If not, see . #include "input.h" #include "main.h" #include "utils.h" +#include "x11eventfilter.h" #include "xcbutils.h" // KDE #include #include #include // Qt +#include #include #include #include // xcb #include #include +// X11 +#include +#if HAVE_X11_XINPUT +#include +#else +#define XI_RawMotion 0 +#endif +#include namespace KWin { @@ -248,13 +258,36 @@ void Cursor::notifyCursorChanged(uint32_t serial) emit cursorChanged(serial); } +class XInputEventFilter : public X11EventFilter +{ +public: + XInputEventFilter(X11Cursor *parent, int xi_opcode) + : X11EventFilter(XCB_GE_GENERIC, xi_opcode, XI_RawMotion) + , m_x11Cursor(parent) + {} + virtual ~XInputEventFilter() = default; + + bool event(xcb_generic_event_t *event) override { + Q_UNUSED(event) + m_x11Cursor->schedulePoll(); + return false; + } + +private: + X11Cursor *m_x11Cursor; +}; + X11Cursor::X11Cursor(QObject *parent) : Cursor(parent) , m_timeStamp(XCB_TIME_CURRENT_TIME) , m_buttonMask(0) , m_resetTimeStampTimer(new QTimer(this)) , m_mousePollingTimer(new QTimer(this)) + , m_hasXInput(false) + , m_xiOpcode(0) + , m_needsPoll(false) { + initXInput(); m_resetTimeStampTimer->setSingleShot(true); connect(m_resetTimeStampTimer, SIGNAL(timeout()), SLOT(resetTimeStamp())); // TODO: How often do we really need to poll? @@ -268,6 +301,39 @@ X11Cursor::~X11Cursor() { } +void X11Cursor::initXInput() +{ +#ifndef KCMRULES +#if HAVE_X11_XINPUT + if (qEnvironmentVariableIsSet("KWIN_NO_XI2")) { + return; + } + Display *dpy = display(); + int xi_opcode, event, error; + // init XInput extension + if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) { + return; + } + + // verify that the XInput extension is at at least version 2.0 + int major = 2, minor = 0; + int result = XIQueryVersion(dpy, &major, &minor); + if (result == BadImplementation) { + // Xinput 2.2 returns BadImplementation if checked against 2.0 + major = 2; + minor = 2; + if (XIQueryVersion(dpy, &major, &minor) != Success) { + return; + } + } else if (result != Success) { + return; + } + m_hasXInput = true; + m_xiOpcode = xi_opcode; +#endif +#endif +} + void X11Cursor::doSetPos() { const QPoint &pos = currentPos(); @@ -298,14 +364,64 @@ void X11Cursor::resetTimeStamp() m_timeStamp = XCB_TIME_CURRENT_TIME; } +void X11Cursor::aboutToBlock() +{ + if (m_needsPoll) { + mousePolled(); + m_needsPoll = false; + } +} + void X11Cursor::doStartMousePolling() { - m_mousePollingTimer->start(); + if (m_hasXInput) { +#ifndef KCMRULES +#if HAVE_X11_XINPUT + m_xiEventFilter.reset(new XInputEventFilter(this, m_xiOpcode)); + + // this assumes KWin is the only one setting events on the root window + // given Qt's source code this seems to be true. If it breaks, we need to change + XIEventMask evmasks[1]; + unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; + + memset(mask1, 0, sizeof(mask1)); + + XISetMask(mask1, XI_RawMotion); + + evmasks[0].deviceid = XIAllMasterDevices; + evmasks[0].mask_len = sizeof(mask1); + evmasks[0].mask = mask1; + XISelectEvents(display(), rootWindow(), evmasks, 1); + connect(qApp->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &X11Cursor::aboutToBlock); +#endif +#endif + } else { + m_mousePollingTimer->start(); + } } void X11Cursor::doStopMousePolling() { - m_mousePollingTimer->stop(); + if (m_hasXInput) { +#ifndef KCMRULES +#if HAVE_X11_XINPUT + m_xiEventFilter.reset(); + + XIEventMask evmasks[1]; + unsigned char mask1[(XI_LASTEVENT + 7)/8]; + + memset(mask1, 0, sizeof(mask1)); + + evmasks[0].deviceid = XIAllMasterDevices; + evmasks[0].mask_len = sizeof(mask1); + evmasks[0].mask = mask1; + XISelectEvents(display(), rootWindow(), evmasks, 1); + disconnect(qApp->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &X11Cursor::aboutToBlock); +#endif +#endif + } else { + m_mousePollingTimer->stop(); + } } void X11Cursor::doStartCursorTracking() diff --git a/cursor.h b/cursor.h index d37a0aabcb..e0a3091e02 100644 --- a/cursor.h +++ b/cursor.h @@ -33,6 +33,8 @@ class QTimer; namespace KWin { +class XInputEventFilter; + /** * @short Replacement for QCursor. * @@ -221,6 +223,11 @@ class X11Cursor : public Cursor Q_OBJECT public: virtual ~X11Cursor(); + + void schedulePoll() { + m_needsPoll = true; + } + protected: virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape); xcb_cursor_t getX11Cursor(const QByteArray &name) override; @@ -239,14 +246,20 @@ private Q_SLOTS: */ void resetTimeStamp(); void mousePolled(); + void aboutToBlock(); private: X11Cursor(QObject *parent); + void initXInput(); xcb_cursor_t createCursor(const QByteArray &name); QHash m_cursors; xcb_timestamp_t m_timeStamp; uint16_t m_buttonMask; QTimer *m_resetTimeStampTimer; QTimer *m_mousePollingTimer; + bool m_hasXInput; + int m_xiOpcode; + bool m_needsPoll; + QScopedPointer m_xiEventFilter; friend class Cursor; };