From 040ad7357b5a630f2fffface6a29d1be5103c663 Mon Sep 17 00:00:00 2001 From: Holger Kaelberer Date: Tue, 10 Jan 2017 21:12:42 +0100 Subject: [PATCH] kdeconnect-kde: Add remotekeyboard plugin Allow to inject keypress events to remote peers (most notably Android devices) Notes / open issues / possible improvements: - For the json-payload I used the syntax of the key-events as sent by mousepad-plugin with the addition of a "sendAck"-flag. If "sendAck" is set to true the remote peer should echo a key-event if it could be handled, thus allowing the local client to find out whether the key was accepted. For performance reasons, it's allowed to send multi-char strings in the "key" property (performs much better if you send a whole string via "echo '...' | kdeconnect-cli ..." e.g.) - kdeconnect-cli: For now takes a string and transforms it into single key-events for visible characters only. In a first implementation I used a kbhit() helper that used termios.h to catch and relay keypresses interactively (including some special-events), which was not optimal. A better approch might be to use linux input-api directly. Would this be an option regarding cross-platform compatibility or can I assume to develop for Linux only? Being a command-line guy, I'd really like to have a fully featured kdeconnect-cli interface ;-) - Factor out the Qt::Key-to-internal keymap to some core-helper because it corresponds to the mapping in the mousepad-plugin? - The plasmoid is not perfect as it is: A single line containing a non-echoing TextField (i.e. it eats all the KeyPress events), and only ack-ed keypress-packets from the peer device are injected if they contain visible keys. Advantage: the user sees whether his key-presses are accepted by the peer device. Disadvantage: The echoed text does not correspond 1:1 to what is shown on the peer's display, user might be confused when typing without success. I played around with different variations each of which with its proper shortcomings: 1. An echoing Textfield for typing: Has the advantage that the user can directly see what he is typing, which makes interaction in the typing field easier, BUT messes up interaction if the Editor on the peer is changed silently and does not notify the user if his keypresses are not handled by the peer. 2. A non-echoing TextField for typing PLUS a readonly one for printing visible echoed keys. Disadvantage: same as for the previous one and uses more space on the plasmoid. Comments? Ideas? REVIEW: 129727 BUG: 370919 --- cli/kdeconnect-cli.cpp | 20 +++ interfaces/CMakeLists.txt | 1 + interfaces/dbusinterfaces.cpp | 7 + interfaces/dbusinterfaces.h | 10 ++ .../kdeconnectdeclarativeplugin.cpp | 9 ++ .../package/contents/ui/DeviceDelegate.qml | 60 ++++++++ .../package/contents/ui/RemoteKeyboard.qml | 71 +++++++++ plugins/CMakeLists.txt | 1 + plugins/mousepad/kdeconnect_mousepad.json | 2 +- plugins/remotekeyboard/CMakeLists.txt | 8 + plugins/remotekeyboard/README | 23 +++ .../kdeconnect_remotekeyboard.json | 28 ++++ .../remotekeyboard/remotekeyboardplugin.cpp | 144 ++++++++++++++++++ plugins/remotekeyboard/remotekeyboardplugin.h | 65 ++++++++ 14 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 plasmoid/package/contents/ui/RemoteKeyboard.qml create mode 100644 plugins/remotekeyboard/CMakeLists.txt create mode 100644 plugins/remotekeyboard/README create mode 100644 plugins/remotekeyboard/kdeconnect_remotekeyboard.json create mode 100644 plugins/remotekeyboard/remotekeyboardplugin.cpp create mode 100644 plugins/remotekeyboard/remotekeyboardplugin.h diff --git a/cli/kdeconnect-cli.cpp b/cli/kdeconnect-cli.cpp index 1acb72314..8281b1239 100644 --- a/cli/kdeconnect-cli.cpp +++ b/cli/kdeconnect-cli.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,7 @@ int main(int argc, char** argv) parser.addOption(QCommandLineOption(QStringLiteral("encryption-info"), i18n("Get encryption info about said device"))); parser.addOption(QCommandLineOption(QStringLiteral("list-commands"), i18n("Lists remote commands and their ids"))); parser.addOption(QCommandLineOption(QStringLiteral("execute-command"), i18n("Executes a remote command by id"), QStringLiteral("id"))); + parser.addOption(QCommandLineOption(QStringList{QStringLiteral("k"), QStringLiteral("send-keys")}, i18n("Sends keys to a said device"))); about.setupCommandLine(&parser); parser.addHelpOption(); @@ -199,6 +201,24 @@ int main(int argc, char** argv) } else if(parser.isSet(QStringLiteral("ring"))) { QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/findmyphone", QStringLiteral("org.kde.kdeconnect.device.findmyphone"), QStringLiteral("ring")); QDBusConnection::sessionBus().call(msg); + } else if(parser.isSet("send-keys")) { + QString seq = parser.value("send-keys"); + QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect/devices/"+device+"/remotekeyboard", "org.kde.kdeconnect.device.remotekeyboard", "sendKeyPress"); + if (seq.trimmed() == QLatin1String("-")) { + // from file + QFile in; + if(in.open(stdin,QIODevice::ReadOnly | QIODevice::Unbuffered)) { + while (!in.atEnd()) { + QByteArray line = in.readLine(); // sanitize to ASCII-codes > 31? + msg.setArguments({QString(line), -1, false, false, false}); + QDBusConnection::sessionBus().call(msg); + } + in.close(); + } + } else { + msg.setArguments({seq, -1, false, false, false}); + QDBusConnection::sessionBus().call(msg); + } } else if(parser.isSet(QStringLiteral("list-notifications"))) { NotificationsModel notifications; notifications.setDeviceId(device); diff --git a/interfaces/CMakeLists.txt b/interfaces/CMakeLists.txt index 19a9cdc22..806ff36e7 100644 --- a/interfaces/CMakeLists.txt +++ b/interfaces/CMakeLists.txt @@ -43,6 +43,7 @@ geninterface(${CMAKE_SOURCE_DIR}/plugins/mprisremote/mprisremoteplugin.h mprisre geninterface(${CMAKE_SOURCE_DIR}/plugins/remotecontrol/remotecontrolplugin.h remotecontrolinterface) geninterface(${CMAKE_SOURCE_DIR}/plugins/lockdevice/lockdeviceplugin.h lockdeviceinterface) geninterface(${CMAKE_SOURCE_DIR}/plugins/remotecommands/remotecommandsplugin.h remotecommandsinterface) +geninterface(${CMAKE_SOURCE_DIR}/plugins/remotekeyboard/remotekeyboardplugin.h remotekeyboardinterface) add_library(kdeconnectinterfaces SHARED ${libkdeconnect_SRC}) diff --git a/interfaces/dbusinterfaces.cpp b/interfaces/dbusinterfaces.cpp index aab3e0530..1f9903674 100644 --- a/interfaces/dbusinterfaces.cpp +++ b/interfaces/dbusinterfaces.cpp @@ -156,4 +156,11 @@ RemoteCommandsDbusInterface::RemoteCommandsDbusInterface(const QString& deviceId RemoteCommandsDbusInterface::~RemoteCommandsDbusInterface() = default; +RemoteKeyboardDbusInterface::RemoteKeyboardDbusInterface(const QString& deviceId, QObject* parent): + OrgKdeKdeconnectDeviceRemotekeyboardInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/remotekeyboard", QDBusConnection::sessionBus(), parent) +{ +} + +RemoteKeyboardDbusInterface::~RemoteKeyboardDbusInterface() = default; + #include "dbusinterfaces.moc" diff --git a/interfaces/dbusinterfaces.h b/interfaces/dbusinterfaces.h index 9848d0cfe..ade1543fa 100644 --- a/interfaces/dbusinterfaces.h +++ b/interfaces/dbusinterfaces.h @@ -34,6 +34,7 @@ #include "interfaces/remotecontrolinterface.h" #include "interfaces/lockdeviceinterface.h" #include "interfaces/remotecommandsinterface.h" +#include "interfaces/remotekeyboardinterface.h" /** * Using these "proxy" classes just in case we need to rename the @@ -180,4 +181,13 @@ public: ~RemoteCommandsDbusInterface() override; }; +class KDECONNECTINTERFACES_EXPORT RemoteKeyboardDbusInterface + : public OrgKdeKdeconnectDeviceRemotekeyboardInterface +{ + Q_OBJECT +public: + explicit RemoteKeyboardDbusInterface(const QString& deviceId, QObject* parent = nullptr); + ~RemoteKeyboardDbusInterface() override; +}; + #endif diff --git a/plasmoid/declarativeplugin/kdeconnectdeclarativeplugin.cpp b/plasmoid/declarativeplugin/kdeconnectdeclarativeplugin.cpp index 282f2a97f..38c5960d5 100644 --- a/plasmoid/declarativeplugin/kdeconnectdeclarativeplugin.cpp +++ b/plasmoid/declarativeplugin/kdeconnectdeclarativeplugin.cpp @@ -49,6 +49,11 @@ QObject* createFindMyPhoneInterface(const QVariant &deviceId) return new FindMyPhoneDeviceDbusInterface(deviceId.toString()); } +QObject* createRemoteKeyboardInterface(const QVariant &deviceId) +{ + return new RemoteKeyboardDbusInterface(deviceId.toString()); +} + QObject* createSftpInterface(const QVariant &deviceId) { return new SftpDbusInterface(deviceId.toString()); @@ -85,6 +90,7 @@ void KdeConnectDeclarativePlugin::registerTypes(const char* uri) qmlRegisterUncreatableType(uri, 1, 0, "MprisDbusInterface", QStringLiteral("You're not supposed to instantiate interfacess")); qmlRegisterUncreatableType(uri, 1, 0, "LockDeviceDbusInterface", QStringLiteral("You're not supposed to instantiate interfacess")); qmlRegisterUncreatableType(uri, 1, 0, "FindMyPhoneDbusInterface", QStringLiteral("You're not supposed to instantiate interfacess")); + qmlRegisterUncreatableType(uri, 1, 0, "RemoteKeyboardDbusInterface", QStringLiteral("You're not supposed to instantiate interfacess")); qmlRegisterUncreatableType(uri, 1, 0, "DeviceDbusInterface", QStringLiteral("You're not supposed to instantiate interfacess")); qmlRegisterSingletonType(uri, 1, 0, "DaemonDbusInterface", [](QQmlEngine*, QJSEngine*) -> QObject* { @@ -109,6 +115,9 @@ void KdeConnectDeclarativePlugin::initializeEngine(QQmlEngine* engine, const cha engine->rootContext()->setContextProperty(QStringLiteral("SftpDbusInterfaceFactory") , new ObjectFactory(engine, createSftpInterface)); + engine->rootContext()->setContextProperty(QStringLiteral("RemoteKeyboardDbusInterfaceFactory") + , new ObjectFactory(engine, createRemoteKeyboardInterface)); + engine->rootContext()->setContextProperty(QStringLiteral("MprisDbusInterfaceFactory") , new ObjectFactory(engine, createMprisInterface)); diff --git a/plasmoid/package/contents/ui/DeviceDelegate.qml b/plasmoid/package/contents/ui/DeviceDelegate.qml index e90e021a6..3618ef55c 100644 --- a/plasmoid/package/contents/ui/DeviceDelegate.qml +++ b/plasmoid/package/contents/ui/DeviceDelegate.qml @@ -23,12 +23,31 @@ import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kdeconnect 1.0 +import QtQuick.Controls.Styles 1.4 PlasmaComponents.ListItem { id: root readonly property QtObject device: DeviceDbusInterfaceFactory.create(model.deviceId) + RemoteKeyboard { + id: remoteKeyboard + device: root.device + onKeyPressReceived: { + if (specialKey == 12) // Return -> clear + remoteKeyboardInput.text = ""; + else { + var sanitized = ""; + for (var i = 0; i < key.length; i++) { + if (key.charCodeAt(i) > 31) + sanitized += key.charAt(i); + } + if (sanitized.length > 0 && !ctrl && !alt) + remoteKeyboardInput.text += sanitized; + } + } + } + Column { width: parent.width @@ -86,6 +105,47 @@ PlasmaComponents.ListItem width: parent.width } + //RemoteKeyboard + PlasmaComponents.ListItem { + sectionDelegate: true + visible: remoteKeyboard.available + width: parent.width + + Row { + width: parent.width + spacing: 5 + + PlasmaComponents.Label { + id: remoteKeyboardLabel + //font.bold: true + text: i18n("Remote Keyboard") + } + + PlasmaComponents.TextField { + id: remoteKeyboardInput + textColor: "black" + height: parent.height + width: parent.width - 5 - remoteKeyboardLabel.width + verticalAlignment: TextInput.AlignVCenter + style: TextFieldStyle { + textColor: "black" + background: Rectangle { + radius: 2 + border.color: "gray" + border.width: 1 + color: "white" + } + } + + Keys.onPressed: { + if (remoteKeyboard.available) + remoteKeyboard.sendEvent(event); + event.accepted = true; + } + } + } + } + //Battery PlasmaComponents.ListItem { diff --git a/plasmoid/package/contents/ui/RemoteKeyboard.qml b/plasmoid/package/contents/ui/RemoteKeyboard.qml new file mode 100644 index 000000000..80c8b5ab5 --- /dev/null +++ b/plasmoid/package/contents/ui/RemoteKeyboard.qml @@ -0,0 +1,71 @@ +/** + * Copyright 2016 Holger Kaelberer + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + */ + +import QtQuick 2.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kdeconnect 1.0 + +QtObject { + + id: root + + property alias device: checker.device + readonly property alias available: checker.available + + readonly property PluginChecker pluginChecker: PluginChecker { + id: checker + pluginName: "remotekeyboard" + } + + property variant remoteKeyboard: null + + signal keyPressReceived(string key, int specialKey, bool shift, bool ctrl, bool alt) + + function sendEvent(event) { + if (remoteKeyboard) { + var transEvent = JSON.parse(JSON.stringify(event)); // transform to anonymous object + if (transEvent.modifiers & Qt.ControlModifier) { + // special handling for ctrl+c/v/x/a, for which only 'key' gets + // set, but no visbile 'text', which is expected by the remoteKeyboard + // wire-format: + if (transEvent.key === Qt.Key_C) + transEvent.text = 'c'; + if (transEvent.key === Qt.Key_V) + transEvent.text = 'v'; + if (transEvent.key === Qt.Key_A) + transEvent.text = 'a'; + if (transEvent.key === Qt.Key_X) + transEvent.text = 'x'; + } + remoteKeyboard.sendQKeyEvent(transEvent); + } + } + + onAvailableChanged: { + if (available) { + remoteKeyboard = RemoteKeyboardDbusInterfaceFactory.create(device.id()); + remoteKeyboard.keyPressReceived.connect(keyPressReceived); + + } else { + remoteKeyboard = null + } + } +} diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9d230ac32..f0d5bb5c9 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(notifications) add_subdirectory(battery) add_subdirectory(remotecommands) add_subdirectory(findmyphone) +add_subdirectory(remotekeyboard) if(NOT WIN32) add_subdirectory(runcommand) add_subdirectory(sendnotifications) diff --git a/plugins/mousepad/kdeconnect_mousepad.json b/plugins/mousepad/kdeconnect_mousepad.json index 850e855af..962cf61b2 100644 --- a/plugins/mousepad/kdeconnect_mousepad.json +++ b/plugins/mousepad/kdeconnect_mousepad.json @@ -78,4 +78,4 @@ "X-KdeConnect-SupportedPackageType": [ "kdeconnect.mousepad.request" ] -} +} \ No newline at end of file diff --git a/plugins/remotekeyboard/CMakeLists.txt b/plugins/remotekeyboard/CMakeLists.txt new file mode 100644 index 000000000..95ad9833b --- /dev/null +++ b/plugins/remotekeyboard/CMakeLists.txt @@ -0,0 +1,8 @@ +kdeconnect_add_plugin(kdeconnect_remotekeyboard JSON kdeconnect_remotekeyboard.json + SOURCES remotekeyboardplugin.cpp) + +target_link_libraries(kdeconnect_remotekeyboard + kdeconnectcore + KF5::I18n + Qt5::DBus +) diff --git a/plugins/remotekeyboard/README b/plugins/remotekeyboard/README new file mode 100644 index 000000000..b3016acac --- /dev/null +++ b/plugins/remotekeyboard/README @@ -0,0 +1,23 @@ +Sends key-events to remote devices. The payload structure corresponds basically +to that of remote key-presses in the mousepad-plugin (with the exception of the +"sendAck"-flag) , e.g.: + +{ + "key": "a", + "specialKey": 12, + "shift": false, + "ctrl": false, + "alt": false, + "sendAck": true +} + +If "specialKey" is a valid keycode according to the internal map (1 <= x <= 32), +the event is interpreted as a special event and the contents of "key" are not +considered. + +"key" may contain multi-char strings for performance reasons. In that case, +the peer is expected to print the whole string. + +If "sendAck" is set to true, the device expects the remote peer to echo the +event in case it could be handled. This can be used to determine whether the +remote device is ready to accept remote keypresses. diff --git a/plugins/remotekeyboard/kdeconnect_remotekeyboard.json b/plugins/remotekeyboard/kdeconnect_remotekeyboard.json new file mode 100644 index 000000000..a0fbc75e9 --- /dev/null +++ b/plugins/remotekeyboard/kdeconnect_remotekeyboard.json @@ -0,0 +1,28 @@ +{ + "Encoding": "UTF-8", + "KPlugin": { + "Authors": [ + { + "Email": "holger.k@elberer.de", + "Name": "Holger Kaelberer" + } + ], + "Description": "Use your keyboard to send key-events to your paired device", + "Description[x-test]": "xxUse your keyboard to send key-events to your paired devicexx", + "EnabledByDefault": true, + "Icon": "edit-select", + "Id": "kdeconnect_remotekeyboard", + "License": "GPL", + "Name": "Remote keyboard from the desktop", + "ServiceTypes": [ + "KdeConnect/Plugin" + ], + "Version": "0.1" + }, + "X-KdeConnect-OutgoingPackageType": [ + "kdeconnect.remotekeyboard.request" + ], + "X-KdeConnect-SupportedPackageType": [ + "kdeconnect.remotekeyboard" + ] +} diff --git a/plugins/remotekeyboard/remotekeyboardplugin.cpp b/plugins/remotekeyboard/remotekeyboardplugin.cpp new file mode 100644 index 000000000..66565af4b --- /dev/null +++ b/plugins/remotekeyboard/remotekeyboardplugin.cpp @@ -0,0 +1,144 @@ +/** + * Copyright 2016 Holger Kaelberer + * + * 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) 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 14 of version 3 of the license. + * + * 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 "remotekeyboardplugin.h" +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_remotekeyboard.json", registerPlugin< RemoteKeyboardPlugin >(); ) + +Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_REMOTEKEYBOARD, "kdeconnect.plugin.remotekeyboard"); + +// Mapping of Qt::Key to internal codes, corresponds to the mapping in mousepadplugin +QMap specialKeysMap = { + //0, // Invalid + {Qt::Key_Backspace, 1}, + {Qt::Key_Tab, 2}, + //XK_Linefeed, // 3 + {Qt::Key_Left, 4}, + {Qt::Key_Up, 5}, + {Qt::Key_Right, 6}, + {Qt::Key_Down, 7}, + {Qt::Key_PageUp, 8}, + {Qt::Key_PageDown, 9}, + {Qt::Key_Home, 10}, + {Qt::Key_End, 11}, + {Qt::Key_Return, 12}, + {Qt::Key_Enter, 12}, + {Qt::Key_Delete, 13}, + {Qt::Key_Escape, 14}, + {Qt::Key_SysReq, 15}, + {Qt::Key_ScrollLock, 16}, + //0, // 17 + //0, // 18 + //0, // 19 + //0, // 20 + {Qt::Key_F1, 21}, + {Qt::Key_F2, 22}, + {Qt::Key_F3, 23}, + {Qt::Key_F4, 24}, + {Qt::Key_F5, 25}, + {Qt::Key_F6, 26}, + {Qt::Key_F7, 27}, + {Qt::Key_F8, 28}, + {Qt::Key_F9, 29}, + {Qt::Key_F10, 30}, + {Qt::Key_F11, 31}, + {Qt::Key_F12, 32}, +}; + +RemoteKeyboardPlugin::RemoteKeyboardPlugin(QObject* parent, const QVariantList& args) + : KdeConnectPlugin(parent, args) +{ +} + +RemoteKeyboardPlugin::~RemoteKeyboardPlugin() +{ +} + +bool RemoteKeyboardPlugin::receivePackage(const NetworkPackage& np) +{ + if (np.type() == PACKAGE_TYPE_REMOTEKEYBOARD) { + if (!np.has("isAck") || !np.has("key")) { + qCWarning(KDECONNECT_PLUGIN_REMOTEKEYBOARD) << "Invalid packet of type" + << PACKAGE_TYPE_REMOTEKEYBOARD; + return false; + } +// qCWarning(KDECONNECT_PLUGIN_REMOTEKEYBOARD) << "Received keypress" << np; + Q_EMIT keyPressReceived(np.get("key"), + np.get("specialKey", 0), + np.get("shift", false), + np.get("ctrl", false), + np.get("alt", false)); + return true; + } + return true; +} + +void RemoteKeyboardPlugin::sendKeyPress(const QString& key, int specialKey, + bool shift, bool ctrl, + bool alt, bool sendAck) const +{ + NetworkPackage np(PACKAGE_TYPE_REMOTEKEYBOARD_REQUEST, { + {"key", key}, + {"specialKey", specialKey}, + {"shift", shift}, + {"ctrl", ctrl}, + {"alt", alt}, + {"sendAck", sendAck} + }); + sendPackage(np); +} + +void RemoteKeyboardPlugin::sendQKeyEvent(const QVariantMap& keyEvent, bool sendAck) const +{ + if (!keyEvent.contains("key")) + return; + int k = translateQtKey(keyEvent.value("key").toInt()); + int modifiers = keyEvent.value("modifiers").toInt(); + sendKeyPress(keyEvent.value("text").toString(), k, + modifiers & Qt::ShiftModifier, + modifiers & Qt::ControlModifier, + modifiers & Qt::AltModifier, + sendAck); +} + +int RemoteKeyboardPlugin::translateQtKey(int qtKey) const +{ + return specialKeysMap.value(qtKey, 0); +} + +void RemoteKeyboardPlugin::connected() +{ + QDBusConnection::sessionBus().registerObject(dbusPath(), this, + QDBusConnection::ExportScriptableInvokables + | QDBusConnection::ExportScriptableSignals); +} + +QString RemoteKeyboardPlugin::dbusPath() const +{ + return "/modules/kdeconnect/devices/" + device()->id() + "/remotekeyboard"; +} + + +#include "remotekeyboardplugin.moc" diff --git a/plugins/remotekeyboard/remotekeyboardplugin.h b/plugins/remotekeyboard/remotekeyboardplugin.h new file mode 100644 index 000000000..f0da7b1f3 --- /dev/null +++ b/plugins/remotekeyboard/remotekeyboardplugin.h @@ -0,0 +1,65 @@ +/** + * Copyright 2016 Holger Kaelberer + * + * 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) 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 14 of version 3 of the license. + * + * 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 REMOTEKEYBOARDPLUGIN_H +#define REMOTEKEYBOARDPLUGIN_H + +#include +#include +#include +#include + +struct FakeKey; + +Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_REMOTEKEYBOARD); + +#define PACKAGE_TYPE_REMOTEKEYBOARD_REQUEST QLatin1String("kdeconnect.remotekeyboard.request") +#define PACKAGE_TYPE_REMOTEKEYBOARD QLatin1String("kdeconnect.remotekeyboard") + +class RemoteKeyboardPlugin + : public KdeConnectPlugin +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.remotekeyboard") + +public: + explicit RemoteKeyboardPlugin(QObject *parent, const QVariantList &args); + ~RemoteKeyboardPlugin() override; + + bool receivePackage(const NetworkPackage& np) override; + void connected() override; + + Q_SCRIPTABLE void sendKeyPress(const QString& key, int specialKey = 0, + bool shift = false, bool ctrl = false, + bool alt = false, bool sendAck = true) const; + Q_SCRIPTABLE void sendQKeyEvent(const QVariantMap& keyEvent, + bool sendAck = true) const; + Q_SCRIPTABLE int translateQtKey(int qtKey) const; + +Q_SIGNALS: + Q_SCRIPTABLE void keyPressReceived(const QString& key, int specialKey = 0, + bool shift = false, bool ctrl = false, + bool alt = false) const; + +private: + QString dbusPath() const; +}; + +#endif