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
master
Martin Gräßlin 9 years ago
parent 9bf7ad4a9f
commit 5a31618461

@ -189,6 +189,8 @@ set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
URL "http://www.x.org" URL "http://www.x.org"
TYPE REQUIRED 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 # All the required XCB components
find_package(XCB 1.10 find_package(XCB 1.10
@ -480,6 +482,7 @@ set(kwin_XLIB_LIBS
${X11_X11_LIB} ${X11_X11_LIB}
${X11_ICE_LIB} ${X11_ICE_LIB}
${X11_SM_LIB} ${X11_SM_LIB}
${X11_Xinput_LIB}
) )
set(kwin_XCB_LIBS set(kwin_XCB_LIBS

@ -10,6 +10,7 @@
#define KWIN_RULES_DIALOG_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_rules_dialog" #define KWIN_RULES_DIALOG_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_rules_dialog"
#cmakedefine01 HAVE_INPUT #cmakedefine01 HAVE_INPUT
#cmakedefine01 HAVE_X11_XCB #cmakedefine01 HAVE_X11_XCB
#cmakedefine01 HAVE_X11_XINPUT
#cmakedefine01 HAVE_DRM #cmakedefine01 HAVE_DRM
#cmakedefine01 HAVE_GBM #cmakedefine01 HAVE_GBM
#cmakedefine01 HAVE_LIBHYBRIS #cmakedefine01 HAVE_LIBHYBRIS

@ -24,18 +24,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "input.h" #include "input.h"
#include "main.h" #include "main.h"
#include "utils.h" #include "utils.h"
#include "x11eventfilter.h"
#include "xcbutils.h" #include "xcbutils.h"
// KDE // KDE
#include <KConfig> #include <KConfig>
#include <KConfigGroup> #include <KConfigGroup>
#include <KSharedConfig> #include <KSharedConfig>
// Qt // Qt
#include <QAbstractEventDispatcher>
#include <QDBusConnection> #include <QDBusConnection>
#include <QScreen> #include <QScreen>
#include <QTimer> #include <QTimer>
// xcb // xcb
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include <xcb/xcb_cursor.h> #include <xcb/xcb_cursor.h>
// X11
#include <X11/Xlib.h>
#if HAVE_X11_XINPUT
#include <X11/extensions/XInput2.h>
#else
#define XI_RawMotion 0
#endif
#include <fixx11h.h>
namespace KWin namespace KWin
{ {
@ -248,13 +258,36 @@ void Cursor::notifyCursorChanged(uint32_t serial)
emit cursorChanged(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) X11Cursor::X11Cursor(QObject *parent)
: Cursor(parent) : Cursor(parent)
, m_timeStamp(XCB_TIME_CURRENT_TIME) , m_timeStamp(XCB_TIME_CURRENT_TIME)
, m_buttonMask(0) , m_buttonMask(0)
, m_resetTimeStampTimer(new QTimer(this)) , m_resetTimeStampTimer(new QTimer(this))
, m_mousePollingTimer(new QTimer(this)) , m_mousePollingTimer(new QTimer(this))
, m_hasXInput(false)
, m_xiOpcode(0)
, m_needsPoll(false)
{ {
initXInput();
m_resetTimeStampTimer->setSingleShot(true); m_resetTimeStampTimer->setSingleShot(true);
connect(m_resetTimeStampTimer, SIGNAL(timeout()), SLOT(resetTimeStamp())); connect(m_resetTimeStampTimer, SIGNAL(timeout()), SLOT(resetTimeStamp()));
// TODO: How often do we really need to poll? // 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() void X11Cursor::doSetPos()
{ {
const QPoint &pos = currentPos(); const QPoint &pos = currentPos();
@ -298,14 +364,64 @@ void X11Cursor::resetTimeStamp()
m_timeStamp = XCB_TIME_CURRENT_TIME; m_timeStamp = XCB_TIME_CURRENT_TIME;
} }
void X11Cursor::aboutToBlock()
{
if (m_needsPoll) {
mousePolled();
m_needsPoll = false;
}
}
void X11Cursor::doStartMousePolling() 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() 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() void X11Cursor::doStartCursorTracking()

@ -33,6 +33,8 @@ class QTimer;
namespace KWin namespace KWin
{ {
class XInputEventFilter;
/** /**
* @short Replacement for QCursor. * @short Replacement for QCursor.
* *
@ -221,6 +223,11 @@ class X11Cursor : public Cursor
Q_OBJECT Q_OBJECT
public: public:
virtual ~X11Cursor(); virtual ~X11Cursor();
void schedulePoll() {
m_needsPoll = true;
}
protected: protected:
virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape); virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
xcb_cursor_t getX11Cursor(const QByteArray &name) override; xcb_cursor_t getX11Cursor(const QByteArray &name) override;
@ -239,14 +246,20 @@ private Q_SLOTS:
*/ */
void resetTimeStamp(); void resetTimeStamp();
void mousePolled(); void mousePolled();
void aboutToBlock();
private: private:
X11Cursor(QObject *parent); X11Cursor(QObject *parent);
void initXInput();
xcb_cursor_t createCursor(const QByteArray &name); xcb_cursor_t createCursor(const QByteArray &name);
QHash<QByteArray, xcb_cursor_t > m_cursors; QHash<QByteArray, xcb_cursor_t > m_cursors;
xcb_timestamp_t m_timeStamp; xcb_timestamp_t m_timeStamp;
uint16_t m_buttonMask; uint16_t m_buttonMask;
QTimer *m_resetTimeStampTimer; QTimer *m_resetTimeStampTimer;
QTimer *m_mousePollingTimer; QTimer *m_mousePollingTimer;
bool m_hasXInput;
int m_xiOpcode;
bool m_needsPoll;
QScopedPointer<XInputEventFilter> m_xiEventFilter;
friend class Cursor; friend class Cursor;
}; };

Loading…
Cancel
Save