diff --git a/plugins/clipboard/clipboardlistener.cpp b/plugins/clipboard/clipboardlistener.cpp index abd43876..222e279f 100644 --- a/plugins/clipboard/clipboardlistener.cpp +++ b/plugins/clipboard/clipboardlistener.cpp @@ -1,53 +1,65 @@ /** * Copyright 2016 Albert Vaca * * 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 "clipboardlistener.h" -ClipboardListener::ClipboardListener() +ClipboardListener::ClipboardListener() : clipboard(QGuiApplication::clipboard()) { #ifdef Q_OS_MAC connect(&m_clipboardMonitorTimer, &QTimer::timeout, this, [this](){ updateClipboard(QClipboard::Clipboard); }); m_clipboardMonitorTimer.start(1000); // Refresh 1s #endif connect(clipboard, &QClipboard::changed, this, &ClipboardListener::updateClipboard); } -void ClipboardListener::updateClipboard(QClipboard::Mode mode) +void ClipboardListener::updateClipboard(QClipboard::Mode mode) { if (mode != QClipboard::Clipboard) { return; } QString content = clipboard->text(); - if (content == currentContent) { + if (content == m_currentContent) { return; } - currentContent = content; + m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_currentContent = content; Q_EMIT clipboardChanged(content); } +QString ClipboardListener::currentContent() +{ + return m_currentContent; +} + +qint64 ClipboardListener::updateTimestamp(){ + + return m_updateTimestamp; +} + void ClipboardListener::setText(const QString& content) { - currentContent = content; + m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_currentContent = content; clipboard->setText(content); } diff --git a/plugins/clipboard/clipboardlistener.h b/plugins/clipboard/clipboardlistener.h index 20973b12..43db8147 100644 --- a/plugins/clipboard/clipboardlistener.h +++ b/plugins/clipboard/clipboardlistener.h @@ -1,63 +1,68 @@ /** * Copyright 2016 Albert Vaca * * 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 CLIPBOARDLISTENER_H #define CLIPBOARDLISTENER_H +#include #include #include #include #include /** * Wrapper around QClipboard, which emits clipboardChanged only when it really changed */ -class ClipboardListener : public QObject +class ClipboardListener : public QObject { Q_OBJECT private: ClipboardListener(); - QString currentContent; + QString m_currentContent; + qint64 m_updateTimestamp = 0; QClipboard* clipboard; #ifdef Q_OS_MAC QTimer m_clipboardMonitorTimer; #endif public: - static ClipboardListener* instance() + static ClipboardListener* instance() { static ClipboardListener* me = nullptr; if (!me) { me = new ClipboardListener(); } return me; } void updateClipboard(QClipboard::Mode mode); void setText(const QString& content); + QString currentContent(); + qint64 updateTimestamp(); + Q_SIGNALS: void clipboardChanged(const QString& content); }; #endif diff --git a/plugins/clipboard/clipboardplugin.cpp b/plugins/clipboard/clipboardplugin.cpp index d9926968..bab72f5c 100644 --- a/plugins/clipboard/clipboardplugin.cpp +++ b/plugins/clipboard/clipboardplugin.cpp @@ -1,51 +1,78 @@ /** * Copyright 2013 Albert Vaca * * 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 "clipboardplugin.h" #include "clipboardlistener.h" #include K_PLUGIN_CLASS_WITH_JSON(ClipboardPlugin, "kdeconnect_clipboard.json") Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD, "kdeconnect.plugin.clipboard") ClipboardPlugin::ClipboardPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { connect(ClipboardListener::instance(), &ClipboardListener::clipboardChanged, this, &ClipboardPlugin::propagateClipboard); } +void ClipboardPlugin::connected() +{ + sendConnectPacket(); +} + void ClipboardPlugin::propagateClipboard(const QString& content) { NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{QStringLiteral("content"), content}}); sendPacket(np); } +void ClipboardPlugin::sendConnectPacket() +{ + NetworkPacket np(PACKET_TYPE_CLIPBOARD_CONNECT, { + {QStringLiteral("content"), ClipboardListener::instance()->currentContent()}, + {QStringLiteral("timestamp"), ClipboardListener::instance()->updateTimestamp()} + }); + sendPacket(np); +} + + bool ClipboardPlugin::receivePacket(const NetworkPacket& np) { QString content = np.get(QStringLiteral("content")); - ClipboardListener::instance()->setText(content); - return true; + if (np.type() == PACKET_TYPE_CLIPBOARD) { + ClipboardListener::instance()->setText(content); + return true; + } else if (np.type() == PACKET_TYPE_CLIPBOARD_CONNECT) { + qint64 packetTime = np.get(QStringLiteral("timestamp")); + // If the packetTime is 0, it means the timestamp is unknown (so do nothing). + if (packetTime == 0 || packetTime < ClipboardListener::instance()->updateTimestamp()) { + return false; + } + + ClipboardListener::instance()->setText(content); + return true; + } + return false; } #include "clipboardplugin.moc" diff --git a/plugins/clipboard/clipboardplugin.h b/plugins/clipboard/clipboardplugin.h index 1e180573..8d104b62 100644 --- a/plugins/clipboard/clipboardplugin.h +++ b/plugins/clipboard/clipboardplugin.h @@ -1,48 +1,72 @@ /** * Copyright 2013 Albert Vaca * * 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 CLIPBOARDPLUGIN_H #define CLIPBOARDPLUGIN_H #include #include #include #include Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD) + +/** + * Packet containing just clipboard contents, sent when a device updates its clipboard. + *

+ * The body should look like so: + * { + * "content": "password" + * } + */ #define PACKET_TYPE_CLIPBOARD QStringLiteral("kdeconnect.clipboard") +/** + * Packet containing clipboard contents and a timestamp that the contents were last updated, sent + * on first connection + *

+ * The timestamp is milliseconds since epoch. It can be 0, which indicates that the clipboard + * update time is currently unknown. + *

+ * The body should look like so: + * { + * "timestamp": 542904563213, + * "content": "password" + * } + */ +#define PACKET_TYPE_CLIPBOARD_CONNECT QStringLiteral("kdeconnect.clipboard.connect") + class ClipboardPlugin : public KdeConnectPlugin { Q_OBJECT public: explicit ClipboardPlugin(QObject* parent, const QVariantList& args); bool receivePacket(const NetworkPacket& np) override; - void connected() override { } - + void connected() override; private Q_SLOTS: void propagateClipboard(const QString& content); + void sendConnectPacket(); }; #endif diff --git a/plugins/clipboard/kdeconnect_clipboard.json b/plugins/clipboard/kdeconnect_clipboard.json index fba6cc00..040a439d 100644 --- a/plugins/clipboard/kdeconnect_clipboard.json +++ b/plugins/clipboard/kdeconnect_clipboard.json @@ -1,137 +1,137 @@ { "KPlugin": { "Authors": [ { "Email": "albertvaka@gmail.com", "Name": "Albert Vaca", "Name[ar]": "Albert Vaca", "Name[ca@valencia]": "Albert Vaca", "Name[ca]": "Albert Vaca", "Name[cs]": "Albert Vaca", "Name[da]": "Albert Vaca", "Name[de]": "Albert Vaca", "Name[el]": "Albert Vaca", "Name[en_GB]": "Albert Vaca", "Name[es]": "Albert Vaca", "Name[et]": "Albert Vaca", "Name[eu]": "Albert Vaca", "Name[fi]": "Albert Vaca", "Name[fr]": "Albert Vaca", "Name[gl]": "Albert Vaca", "Name[id]": "Albert Vaca", "Name[it]": "Albert Vaca", "Name[ko]": "Albert Vaca", "Name[lt]": "Albert Vaca", "Name[nl]": "Albert Vaca", "Name[nn]": "Albert Vaca", "Name[pl]": "Albert Vaca", "Name[pt]": "Albert Vaca", "Name[pt_BR]": "Albert Vaca", "Name[ru]": "Albert Vaca", "Name[sk]": "Albert Vaca", "Name[sr@ijekavian]": "Алберт Вака Синтора", "Name[sr@ijekavianlatin]": "Albert Vaka Sintora", "Name[sr@latin]": "Albert Vaka Sintora", "Name[sr]": "Алберт Вака Синтора", "Name[sv]": "Albert Vaca", "Name[tr]": "Albert Vaca", "Name[uk]": "Albert Vaca", "Name[x-test]": "xxAlbert Vacaxx", "Name[zh_CN]": "Albert Vaca", "Name[zh_TW]": "Albert Vaca" } ], "Description": "Share the clipboard between devices", "Description[ar]": "شارك الحافظة بين الجهازين", "Description[ca@valencia]": "Comparteix el porta-retalls entre dispositius", "Description[ca]": "Comparteix el porta-retalls entre dispositius", "Description[cs]": "Sdílet obsah schránky mezi zařízeními", "Description[da]": "Del udklipsholderen mellem enheder", "Description[de]": "Die Zwischenablage mit Geräten teilen", "Description[el]": "Διαμοιρασμός του προχείρου μεταξύ συσκευών", "Description[en_GB]": "Share the clipboard between devices", "Description[es]": "Compartir portapapeles entre dispositivos", "Description[et]": "Lõikepuhvri jagamine seadmete vahel", "Description[eu]": "Partekatu arbela gailuen artean", "Description[fi]": "Jaa leikepöytä laitteiden välillä", "Description[fr]": "Partagez le presse-papiers entre périphériques", "Description[gl]": "Comparta o portapapeis entre os dispositivos", "Description[hu]": "A vágólap megosztása az eszközök között", "Description[id]": "Berbagi papan-klip antara perangkat", "Description[it]": "Condividi gli appunti tra i dispositivi", "Description[ja]": "デバイス間でクリップボードを共有", "Description[ko]": "장치간 클립보드 공유", "Description[lt]": "Bendrinti iškarpinę tarp įrenginių", "Description[nl]": "Het klembord tussen apparaten delen", "Description[nn]": "Del utklippstavla mellom einingar", "Description[pl]": "Współdziel schowek pomiędzy urządzeniami", "Description[pt]": "Partilhar a área de transferência entre dispositivos", "Description[pt_BR]": "Compartilha a área de transferência entre dispositivos", "Description[ru]": "Общий буфер обмена между устройствами", "Description[sk]": "Zdieľať schránku medzi zariadeniami", "Description[sr@ijekavian]": "Дељење клипборда између уређаја", "Description[sr@ijekavianlatin]": "Deljenje klipborda između uređaja", "Description[sr@latin]": "Deljenje klipborda između uređaja", "Description[sr]": "Дељење клипборда између уређаја", "Description[sv]": "Dela klippbordet mellan apparater", "Description[tr]": "Aygıtlar arasında panoyu paylaştır", "Description[uk]": "Спільне використання буфера обміну даними на пристроях", "Description[x-test]": "xxShare the clipboard between devicesxx", "Description[zh_CN]": "在设备间共享剪贴板", "Description[zh_TW]": "在設備之間共享剪貼板", "EnabledByDefault": true, "Icon": "klipper", "Id": "kdeconnect_clipboard", "License": "GPL", "Name": "Clipboard", "Name[ar]": "الحافظة", "Name[ca@valencia]": "Porta-retalls", "Name[ca]": "Porta-retalls", "Name[cs]": "Schránka", "Name[da]": "Udklipsholder", "Name[de]": "Zwischenablage", "Name[el]": "Πρόχειρο", "Name[en_GB]": "Clipboard", "Name[es]": "Portapapeles", "Name[et]": "Lõikepuhver", "Name[eu]": "Arbela", "Name[fi]": "Leikepöytä", "Name[fr]": "Presse-papiers", "Name[gl]": "Portapapeis", "Name[hu]": "Vágólap", "Name[ia]": "Area de transferentia", "Name[id]": "Papan-klip", "Name[it]": "Appunti", "Name[ja]": "クリップボード", "Name[ko]": "클립보드", "Name[lt]": "Iškarpinė", "Name[nl]": "Klembord", "Name[nn]": "Utklippstavle", "Name[pl]": "Schowek", "Name[pt]": "Área de Transferência", "Name[pt_BR]": "Área de transferência", "Name[ru]": "Буфер обмена", "Name[sk]": "Schránka", "Name[sr@ijekavian]": "Клипборд", "Name[sr@ijekavianlatin]": "Klipbord", "Name[sr@latin]": "Klipbord", "Name[sr]": "Клипборд", "Name[sv]": "Klippbord", "Name[tr]": "Geçici taşıma panosu", "Name[uk]": "Буфер обміну", "Name[x-test]": "xxClipboardxx", "Name[zh_CN]": "剪切板", "Name[zh_TW]": "剪貼簿", "ServiceTypes": [ "KdeConnect/Plugin" ], "Version": "0.1", "Website": "https://albertvaka.wordpress.com" }, "X-KdeConnect-OutgoingPacketType": [ - "kdeconnect.clipboard" + "kdeconnect.clipboard", "kdeconnect.clipboard.connect" ], "X-KdeConnect-SupportedPacketType": [ - "kdeconnect.clipboard" + "kdeconnect.clipboard", "kdeconnect.clipboard.connect" ] }