diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -511,10 +511,6 @@ ${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 @@ XCB::KEYSYMS XCB::SHM XCB::GLX - XCB::CURSOR ) set(kwin_WAYLAND_LIBS diff --git a/cursor.h b/cursor.h --- a/cursor.h +++ b/cursor.h @@ -33,8 +33,6 @@ namespace KWin { -class XInputEventFilter; - /** * @short Replacement for QCursor. * @@ -222,53 +220,6 @@ 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/cursor.cpp b/cursor.cpp --- a/cursor.cpp +++ b/cursor.cpp @@ -25,7 +25,6 @@ #include "keyboard_input.h" #include "main.h" #include "utils.h" -#include "x11eventfilter.h" #include "xcbutils.h" // KDE #include @@ -38,16 +37,6 @@ #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 @@ 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/kcmkwin/kwinrules/CMakeLists.txt b/kcmkwin/kwinrules/CMakeLists.txt --- a/kcmkwin/kwinrules/CMakeLists.txt +++ b/kcmkwin/kwinrules/CMakeLists.txt @@ -3,7 +3,8 @@ 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 --- 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 --- 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 --- a/keyboard_input.h +++ b/keyboard_input.h @@ -47,7 +47,7 @@ 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 --- 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.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -27,6 +27,7 @@ namespace KWin { +class XInputIntegration; class KWIN_EXPORT X11StandalonePlatform : public Platform { @@ -60,6 +61,8 @@ **/ static bool hasGlx(); + XInputIntegration *m_xinputIntegration = nullptr; + }; } diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -18,13 +18,18 @@ 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 @@ 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 @@ 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/x11cursor.h b/plugins/platforms/x11/standalone/x11cursor.h new file mode 100644 --- /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/x11cursor.cpp b/plugins/platforms/x11/standalone/x11cursor.cpp new file mode 100644 --- /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/xinputintegration.h b/plugins/platforms/x11/standalone/xinputintegration.h new file mode 100644 --- /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/plugins/platforms/x11/standalone/xinputintegration.cpp b/plugins/platforms/x11/standalone/xinputintegration.cpp new file mode 100644 --- /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/utils.h b/utils.h --- a/utils.h +++ b/utils.h @@ -179,8 +179,8 @@ 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();