From 415412e15d6f22cbbc33193383a5b5b398c58158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 3 Jul 2015 07:45:43 +0200 Subject: [PATCH 1/2] [mousepad] Split X11 implementation into dedicated method --- plugins/mousepad/mousepadplugin.cpp | 12 ++++++++---- plugins/mousepad/mousepadplugin.h | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/mousepad/mousepadplugin.cpp b/plugins/mousepad/mousepadplugin.cpp index 3d69959af..38b7c8e6e 100644 --- a/plugins/mousepad/mousepadplugin.cpp +++ b/plugins/mousepad/mousepadplugin.cpp @@ -91,6 +91,14 @@ MousepadPlugin::~MousepadPlugin() } bool MousepadPlugin::receivePackage(const NetworkPackage& np) +{ + if (m_x11) { + return handlePackageX11(np); + } + return false; +} + +bool MousepadPlugin::handlePackageX11(const NetworkPackage &np) { //qDebug() << np.serialize(); @@ -110,10 +118,6 @@ bool MousepadPlugin::receivePackage(const NetworkPackage& np) int specialKey = np.get("specialKey", 0); if (isSingleClick || isDoubleClick || isMiddleClick || isRightClick || isSingleHold || isScroll || !key.isEmpty() || specialKey) { - - if (!m_x11) { - return false; - } Display *display = QX11Info::display(); if(!display) { return false; diff --git a/plugins/mousepad/mousepadplugin.h b/plugins/mousepad/mousepadplugin.h index b1ebcaae8..4bef0dba9 100644 --- a/plugins/mousepad/mousepadplugin.h +++ b/plugins/mousepad/mousepadplugin.h @@ -41,8 +41,10 @@ public: virtual void connected() { } private: + bool handlePackageX11(const NetworkPackage& np); + FakeKey* m_fakekey; - bool m_x11; + const bool m_x11; }; From 2506f0e78aecbbf7949e683ad6a4742af1d25114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 3 Jul 2015 09:25:34 +0200 Subject: [PATCH 2/2] [mousepad] Integrate with KWayland for fake input support KWayland provides a FakeInput interface which KWin as a compositor supports. This can be used to fake input events on Wayland. As it's a KWin specific interface it won't work with other Wayland compositors. If the compositor does not support the required interface, the module just doesn't do anything. Support in the implementation is completely optional. Adding fake input events circumvents the Wayland security model. Because of that the interface is designed in a way that the security decision can be done by the compositor and can be delegated to the user. On first input event kdeconnect tries to "authenticate" with the compositor. This gives the compositor the possibility to e.g. ask the user whether it should be allowed. It's not done on startup or of load module as that would show such a message way to early and the user would not be able to connect it with his action on the smartphone. REVIEW: 124238 --- plugins/mousepad/CMakeLists.txt | 10 ++- plugins/mousepad/config-mousepad.h.cmake | 1 + plugins/mousepad/mousepadplugin.cpp | 94 +++++++++++++++++++++++- plugins/mousepad/mousepadplugin.h | 20 ++++- 4 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 plugins/mousepad/config-mousepad.h.cmake diff --git a/plugins/mousepad/CMakeLists.txt b/plugins/mousepad/CMakeLists.txt index 88716c719..ecfdbb7b2 100644 --- a/plugins/mousepad/CMakeLists.txt +++ b/plugins/mousepad/CMakeLists.txt @@ -6,9 +6,17 @@ find_package(XTest REQUIRED) find_package(X11 REQUIRED) find_package(LibFakeKey REQUIRED) find_package(Qt5X11Extras REQUIRED) +find_package(KF5Wayland) + +set(HAVE_WAYLAND ${KF5Wayland_FOUND}) +configure_file(config-mousepad.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-mousepad.h ) kdeconnect_add_plugin(kdeconnect_mousepad JSON kdeconnect_mousepad.json SOURCES ${kdeconnect_mousepad_SRCS}) include_directories(${XTEST_INCLUDE_DIRS} ${X11_INCLUDE_DIR} ${LibFakeKey_INCLUDE_DIRS}) -target_link_libraries(kdeconnect_mousepad kdeconnectcore Qt5::Gui Qt5::X11Extras ${X11_LIBRARIES} ${XTEST_LIBRARIES} ${LibFakeKey_LIBRARIES}) +target_link_libraries(kdeconnect_mousepad kdeconnectcore Qt5::Gui Qt5::X11Extras ${X11_LIBRARIES} ${XTEST_LIBRARIES} ${LibFakeKey_LIBRARIES} KF5::I18n) + +if(HAVE_WAYLAND) + target_link_libraries(kdeconnect_mousepad KF5::WaylandClient) +endif() diff --git a/plugins/mousepad/config-mousepad.h.cmake b/plugins/mousepad/config-mousepad.h.cmake new file mode 100644 index 000000000..e6830040c --- /dev/null +++ b/plugins/mousepad/config-mousepad.h.cmake @@ -0,0 +1 @@ +#cmakedefine01 HAVE_WAYLAND diff --git a/plugins/mousepad/mousepadplugin.cpp b/plugins/mousepad/mousepadplugin.cpp index 38b7c8e6e..89ba532f1 100644 --- a/plugins/mousepad/mousepadplugin.cpp +++ b/plugins/mousepad/mousepadplugin.cpp @@ -1,5 +1,6 @@ /** * Copyright 2014 Ahmed I. Khalil + * Copyright 2015 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 @@ -20,12 +21,20 @@ #include "mousepadplugin.h" #include +#include #include +#include #include #include #include #include +#if HAVE_WAYLAND +#include +#include +#include +#endif + K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_mousepad.json", registerPlugin< MousepadPlugin >(); ) enum MouseButtons { @@ -78,8 +87,14 @@ size_t arraySize(T(&arr)[N]) { (void)arr; return N; } MousepadPlugin::MousepadPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args), m_fakekey(0), m_x11(QX11Info::isPlatformX11()) +#if HAVE_WAYLAND + , m_waylandInput(nullptr) + , m_waylandAuthenticationRequested(false) +#endif { - +#if HAVE_WAYLAND + setupWaylandIntegration(); +#endif } MousepadPlugin::~MousepadPlugin() @@ -95,6 +110,15 @@ bool MousepadPlugin::receivePackage(const NetworkPackage& np) if (m_x11) { return handlePackageX11(np); } +#if HAVE_WAYLAND + if (m_waylandInput) { + if (!m_waylandAuthenticationRequested) { + m_waylandInput->authenticate(i18n("KDE Connect"), i18n("Use your phone as a touchpad and keyboard")); + m_waylandAuthenticationRequested = true; + } + handPackageWayland(np); + } +#endif return false; } @@ -203,4 +227,72 @@ bool MousepadPlugin::handlePackageX11(const NetworkPackage &np) return true; } +#if HAVE_WAYLAND +void MousepadPlugin::setupWaylandIntegration() +{ + if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) { + // not wayland + return; + } + using namespace KWayland::Client; + ConnectionThread *connection = ConnectionThread::fromApplication(this); + if (!connection) { + // failed to get the Connection from Qt + return; + } + Registry *registry = new Registry(this); + registry->create(connection); + connect(registry, &Registry::fakeInputAnnounced, this, + [this, registry] (quint32 name, quint32 version) { + m_waylandInput = registry->createFakeInput(name, version, this); + } + ); + registry->setup(); +} + +bool MousepadPlugin::handPackageWayland(const NetworkPackage &np) +{ + const float dx = np.get("dx", 0); + const float dy = np.get("dy", 0); + + const bool isSingleClick = np.get("singleclick", false); + const bool isDoubleClick = np.get("doubleclick", false); + const bool isMiddleClick = np.get("middleclick", false); + const bool isRightClick = np.get("rightclick", false); + const bool isSingleHold = np.get("singlehold", false); + const bool isSingleRelease = np.get("singlerelease", false); + const bool isScroll = np.get("scroll", false); + const QString key = np.get("key", ""); + const int specialKey = np.get("specialKey", 0); + + if (isSingleClick || isDoubleClick || isMiddleClick || isRightClick || isSingleHold || isScroll || !key.isEmpty() || specialKey) { + + if (isSingleClick) { + m_waylandInput->requestPointerButtonClick(Qt::LeftButton); + } else if (isDoubleClick) { + m_waylandInput->requestPointerButtonClick(Qt::LeftButton); + m_waylandInput->requestPointerButtonClick(Qt::LeftButton); + } else if (isMiddleClick) { + m_waylandInput->requestPointerButtonClick(Qt::MiddleButton); + } else if (isRightClick) { + m_waylandInput->requestPointerButtonClick(Qt::RightButton); + } else if (isSingleHold){ + //For drag'n drop + m_waylandInput->requestPointerButtonPress(Qt::LeftButton); + } else if (isSingleRelease){ + //For drag'n drop. NEVER USED (release is done by tapping, which actually triggers a isSingleClick). Kept here for future-proofnes. + m_waylandInput->requestPointerButtonRelease(Qt::LeftButton); + } else if (isScroll) { + m_waylandInput->requestPointerAxis(Qt::Vertical, dy); + } else if (!key.isEmpty() || specialKey) { + // TODO: implement key support + } + + } else { //Is a mouse move event + m_waylandInput->requestPointerMove(QSizeF(dx, dy)); + } + return true; +} +#endif + #include "mousepadplugin.moc" diff --git a/plugins/mousepad/mousepadplugin.h b/plugins/mousepad/mousepadplugin.h index 4bef0dba9..0c4998844 100644 --- a/plugins/mousepad/mousepadplugin.h +++ b/plugins/mousepad/mousepadplugin.h @@ -23,11 +23,22 @@ #include #include +#include #define PACKAGE_TYPE_MOUSEPAD QLatin1String("kdeconnect.mousepad") struct FakeKey; +#if HAVE_WAYLAND +namespace KWayland +{ +namespace Client +{ +class FakeInput; +} +} +#endif + class MousepadPlugin : public KdeConnectPlugin { @@ -42,10 +53,17 @@ public: private: bool handlePackageX11(const NetworkPackage& np); +#if HAVE_WAYLAND + void setupWaylandIntegration(); + bool handPackageWayland(const NetworkPackage& np); +#endif FakeKey* m_fakekey; const bool m_x11; - +#if HAVE_WAYLAND + KWayland::Client::FakeInput *m_waylandInput; + bool m_waylandAuthenticationRequested; +#endif }; #endif