diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,20 +14,7 @@ util/urlhandler.cpp ) -set (kmodifierkeyinfoprovider_impl "dummy") -if (NOT APPLE AND X11_FOUND AND X11_Xkb_FOUND AND XCB_XCB_FOUND) - find_package(Qt5X11Extras ${REQUIRED_QT_VERSION} NO_MODULE) - if (Qt5X11Extras_FOUND) - include_directories ( - ${X11_Xkb_INCLUDE_PATH} - ${X11_Xlib_INCLUDE_PATH} - ${XCB_XCB_INCLUDE_DIR} - ) - set (kmodifierkeyinfoprovider_impl "x11") - endif() -endif () - -set (kguiaddons_LIB_SRCS ${kguiaddons_LIB_SRCS} util/kmodifierkeyinfoprovider_${kmodifierkeyinfoprovider_impl}.cpp) +set (kguiaddons_LIB_SRCS ${kguiaddons_LIB_SRCS} util/kmodifierkeyinfoprovider.cpp) add_library(KF5GuiAddons ${kguiaddons_LIB_SRCS}) @@ -45,8 +32,36 @@ target_compile_definitions(KF5GuiAddons INTERFACE "$") target_link_libraries(KF5GuiAddons PUBLIC Qt5::Gui) -if (kmodifierkeyinfoprovider_impl STREQUAL "x11") - target_link_libraries(KF5GuiAddons LINK_PRIVATE ${X11_LIBRARIES} ${XCB_XCB_LIBRARY} Qt5::X11Extras) +set(WITH_XCB) +if (NOT APPLE AND X11_FOUND AND X11_Xkb_FOUND AND XCB_XCB_FOUND) + find_package(Qt5X11Extras ${REQUIRED_QT_VERSION} NO_MODULE) + if (Qt5X11Extras_FOUND) + target_include_directories (KF5GuiAddons PRIVATE + ${X11_Xkb_INCLUDE_PATH} + ${X11_Xlib_INCLUDE_PATH} + ${XCB_XCB_INCLUDE_DIR} + ) + target_sources(KF5GuiAddons PRIVATE util/kmodifierkeyinfoprovider_xcb.cpp) + target_compile_definitions(KF5GuiAddons PRIVATE -DWITH_XCB) + target_link_libraries(KF5GuiAddons LINK_PRIVATE ${X11_LIBRARIES} ${XCB_XCB_LIBRARY} Qt5::X11Extras) + endif() +endif () + +find_package(Qt5WaylandClient) +find_package(WaylandScanner) +find_package(Wayland COMPONENTS Client) +if(Qt5WaylandClient_FOUND AND WaylandScanner_FOUND AND Wayland_FOUND) + ecm_add_wayland_client_protocol(SRCS PROTOCOL ${CMAKE_CURRENT_SOURCE_DIR}/keystate.xml BASENAME keystate) + execute_process(COMMAND qtwaylandscanner client-code ${CMAKE_CURRENT_SOURCE_DIR}/keystate.xml OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/qwayland-keystate.cpp) + execute_process(COMMAND qtwaylandscanner client-header ${CMAKE_CURRENT_SOURCE_DIR}/keystate.xml OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/qwayland-keystate.h) + + set_property(TARGET KF5GuiAddons PROPERTY C_STANDARD 11) + + target_sources(KF5GuiAddons PRIVATE util/kmodifierkeyinfoprovider_wayland.cpp + ${CMAKE_CURRENT_BINARY_DIR}/qwayland-keystate.cpp + ${SRCS}) + target_compile_definitions(KF5GuiAddons PRIVATE -DWITH_WAYLAND) + target_link_libraries(KF5GuiAddons LINK_PRIVATE Qt5::WaylandClient Wayland::Client) endif() set_target_properties(KF5GuiAddons PROPERTIES VERSION ${KGUIADDONS_VERSION_STRING} diff --git a/src/keystate.xml b/src/keystate.xml new file mode 100644 --- /dev/null +++ b/src/keystate.xml @@ -0,0 +1,43 @@ + + + + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . + ]]> + + + + xxxxxxxxxxxxxxx + + + + + + + + + + + + + + + + + + + + + diff --git a/src/util/kmodifierkeyinfo.cpp b/src/util/kmodifierkeyinfo.cpp --- a/src/util/kmodifierkeyinfo.cpp +++ b/src/util/kmodifierkeyinfo.cpp @@ -20,9 +20,32 @@ #include "kmodifierkeyinfo.h" #include "kmodifierkeyinfoprovider_p.h" +#ifdef WITH_XCB +#include "kmodifierkeyinfoprovider_xcb.h" +#endif +#ifdef WITH_WAYLAND +#include "kmodifierkeyinfoprovider_wayland.h" +#endif + +#include + +KModifierKeyInfoProvider* createProvider() +{ +#ifdef WITH_XCB + if (qGuiApp->platformName() == QLatin1String("xcb")) + return new KModifierKeyInfoProviderXcb; + else +#endif +#ifdef WITH_WAYLAND + if (qGuiApp->platformName() == QLatin1String("wayland")) + return new KModifierKeyInfoProviderWayland; + else +#endif + return new KModifierKeyInfoProvider; +} KModifierKeyInfo::KModifierKeyInfo(QObject *parent) - : QObject(parent), p(new KModifierKeyInfoProvider) + : QObject(parent), p(createProvider()) { connect(p, &KModifierKeyInfoProvider::keyPressed, this, &KModifierKeyInfo::keyPressed); diff --git a/src/util/kmodifierkeyinfoprovider.cpp b/src/util/kmodifierkeyinfoprovider.cpp --- a/src/util/kmodifierkeyinfoprovider.cpp +++ b/src/util/kmodifierkeyinfoprovider.cpp @@ -20,6 +20,29 @@ #include "kmodifierkeyinfoprovider_p.h" +KModifierKeyInfoProvider::KModifierKeyInfoProvider() + : QObject(nullptr) +{ +} + +KModifierKeyInfoProvider::~KModifierKeyInfoProvider() +{ +} + +bool KModifierKeyInfoProvider::setKeyLatched(Qt::Key key, bool latched) +{ + Q_UNUSED(key); + Q_UNUSED(latched); + return false; +} + +bool KModifierKeyInfoProvider::setKeyLocked(Qt::Key key, bool locked) +{ + Q_UNUSED(key); + Q_UNUSED(locked); + return false; +} + bool KModifierKeyInfoProvider::isKeyPressed(Qt::Key key) const { if (m_modifierStates.contains(key)) { @@ -62,4 +85,20 @@ return m_modifierStates.keys(); } -#include "moc_kmodifierkeyinfoprovider_p.cpp" +void KModifierKeyInfoProvider::stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates newState) +{ + auto &state = m_modifierStates[key]; + if (newState != state) { + const auto difference = (newState ^ state); + state = newState; + if (difference & Pressed) { + emit keyPressed(key, newState & Pressed); + } + if (difference & Latched) { + emit keyLatched(key, newState & Latched); + } + if (difference & Locked) { + emit keyLocked(key, newState & Locked); + } + } +} diff --git a/src/util/kmodifierkeyinfoprovider_p.h b/src/util/kmodifierkeyinfoprovider_p.h --- a/src/util/kmodifierkeyinfoprovider_p.h +++ b/src/util/kmodifierkeyinfoprovider_p.h @@ -30,7 +30,7 @@ * the different supported platforms. * @internal */ -class KModifierKeyInfoProvider : public QObject, public QAbstractNativeEventFilter +class KModifierKeyInfoProvider : public QObject { Q_OBJECT @@ -41,6 +41,7 @@ Latched = 0x2, Locked = 0x4 }; + Q_ENUM(ModifierState); Q_DECLARE_FLAGS(ModifierStates, ModifierState) KModifierKeyInfoProvider(); @@ -66,7 +67,7 @@ * @param latched true to latch the key, false to unlatch it * @return true if the key is known, false else */ - bool setKeyLatched(Qt::Key key, bool latched); + virtual bool setKeyLatched(Qt::Key key, bool latched); /** * Detect if a key is locked. @@ -81,7 +82,7 @@ * @param latched true to lock the key, false to unlock it * @return true if the key is known, false else */ - bool setKeyLocked(Qt::Key key, bool locked); + virtual bool setKeyLocked(Qt::Key key, bool locked); /** * Check if a mouse button is pressed. @@ -103,8 +104,6 @@ */ const QList knownKeys() const; - bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; - Q_SIGNALS: void keyLatched(Qt::Key key, bool state); void keyLocked(Qt::Key key, bool state); @@ -114,24 +113,13 @@ void keyRemoved(Qt::Key key); protected: - void xkbUpdateModifierMapping(); - void xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, - unsigned char locked_mods); - void xkbButtonStateChanged(unsigned short ptr_buttons); + void stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates state); -private: // the state of each known modifier QHash m_modifierStates; + // the state of each known mouse button QHash m_buttonStates; - - int m_xkbEv; - bool m_xkbAvailable; - - // maps a Qt::Key to a modifier mask - QHash m_xkbModifiers; - // maps a Qt::MouseButton to a button mask - QHash m_xkbButtons; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KModifierKeyInfoProvider::ModifierStates) diff --git a/src/util/kmodifierkeyinfoprovider_dummy.cpp b/src/util/kmodifierkeyinfoprovider_wayland.h rename from src/util/kmodifierkeyinfoprovider_dummy.cpp rename to src/util/kmodifierkeyinfoprovider_wayland.h --- a/src/util/kmodifierkeyinfoprovider_dummy.cpp +++ b/src/util/kmodifierkeyinfoprovider_wayland.h @@ -1,5 +1,5 @@ /* - Copyright 2009 Michael Leupold + Copyright 2019 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -18,36 +18,25 @@ License along with this library. If not, see . */ -#include "kmodifierkeyinfo.h" -#include "kmodifierkeyinfoprovider_p.h" +#ifndef KMODIFIERKEYINFOPROVIDERWAYLAND_H +#define KMODIFIERKEYINFOPROVIDERWAYLAND_H -KModifierKeyInfoProvider::KModifierKeyInfoProvider() - : QObject(0) -{ -} +#include "kmodifierkeyinfoprovider_p.h" +#include -KModifierKeyInfoProvider::~KModifierKeyInfoProvider() +class KModifierKeyInfoProviderWayland : public KModifierKeyInfoProvider { -} +Q_OBJECT +public: + KModifierKeyInfoProviderWayland(); + ~KModifierKeyInfoProviderWayland(); -bool KModifierKeyInfoProvider::setKeyLatched(Qt::Key key, bool latched) -{ - Q_UNUSED(key); - Q_UNUSED(latched); - return false; -} + bool setKeyLatched(Qt::Key key, bool latched) override; + bool setKeyLocked(Qt::Key key, bool locked) override; -bool KModifierKeyInfoProvider::setKeyLocked(Qt::Key key, bool locked) -{ - Q_UNUSED(key); - Q_UNUSED(locked); - return false; -} +private: + class Private; + QScopedPointer const d; +}; -bool KModifierKeyInfoProvider::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) -{ - Q_UNUSED(eventType) - Q_UNUSED(message) - Q_UNUSED(result) - return false; -} +#endif diff --git a/src/util/kmodifierkeyinfoprovider_wayland.cpp b/src/util/kmodifierkeyinfoprovider_wayland.cpp new file mode 100644 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_wayland.cpp @@ -0,0 +1,94 @@ +/* + Copyright 2019 Aleix Pol Gonzalez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRAKModifierKeyInfoProviderNTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "kmodifierkeyinfoprovider_wayland.h" +#include +#include +#include +#include + +using namespace QtWayland; + +class KModifierKeyInfoProviderWayland::Private : public QtWayland::org_kde_kwin_keystate, public QWaylandClientExtensionTemplate +{ +public: + Private(KModifierKeyInfoProviderWayland* q) : QWaylandClientExtensionTemplate(1), q(q) + { + } + + KModifierKeyInfoProvider::ModifierState toState(state state) + { + switch(state) { + case state_unlocked: + return KModifierKeyInfoProvider::Nothing; + case state_latched: + return KModifierKeyInfoProvider::Latched; + case state_locked: + return KModifierKeyInfoProvider::Locked; + } + Q_UNREACHABLE(); + return KModifierKeyInfoProvider::Nothing; + } + + Qt::Key toKey(key key) + { + switch(key) { + case key_capslock: + return Qt::Key_CapsLock; + case key_numlock: + return Qt::Key_NumLock; + case key_scrolllock: + return Qt::Key_ScrollLock; + } + Q_UNREACHABLE(); + return {}; + } + + void bind(struct ::wl_registry * registry, int id, int version) override { + QWaylandClientExtensionTemplate::bind(registry, id, version); + fetchStates(); + } + + void org_kde_kwin_keystate_stateChanged(uint32_t k, uint32_t s) override { + q->stateUpdated(toKey(key(k)), toState(state(s))); + } + + KModifierKeyInfoProviderWayland* const q; +}; + +KModifierKeyInfoProviderWayland::KModifierKeyInfoProviderWayland() + : d(new Private(this)) +{ + stateUpdated(Qt::Key_CapsLock, KModifierKeyInfoProvider::Nothing); + stateUpdated(Qt::Key_NumLock, KModifierKeyInfoProvider::Nothing); + stateUpdated(Qt::Key_ScrollLock, KModifierKeyInfoProvider::Nothing); +} + +KModifierKeyInfoProviderWayland::~KModifierKeyInfoProviderWayland() = default; + +bool KModifierKeyInfoProviderWayland::setKeyLatched(Qt::Key /*key*/, bool /*latched*/) +{ + return false; +} + +bool KModifierKeyInfoProviderWayland::setKeyLocked(Qt::Key /*key*/, bool /*locked*/) +{ + return false; +} diff --git a/src/util/kmodifierkeyinfoprovider_xcb.h b/src/util/kmodifierkeyinfoprovider_xcb.h new file mode 100644 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_xcb.h @@ -0,0 +1,54 @@ +/* + Copyright 2009 Michael Leupold + Copyright 2013 Martin Gräßlin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef KMODIFIERKEYINFOPROVIDERXCB_H +#define KMODIFIERKEYINFOPROVIDERXCB_H + +#include "kmodifierkeyinfoprovider_p.h" + +class KModifierKeyInfoProviderXcb : public KModifierKeyInfoProvider, public QAbstractNativeEventFilter +{ +Q_OBJECT +public: + KModifierKeyInfoProviderXcb(); + ~KModifierKeyInfoProviderXcb(); + + bool setKeyLatched(Qt::Key key, bool latched) override; + bool setKeyLocked(Qt::Key key, bool locked) override; + + bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; + + void xkbUpdateModifierMapping(); + void xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, + unsigned char locked_mods); + void xkbButtonStateChanged(unsigned short ptr_buttons); + +private: + int m_xkbEv; + bool m_xkbAvailable; + + // maps a Qt::Key to a modifier mask + QHash m_xkbModifiers; + // maps a Qt::MouseButton to a button mask + QHash m_xkbButtons; +}; + +#endif diff --git a/src/util/kmodifierkeyinfoprovider_x11.cpp b/src/util/kmodifierkeyinfoprovider_xcb.cpp rename from src/util/kmodifierkeyinfoprovider_x11.cpp rename to src/util/kmodifierkeyinfoprovider_xcb.cpp --- a/src/util/kmodifierkeyinfoprovider_x11.cpp +++ b/src/util/kmodifierkeyinfoprovider_xcb.cpp @@ -20,7 +20,7 @@ */ #include "kmodifierkeyinfo.h" -#include "kmodifierkeyinfoprovider_p.h" +#include "kmodifierkeyinfoprovider_xcb.h" #include #include @@ -61,16 +61,15 @@ XFree(modStr); if (nameEqual) { XkbVirtualModsToReal(xkb, 1 << i, &mask); - break; + break; } } } return mask; } -KModifierKeyInfoProvider::KModifierKeyInfoProvider() - : QObject(nullptr) - , QAbstractNativeEventFilter() +KModifierKeyInfoProviderXcb::KModifierKeyInfoProviderXcb() + : KModifierKeyInfoProvider() , m_xkbEv(0) , m_xkbAvailable(false) { @@ -111,14 +110,14 @@ } } -KModifierKeyInfoProvider::~KModifierKeyInfoProvider() +KModifierKeyInfoProviderXcb::~KModifierKeyInfoProviderXcb() { if (m_xkbAvailable) { QCoreApplication::instance()->removeNativeEventFilter(this); } } -bool KModifierKeyInfoProvider::setKeyLatched(Qt::Key key, bool latched) +bool KModifierKeyInfoProviderXcb::setKeyLatched(Qt::Key key, bool latched) { if (!m_xkbModifiers.contains(key)) { return false; @@ -128,7 +127,7 @@ m_xkbModifiers[key], latched ? m_xkbModifiers[key] : 0); } -bool KModifierKeyInfoProvider::setKeyLocked(Qt::Key key, bool locked) +bool KModifierKeyInfoProviderXcb::setKeyLocked(Qt::Key key, bool locked) { if (!m_xkbModifiers.contains(key)) { return false; @@ -208,7 +207,7 @@ } _xkb_event; } -bool KModifierKeyInfoProvider::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) +bool KModifierKeyInfoProviderXcb::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) { Q_UNUSED(result) if (!m_xkbAvailable || eventType != "xcb_generic_event_t") { @@ -233,12 +232,11 @@ return false; } -void KModifierKeyInfoProvider::xkbModifierStateChanged(unsigned char mods, +void KModifierKeyInfoProviderXcb::xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, unsigned char locked_mods) { // detect keyboard modifiers - ModifierStates oldState; ModifierStates newState; QHash::const_iterator it; @@ -248,7 +246,6 @@ continue; } newState = Nothing; - oldState = m_modifierStates[it.key()]; // determine the new state if (mods & it.value()) { @@ -261,23 +258,11 @@ newState |= Locked; } - if (newState != oldState) { - m_modifierStates[it.key()] = newState; - - if ((newState ^ oldState) & Pressed) { - emit keyPressed(it.key(), newState & Pressed); - } - if ((newState ^ oldState) & Latched) { - emit keyLatched(it.key(), newState & Latched); - } - if ((newState ^ oldState) & Locked) { - emit keyLocked(it.key(), newState & Locked); - } - } + stateUpdated(it.key(), newState); } } -void KModifierKeyInfoProvider::xkbButtonStateChanged(unsigned short ptr_buttons) +void KModifierKeyInfoProviderXcb::xkbButtonStateChanged(unsigned short ptr_buttons) { // detect mouse button states bool newButtonState; @@ -293,7 +278,7 @@ } } -void KModifierKeyInfoProvider::xkbUpdateModifierMapping() +void KModifierKeyInfoProviderXcb::xkbUpdateModifierMapping() { if (!m_xkbAvailable) { return;