diff --git a/CMakeLists.txt b/CMakeLists.txt
index e89dd86049..06aa83c88a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -511,10 +511,6 @@ set(kwin_XLIB_LIBS
${X11_SM_LIB}
)
-if(X11_Xinput_FOUND)
- set(kwin_XLIB_LIBS ${kwin_XLIB_LIBS} ${X11_Xinput_LIB})
-endif()
-
set(kwin_XCB_LIBS
XCB::XCB
XCB::XFIXES
@@ -527,7 +523,6 @@ set(kwin_XCB_LIBS
XCB::KEYSYMS
XCB::SHM
XCB::GLX
- XCB::CURSOR
)
set(kwin_WAYLAND_LIBS
diff --git a/cursor.cpp b/cursor.cpp
index 73736e0596..69f4918b25 100644
--- a/cursor.cpp
+++ b/cursor.cpp
@@ -25,7 +25,6 @@ along with this program. If not, see .
#include "keyboard_input.h"
#include "main.h"
#include "utils.h"
-#include "x11eventfilter.h"
#include "xcbutils.h"
// KDE
#include
@@ -38,16 +37,6 @@ along with this program. If not, see .
#include
// xcb
#include
-#include
-// X11
-#include
-#if HAVE_X11_XINPUT
-#include
-#include
-#else
-#define XI_RawMotion 0
-#endif
-#include
namespace KWin
{
@@ -246,253 +235,6 @@ void Cursor::notifyCursorChanged(uint32_t serial)
emit cursorChanged(serial);
}
-#ifndef KCMRULES
-class XInputEventFilter : public X11EventFilter
-{
-public:
- XInputEventFilter(X11Cursor *parent, int xi_opcode)
- : X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease})
- , m_x11Cursor(parent)
- {}
- virtual ~XInputEventFilter() = default;
-
- bool event(xcb_generic_event_t *event) override {
- xcb_ge_generic_event_t *ge = reinterpret_cast(event);
- switch (ge->event_type) {
- case XI_RawKeyPress:
- input()->keyboard()->xkb()->updateKey(reinterpret_cast(event)->detail - 8, InputRedirection::KeyboardKeyPressed);
- break;
- case XI_RawKeyRelease:
- input()->keyboard()->xkb()->updateKey(reinterpret_cast(event)->detail - 8, InputRedirection::KeyboardKeyReleased);
- break;
- default:
- m_x11Cursor->schedulePoll();
- break;
- }
- return false;
- }
-
-private:
- X11Cursor *m_x11Cursor;
-};
-#endif
-
-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?
- m_mousePollingTimer->setInterval(50);
- connect(m_mousePollingTimer, SIGNAL(timeout()), SLOT(mousePolled()));
-
- connect(this, &Cursor::themeChanged, this, [this] { m_cursors.clear(); });
-}
-
-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;
- input()->keyboard()->xkb()->reconfigure();
-#endif
-#endif
-}
-
-void X11Cursor::doSetPos()
-{
- const QPoint &pos = currentPos();
- xcb_warp_pointer(connection(), XCB_WINDOW_NONE, rootWindow(), 0, 0, 0, 0, pos.x(), pos.y());
- // call default implementation to emit signal
- Cursor::doSetPos();
-}
-
-void X11Cursor::doGetPos()
-{
- if (m_timeStamp != XCB_TIME_CURRENT_TIME &&
- m_timeStamp == xTime()) {
- // time stamps did not change, no need to query again
- return;
- }
- m_timeStamp = xTime();
- Xcb::Pointer pointer(rootWindow());
- if (pointer.isNull()) {
- return;
- }
- m_buttonMask = pointer->mask;
- updatePos(pointer->root_x, pointer->root_y);
- m_resetTimeStampTimer->start(0);
-}
-
-void X11Cursor::resetTimeStamp()
-{
- m_timeStamp = XCB_TIME_CURRENT_TIME;
-}
-
-void X11Cursor::aboutToBlock()
-{
- if (m_needsPoll) {
- mousePolled();
- m_needsPoll = false;
- }
-}
-
-void X11Cursor::doStartMousePolling()
-{
- 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);
- XISetMask(mask1, XI_RawButtonPress);
- XISetMask(mask1, XI_RawButtonRelease);
- XISetMask(mask1, XI_RawKeyPress);
- XISetMask(mask1, XI_RawKeyRelease);
-
- 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()
-{
- 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()
-{
- xcb_xfixes_select_cursor_input(connection(), rootWindow(), XCB_XFIXES_CURSOR_NOTIFY_MASK_DISPLAY_CURSOR);
-}
-
-void X11Cursor::doStopCursorTracking()
-{
- xcb_xfixes_select_cursor_input(connection(), rootWindow(), 0);
-}
-
-void X11Cursor::mousePolled()
-{
- static QPoint lastPos = currentPos();
- static uint16_t lastMask = m_buttonMask;
- doGetPos(); // Update if needed
- if (lastPos != currentPos() || lastMask != m_buttonMask) {
- emit mouseChanged(currentPos(), lastPos,
- x11ToQtMouseButtons(m_buttonMask), x11ToQtMouseButtons(lastMask),
- x11ToQtKeyboardModifiers(m_buttonMask), x11ToQtKeyboardModifiers(lastMask));
- lastPos = currentPos();
- lastMask = m_buttonMask;
- }
-}
-
-xcb_cursor_t X11Cursor::getX11Cursor(Qt::CursorShape shape)
-{
- return getX11Cursor(cursorName(shape));
-}
-
-xcb_cursor_t X11Cursor::getX11Cursor(const QByteArray &name)
-{
- auto it = m_cursors.constFind(name);
- if (it != m_cursors.constEnd()) {
- return it.value();
- }
- return createCursor(name);
-}
-
-xcb_cursor_t X11Cursor::createCursor(const QByteArray &name)
-{
- if (name.isEmpty()) {
- return XCB_CURSOR_NONE;
- }
- xcb_cursor_context_t *ctx;
- if (xcb_cursor_context_new(connection(), defaultScreen(), &ctx) < 0) {
- return XCB_CURSOR_NONE;
- }
- xcb_cursor_t cursor = xcb_cursor_load_cursor(ctx, name.constData());
- if (cursor == XCB_CURSOR_NONE) {
- const auto &names = cursorAlternativeNames(name);
- for (auto cit = names.begin(); cit != names.end(); ++cit) {
- cursor = xcb_cursor_load_cursor(ctx, (*cit).constData());
- if (cursor != XCB_CURSOR_NONE) {
- break;
- }
- }
- }
- if (cursor != XCB_CURSOR_NONE) {
- m_cursors.insert(name, cursor);
- }
- xcb_cursor_context_free(ctx);
- return cursor;
-}
-
QVector Cursor::cursorAlternativeNames(const QByteArray &name) const
{
static const QHash> alternatives = {
diff --git a/cursor.h b/cursor.h
index 4044d9902f..a5f82b7244 100644
--- a/cursor.h
+++ b/cursor.h
@@ -33,8 +33,6 @@ class QTimer;
namespace KWin
{
-class XInputEventFilter;
-
/**
* @short Replacement for QCursor.
*
@@ -222,53 +220,6 @@ private:
KWIN_SINGLETON(Cursor)
};
-class KWIN_EXPORT X11Cursor : public Cursor
-{
- Q_OBJECT
-public:
- X11Cursor(QObject *parent);
- virtual ~X11Cursor();
-
- void schedulePoll() {
- m_needsPoll = true;
- }
-
-protected:
- virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
- xcb_cursor_t getX11Cursor(const QByteArray &name) override;
- virtual void doSetPos();
- virtual void doGetPos();
- virtual void doStartMousePolling();
- virtual void doStopMousePolling();
- virtual void doStartCursorTracking();
- virtual void doStopCursorTracking();
-
-private Q_SLOTS:
- /**
- * Because of QTimer's and the impossibility to get events for all mouse
- * movements (at least I haven't figured out how) the position needs
- * to be also refetched after each return to the event loop.
- */
- void resetTimeStamp();
- void mousePolled();
- void aboutToBlock();
-private:
- 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;
-#ifndef KCMRULES
- QScopedPointer m_xiEventFilter;
-#endif
- friend class Cursor;
-};
-
/**
* @brief Implementation using the InputRedirection framework to get pointer positions.
*
diff --git a/kcmkwin/kwinrules/CMakeLists.txt b/kcmkwin/kwinrules/CMakeLists.txt
index f4e22716b1..fbe3e6d21b 100644
--- a/kcmkwin/kwinrules/CMakeLists.txt
+++ b/kcmkwin/kwinrules/CMakeLists.txt
@@ -3,7 +3,8 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwinrules\")
add_definitions(-DKCMRULES)
########### next target ###############
-set (kwinrules_MOC_HDRS yesnobox.h ../../client_machine.h ../../cursor.h)
+include_directories(../../)
+set (kwinrules_MOC_HDRS yesnobox.h ../../client_machine.h ../../cursor.h ../../plugins/platforms/x11/standalone/x11cursor.h)
qt5_wrap_cpp(kwinrules_MOC_SRCS ${kwinrules_MOC_HDRS})
set(kwinrules_SRCS ruleswidget.cpp ruleslist.cpp kwinsrc.cpp detectwidget.cpp ${kwinrules_MOC_SRCS})
diff --git a/kcmkwin/kwinrules/detectwidget.cpp b/kcmkwin/kwinrules/detectwidget.cpp
index 2a9aab91c6..2e0cfa3964 100644
--- a/kcmkwin/kwinrules/detectwidget.cpp
+++ b/kcmkwin/kwinrules/detectwidget.cpp
@@ -17,7 +17,7 @@
*/
#include "detectwidget.h"
-#include "../../cursor.h"
+#include "../../plugins/platforms/x11/standalone/x11cursor.h"
#include
#include
diff --git a/kcmkwin/kwinrules/kwinsrc.cpp b/kcmkwin/kwinrules/kwinsrc.cpp
index f4707ed6bb..4f48db8240 100644
--- a/kcmkwin/kwinrules/kwinsrc.cpp
+++ b/kcmkwin/kwinrules/kwinsrc.cpp
@@ -21,6 +21,7 @@
#include "ruleslist.h"
#include "../../cursor.cpp"
+#include "../../plugins/platforms/x11/standalone/x11cursor.cpp"
#include "../../rules.cpp"
#include "../../placement.cpp"
#include "../../options.cpp"
diff --git a/keyboard_input.h b/keyboard_input.h
index 1609d81fcc..e47f06dceb 100644
--- a/keyboard_input.h
+++ b/keyboard_input.h
@@ -47,7 +47,7 @@ namespace LibInput
class Device;
}
-class Xkb
+class KWIN_EXPORT Xkb
{
public:
Xkb(InputRedirection *input);
diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt
index b0ceefcb50..4b7a6c70ab 100644
--- a/plugins/platforms/x11/standalone/CMakeLists.txt
+++ b/plugins/platforms/x11/standalone/CMakeLists.txt
@@ -1,16 +1,24 @@
set(X11PLATFORM_SOURCES
edge.cpp
logging.cpp
+ x11cursor.cpp
x11_platform.cpp
screens_xrandr.cpp
)
+if(X11_Xinput_FOUND)
+ set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} xinputintegration.cpp)
+endif()
+
if(HAVE_EPOXY_GLX)
set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} glxbackend.cpp)
endif()
add_library(KWinX11Platform MODULE ${X11PLATFORM_SOURCES})
-target_link_libraries(KWinX11Platform eglx11common kwin Qt5::X11Extras)
+target_link_libraries(KWinX11Platform eglx11common kwin Qt5::X11Extras XCB::CURSOR)
+if(X11_Xinput_FOUND)
+ target_link_libraries(KWinX11Platform ${X11_Xinput_LIB})
+endif()
install(
TARGETS
diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp
index 13a43a4306..8212d1e0e0 100644
--- a/plugins/platforms/x11/standalone/x11_platform.cpp
+++ b/plugins/platforms/x11/standalone/x11_platform.cpp
@@ -18,13 +18,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*********************************************************************/
#include "x11_platform.h"
-#include "cursor.h"
+#include "x11cursor.h"
#include "edge.h"
+#include
#include
#if HAVE_EPOXY_GLX
#include "glxbackend.h"
#endif
+#if HAVE_X11_XINPUT
+#include "xinputintegration.h"
+#endif
#include "eglonxbackend.h"
+#include "keyboard_input.h"
#include "logging.h"
#include "screens_xrandr.h"
#include "options.h"
@@ -41,6 +46,18 @@ namespace KWin
X11StandalonePlatform::X11StandalonePlatform(QObject *parent)
: Platform(parent)
{
+#if HAVE_X11_XINPUT
+ if (!qEnvironmentVariableIsSet("KWIN_NO_XI2")) {
+ m_xinputIntegration = new XInputIntegration(this);
+ m_xinputIntegration->init();
+ if (!m_xinputIntegration->hasXinput()) {
+ delete m_xinputIntegration;
+ m_xinputIntegration = nullptr;
+ } else {
+ connect(kwinApp(), &Application::workspaceCreated, m_xinputIntegration, &XInputIntegration::startListening);
+ }
+ }
+#endif
}
X11StandalonePlatform::~X11StandalonePlatform() = default;
@@ -87,7 +104,16 @@ Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges)
void X11StandalonePlatform::createPlatformCursor(QObject *parent)
{
- new X11Cursor(parent);
+ auto c = new X11Cursor(parent, m_xinputIntegration != nullptr);
+#if HAVE_X11_XINPUT
+ if (m_xinputIntegration) {
+ m_xinputIntegration->setCursor(c);
+ // we know we have xkb already
+ auto xkb = input()->keyboard()->xkb();
+ m_xinputIntegration->setXkb(xkb);
+ xkb->reconfigure();
+ }
+#endif
}
bool X11StandalonePlatform::requiresCompositing() const
diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h
index 4577d3def3..e3a670e0bf 100644
--- a/plugins/platforms/x11/standalone/x11_platform.h
+++ b/plugins/platforms/x11/standalone/x11_platform.h
@@ -27,6 +27,7 @@ along with this program. If not, see .
namespace KWin
{
+class XInputIntegration;
class KWIN_EXPORT X11StandalonePlatform : public Platform
{
@@ -60,6 +61,8 @@ private:
**/
static bool hasGlx();
+ XInputIntegration *m_xinputIntegration = nullptr;
+
};
}
diff --git a/plugins/platforms/x11/standalone/x11cursor.cpp b/plugins/platforms/x11/standalone/x11cursor.cpp
new file mode 100644
index 0000000000..21f762c2cc
--- /dev/null
+++ b/plugins/platforms/x11/standalone/x11cursor.cpp
@@ -0,0 +1,176 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#include "x11cursor.h"
+#include "input.h"
+#include "keyboard_input.h"
+#include "utils.h"
+#include "xcbutils.h"
+
+#include
+#include
+
+#include
+
+namespace KWin
+{
+
+X11Cursor::X11Cursor(QObject *parent, bool xInputSupport)
+ : Cursor(parent)
+ , m_timeStamp(XCB_TIME_CURRENT_TIME)
+ , m_buttonMask(0)
+ , m_resetTimeStampTimer(new QTimer(this))
+ , m_mousePollingTimer(new QTimer(this))
+ , m_hasXInput(xInputSupport)
+ , m_needsPoll(false)
+{
+ m_resetTimeStampTimer->setSingleShot(true);
+ connect(m_resetTimeStampTimer, SIGNAL(timeout()), SLOT(resetTimeStamp()));
+ // TODO: How often do we really need to poll?
+ m_mousePollingTimer->setInterval(50);
+ connect(m_mousePollingTimer, SIGNAL(timeout()), SLOT(mousePolled()));
+
+ connect(this, &Cursor::themeChanged, this, [this] { m_cursors.clear(); });
+
+ if (m_hasXInput) {
+ connect(qApp->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &X11Cursor::aboutToBlock);
+ }
+}
+
+X11Cursor::~X11Cursor()
+{
+}
+
+void X11Cursor::doSetPos()
+{
+ const QPoint &pos = currentPos();
+ xcb_warp_pointer(connection(), XCB_WINDOW_NONE, rootWindow(), 0, 0, 0, 0, pos.x(), pos.y());
+ // call default implementation to emit signal
+ Cursor::doSetPos();
+}
+
+void X11Cursor::doGetPos()
+{
+ if (m_timeStamp != XCB_TIME_CURRENT_TIME &&
+ m_timeStamp == xTime()) {
+ // time stamps did not change, no need to query again
+ return;
+ }
+ m_timeStamp = xTime();
+ Xcb::Pointer pointer(rootWindow());
+ if (pointer.isNull()) {
+ return;
+ }
+ m_buttonMask = pointer->mask;
+ updatePos(pointer->root_x, pointer->root_y);
+ m_resetTimeStampTimer->start(0);
+}
+
+void X11Cursor::resetTimeStamp()
+{
+ m_timeStamp = XCB_TIME_CURRENT_TIME;
+}
+
+void X11Cursor::aboutToBlock()
+{
+ if (m_needsPoll) {
+ mousePolled();
+ m_needsPoll = false;
+ }
+}
+
+void X11Cursor::doStartMousePolling()
+{
+ if (!m_hasXInput) {
+ m_mousePollingTimer->start();
+ }
+}
+
+void X11Cursor::doStopMousePolling()
+{
+ if (!m_hasXInput) {
+ m_mousePollingTimer->stop();
+ }
+}
+
+void X11Cursor::doStartCursorTracking()
+{
+ xcb_xfixes_select_cursor_input(connection(), rootWindow(), XCB_XFIXES_CURSOR_NOTIFY_MASK_DISPLAY_CURSOR);
+}
+
+void X11Cursor::doStopCursorTracking()
+{
+ xcb_xfixes_select_cursor_input(connection(), rootWindow(), 0);
+}
+
+void X11Cursor::mousePolled()
+{
+ static QPoint lastPos = currentPos();
+ static uint16_t lastMask = m_buttonMask;
+ doGetPos(); // Update if needed
+ if (lastPos != currentPos() || lastMask != m_buttonMask) {
+ emit mouseChanged(currentPos(), lastPos,
+ x11ToQtMouseButtons(m_buttonMask), x11ToQtMouseButtons(lastMask),
+ x11ToQtKeyboardModifiers(m_buttonMask), x11ToQtKeyboardModifiers(lastMask));
+ lastPos = currentPos();
+ lastMask = m_buttonMask;
+ }
+}
+
+xcb_cursor_t X11Cursor::getX11Cursor(Qt::CursorShape shape)
+{
+ return getX11Cursor(cursorName(shape));
+}
+
+xcb_cursor_t X11Cursor::getX11Cursor(const QByteArray &name)
+{
+ auto it = m_cursors.constFind(name);
+ if (it != m_cursors.constEnd()) {
+ return it.value();
+ }
+ return createCursor(name);
+}
+
+xcb_cursor_t X11Cursor::createCursor(const QByteArray &name)
+{
+ if (name.isEmpty()) {
+ return XCB_CURSOR_NONE;
+ }
+ xcb_cursor_context_t *ctx;
+ if (xcb_cursor_context_new(connection(), defaultScreen(), &ctx) < 0) {
+ return XCB_CURSOR_NONE;
+ }
+ xcb_cursor_t cursor = xcb_cursor_load_cursor(ctx, name.constData());
+ if (cursor == XCB_CURSOR_NONE) {
+ const auto &names = cursorAlternativeNames(name);
+ for (auto cit = names.begin(); cit != names.end(); ++cit) {
+ cursor = xcb_cursor_load_cursor(ctx, (*cit).constData());
+ if (cursor != XCB_CURSOR_NONE) {
+ break;
+ }
+ }
+ }
+ if (cursor != XCB_CURSOR_NONE) {
+ m_cursors.insert(name, cursor);
+ }
+ xcb_cursor_context_free(ctx);
+ return cursor;
+}
+
+}
diff --git a/plugins/platforms/x11/standalone/x11cursor.h b/plugins/platforms/x11/standalone/x11cursor.h
new file mode 100644
index 0000000000..b480c7c711
--- /dev/null
+++ b/plugins/platforms/x11/standalone/x11cursor.h
@@ -0,0 +1,72 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#ifndef KWIN_X11CURSOR_H
+#define KWIN_X11CURSOR_H
+#include "cursor.h"
+
+namespace KWin
+{
+
+class KWIN_EXPORT X11Cursor : public Cursor
+{
+ Q_OBJECT
+public:
+ X11Cursor(QObject *parent, bool xInputSupport = false);
+ virtual ~X11Cursor();
+
+ void schedulePoll() {
+ m_needsPoll = true;
+ }
+
+protected:
+ virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
+ xcb_cursor_t getX11Cursor(const QByteArray &name) override;
+ virtual void doSetPos();
+ virtual void doGetPos();
+ virtual void doStartMousePolling();
+ virtual void doStopMousePolling();
+ virtual void doStartCursorTracking();
+ virtual void doStopCursorTracking();
+
+private Q_SLOTS:
+ /**
+ * Because of QTimer's and the impossibility to get events for all mouse
+ * movements (at least I haven't figured out how) the position needs
+ * to be also refetched after each return to the event loop.
+ */
+ void resetTimeStamp();
+ void mousePolled();
+ void aboutToBlock();
+private:
+ 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;
+ bool m_needsPoll;
+ friend class Cursor;
+};
+
+
+}
+
+#endif
diff --git a/plugins/platforms/x11/standalone/xinputintegration.cpp b/plugins/platforms/x11/standalone/xinputintegration.cpp
new file mode 100644
index 0000000000..908b26ab26
--- /dev/null
+++ b/plugins/platforms/x11/standalone/xinputintegration.cpp
@@ -0,0 +1,154 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#include "xinputintegration.h"
+#include "logging.h"
+#include "x11cursor.h"
+
+#include "keyboard_input.h"
+#include "x11eventfilter.h"
+#include
+
+#include
+#include
+
+namespace KWin
+{
+
+class XInputEventFilter : public X11EventFilter
+{
+public:
+ XInputEventFilter(int xi_opcode)
+ : X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease})
+ {}
+ virtual ~XInputEventFilter() = default;
+
+ bool event(xcb_generic_event_t *event) override {
+ xcb_ge_generic_event_t *ge = reinterpret_cast(event);
+ switch (ge->event_type) {
+ case XI_RawKeyPress:
+ if (m_xkb) {
+ m_xkb->updateKey(reinterpret_cast(event)->detail - 8, InputRedirection::KeyboardKeyPressed);
+ }
+ break;
+ case XI_RawKeyRelease:
+ if (m_xkb) {
+ m_xkb->updateKey(reinterpret_cast(event)->detail - 8, InputRedirection::KeyboardKeyReleased);
+ }
+ break;
+ default:
+ if (m_x11Cursor) {
+ m_x11Cursor->schedulePoll();
+ }
+ break;
+ }
+ return false;
+ }
+
+ void setCursor(const QPointer &cursor) {
+ m_x11Cursor = cursor;
+ }
+ void setXkb(Xkb *xkb) {
+ m_xkb = xkb;
+ }
+
+private:
+ QPointer m_x11Cursor;
+ // TODO: QPointer
+ Xkb *m_xkb = nullptr;
+};
+
+
+XInputIntegration::XInputIntegration(QObject *parent)
+ : QObject(parent)
+{
+}
+
+XInputIntegration::~XInputIntegration() = default;
+
+void XInputIntegration::init()
+{
+ Display *dpy = display();
+ int xi_opcode, event, error;
+ // init XInput extension
+ if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
+ qCDebug(KWIN_X11STANDALONE) << "XInputExtension not present";
+ 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) {
+ qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
+ return;
+ }
+ } else if (result != Success) {
+ qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
+ return;
+ }
+ m_hasXInput = true;
+ m_xiOpcode = xi_opcode;
+ m_majorVersion = major;
+ m_minorVersion = minor;
+ qCDebug(KWIN_X11STANDALONE) << "Has XInput support" << m_majorVersion << "." << m_minorVersion;
+}
+
+void XInputIntegration::setCursor(X11Cursor *cursor)
+{
+ m_x11Cursor = QPointer(cursor);
+}
+
+void XInputIntegration::setXkb(Xkb *xkb)
+{
+ m_xkb = xkb;
+}
+
+void XInputIntegration::startListening()
+{
+ // 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);
+ XISetMask(mask1, XI_RawButtonPress);
+ XISetMask(mask1, XI_RawButtonRelease);
+ if (m_majorVersion >= 2 && m_minorVersion >= 1) {
+ // we need to listen to all events, which is only available with XInput 2.1
+ XISetMask(mask1, XI_RawKeyPress);
+ XISetMask(mask1, XI_RawKeyRelease);
+ }
+
+ evmasks[0].deviceid = XIAllMasterDevices;
+ evmasks[0].mask_len = sizeof(mask1);
+ evmasks[0].mask = mask1;
+ XISelectEvents(display(), rootWindow(), evmasks, 1);
+ m_xiEventFilter.reset(new XInputEventFilter(m_xiOpcode));
+ m_xiEventFilter->setCursor(m_x11Cursor);
+ m_xiEventFilter->setXkb(m_xkb);
+}
+
+}
diff --git a/plugins/platforms/x11/standalone/xinputintegration.h b/plugins/platforms/x11/standalone/xinputintegration.h
new file mode 100644
index 0000000000..ef540acbbd
--- /dev/null
+++ b/plugins/platforms/x11/standalone/xinputintegration.h
@@ -0,0 +1,65 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2016 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#ifndef KWIN_XINPUTINTEGRATION_H
+#define KWIN_XINPUTINTEGRATION_H
+
+#include
+#include
+#include
+
+namespace KWin
+{
+
+class XInputEventFilter;
+class X11Cursor;
+class Xkb;
+
+class XInputIntegration : public QObject
+{
+ Q_OBJECT
+public:
+ explicit XInputIntegration(QObject *parent);
+ virtual ~XInputIntegration();
+
+ void init();
+ void startListening();
+
+ bool hasXinput() const {
+ return m_hasXInput;
+ }
+ void setCursor(X11Cursor *cursor);
+ void setXkb(Xkb *xkb);
+
+private:
+
+ bool m_hasXInput = false;
+ int m_xiOpcode = 0;
+ int m_majorVersion = 0;
+ int m_minorVersion = 0;
+ QPointer m_x11Cursor;
+ // TODO: QPointer
+ Xkb *m_xkb = nullptr;
+
+ QScopedPointer m_xiEventFilter;
+};
+
+}
+
+#endif
diff --git a/utils.h b/utils.h
index 96543d8a7e..e9fa912328 100644
--- a/utils.h
+++ b/utils.h
@@ -179,8 +179,8 @@ public:
int qtToX11Button(Qt::MouseButton button);
Qt::MouseButton x11ToQtMouseButton(int button);
int qtToX11State(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
-Qt::MouseButtons x11ToQtMouseButtons(int state);
-Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state);
+Qt::MouseButtons KWIN_EXPORT x11ToQtMouseButtons(int state);
+Qt::KeyboardModifiers KWIN_EXPORT x11ToQtKeyboardModifiers(int state);
void checkNonExistentClients();