From 3f2ec76bd4332b41e29578477d428e35f6554df7 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 9 Nov 2025 12:51:32 -0300 Subject: [PATCH 01/19] new: Klipper dbus interface --- CMakeLists.txt | 2 ++ dbus/org.kde.Klipper.xml | 13 +++++++++ src/dbus_klipper_inf.cpp | 26 +++++++++++++++++ src/dbus_klipper_inf.h | 62 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 dbus/org.kde.Klipper.xml create mode 100644 src/dbus_klipper_inf.cpp create mode 100644 src/dbus_klipper_inf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 49f656b5..696038aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,6 +288,8 @@ set(dsnote_lib_sources ${sources_dir}/dbus_application_inf.h ${sources_dir}/dbus_notifications_inf.cpp ${sources_dir}/dbus_notifications_inf.h + ${sources_dir}/dbus_klipper_inf.h + ${sources_dir}/dbus_klipper_inf.cpp ${sources_dir}/dirmodel.cpp ${sources_dir}/dirmodel.h ${sources_dir}/dsnote_app.cpp diff --git a/dbus/org.kde.Klipper.xml b/dbus/org.kde.Klipper.xml new file mode 100644 index 00000000..90dc0bc6 --- /dev/null +++ b/dbus/org.kde.Klipper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/dbus_klipper_inf.cpp b/src/dbus_klipper_inf.cpp new file mode 100644 index 00000000..cb01f145 --- /dev/null +++ b/src/dbus_klipper_inf.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp ../dbus/org.kde.Klipper.xml -p dbus_klipper_inf + * + * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "dbus_klipper_inf.h" + +/* + * Implementation of interface class OrgKdeKlipperKlipperInterface + */ + +OrgKdeKlipperKlipperInterface::OrgKdeKlipperKlipperInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +OrgKdeKlipperKlipperInterface::~OrgKdeKlipperKlipperInterface() +{ +} + diff --git a/src/dbus_klipper_inf.h b/src/dbus_klipper_inf.h new file mode 100644 index 00000000..f99e0898 --- /dev/null +++ b/src/dbus_klipper_inf.h @@ -0,0 +1,62 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp ../dbus/org.kde.Klipper.xml -p dbus_klipper_inf + * + * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef DBUS_KLIPPER_INF_H_1762702122 +#define DBUS_KLIPPER_INF_H_1762702122 + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Proxy class for interface org.kde.klipper.klipper + */ +class OrgKdeKlipperKlipperInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.kde.klipper.klipper"; } + +public: + OrgKdeKlipperKlipperInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~OrgKdeKlipperKlipperInterface(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply getClipboardContents() + { + QList argumentList; + return asyncCallWithArgumentList(QLatin1String("getClipboardContents"), argumentList); + } + + inline QDBusPendingReply<> setClipboardContents(const QString &s) + { + QList argumentList; + argumentList << QVariant::fromValue(s); + return asyncCallWithArgumentList(QLatin1String("setClipboardContents"), argumentList); + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace kde { + namespace klipper { + typedef ::OrgKdeKlipperKlipperInterface klipper; + } + } +} +#endif From aaf6acb3d89ac515b69e9bb11c36071db2f6fc10 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 9 Nov 2025 13:34:41 -0300 Subject: [PATCH 02/19] new: copying text with klipper on wayland environments --- src/dsnote_app.cpp | 20 +++----------------- src/fake_keyboard.cpp | 27 +++++++++++++++++++++++++++ src/fake_keyboard.hpp | 3 +++ 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/dsnote_app.cpp b/src/dsnote_app.cpp index 75746a3c..2ff1b031 100644 --- a/src/dsnote_app.cpp +++ b/src/dsnote_app.cpp @@ -8,7 +8,6 @@ #include "dsnote_app.h" #include -#include #include #include #include @@ -1028,27 +1027,14 @@ void dsnote_app::handle_stt_text_decoded(QString text, const QString &lang, settings::instance()->text_to_window_method() == settings::text_to_window_method_t::TextToWindowMethodCtrlV; if (paste_mode) { - // preserve current clipboard text, copy recognized text, - // paste via Ctrl+V, then restore previous clipboard content - // after a short delay - auto *clip = QGuiApplication::clipboard(); - const QString prev_clip_text = clip->text(); - // set recognized text to clipboard - clip->setText(text); + // Copy text to clipboard and paste via Ctrl+V. Then restore it + auto prev_clip_text = m_fake_keyboard->copy_to_clipboard(text); m_fake_keyboard.emplace(); m_fake_keyboard->send_ctrl_v(); + m_fake_keyboard->restore_clipboard(prev_clip_text); emit text_decoded_to_active_window(); - - // restore previous clipboard text after a short delay - int restore_delay_ms = std::max( - 200, settings::instance()->fake_keyboard_delay() * 2); - QTimer::singleShot( - restore_delay_ms, this, [prev_clip_text]() { - QGuiApplication::clipboard()->setText( - prev_clip_text); - }); } else { m_fake_keyboard.emplace(); m_fake_keyboard->send_text(text); diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 1f2f921b..d36cb703 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -8,6 +8,7 @@ #include "fake_keyboard.hpp" // clang-format off +#include "dbus_speech_adaptor.h" #include "settings.h" // clang-format on @@ -27,6 +28,8 @@ #include #include +#include +#include #include #include #include @@ -36,6 +39,7 @@ #include #include "logger.hpp" +#include "dbus_klipper_inf.h" #include "qtlogger.hpp" using namespace std::chrono_literals; @@ -325,6 +329,29 @@ void fake_keyboard::ydo_type_char(uint32_t c) { } } +// Set the clipboard text +QString fake_keyboard::copy_to_clipboard(const QString& text) { + QString prev_clip_text; + auto platform = QGuiApplication::platformName(); + + if (QString::compare(platform, "wayland", Qt::CaseInsensitive) == 0) { + OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); + prev_clip_text = klipper.getClipboardContents(); + klipper.setClipboardContents(text); + } else { + auto *clip = QGuiApplication::clipboard(); + prev_clip_text = clip->text(); + clip->setText(text); + } + + return prev_clip_text; +} + +void fake_keyboard::restore_clipboard(const QString& prev_text) { + this->copy_to_clipboard(prev_text); +} + + // Send Ctrl+V (paste) to the active window using the configured method void fake_keyboard::send_ctrl_v() { // Delay to allow clipboard to update diff --git a/src/fake_keyboard.hpp b/src/fake_keyboard.hpp index a336903c..e0b4bee0 100644 --- a/src/fake_keyboard.hpp +++ b/src/fake_keyboard.hpp @@ -19,6 +19,7 @@ #include #include #include +#include "dbus_speech_adaptor.h" #ifdef USE_X11_FEATURES #include @@ -50,6 +51,8 @@ class fake_keyboard : public QObject { void send_text(const QString& text); // Send Ctrl+V (paste) to the active window void send_ctrl_v(); + QString copy_to_clipboard(const QString& text); + void restore_clipboard(const QString& prev_text); signals: void text_sending_completed(); From dfa6dc503c614ee41df01bd80cbcdf45d8843e9a Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Wed, 12 Nov 2025 18:21:29 -0300 Subject: [PATCH 03/19] fix: address comments on #341 --- src/dsnote_app.cpp | 14 +++++++++----- src/fake_keyboard.cpp | 31 ++++++++++++++++--------------- src/fake_keyboard.hpp | 2 -- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/dsnote_app.cpp b/src/dsnote_app.cpp index 2ff1b031..2a1dc645 100644 --- a/src/dsnote_app.cpp +++ b/src/dsnote_app.cpp @@ -8,6 +8,7 @@ #include "dsnote_app.h" #include +#include #include #include #include @@ -1021,22 +1022,25 @@ void dsnote_app::handle_stt_text_decoded(QString text, const QString &lang, case text_destination_t::active_window: #ifdef USE_DESKTOP try { + // Make sure keyboard exists! + m_fake_keyboard.emplace(); + // If this request was started as paste-to-active-window, copy // to clipboard and paste via Ctrl+V bool paste_mode = settings::instance()->text_to_window_method() == settings::text_to_window_method_t::TextToWindowMethodCtrlV; if (paste_mode) { - // Copy text to clipboard and paste via Ctrl+V. Then restore it - auto prev_clip_text = m_fake_keyboard->copy_to_clipboard(text); + // Copy text to clipboard and paste via Ctrl+V. Then restore + // it + auto prev_clip_text = + m_fake_keyboard->copy_to_clipboard(text); - m_fake_keyboard.emplace(); m_fake_keyboard->send_ctrl_v(); - m_fake_keyboard->restore_clipboard(prev_clip_text); + m_fake_keyboard->copy_to_clipboard(prev_clip_text); emit text_decoded_to_active_window(); } else { - m_fake_keyboard.emplace(); m_fake_keyboard->send_text(text); emit text_decoded_to_active_window(); } diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index d36cb703..e26a3789 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -8,7 +8,6 @@ #include "fake_keyboard.hpp" // clang-format off -#include "dbus_speech_adaptor.h" #include "settings.h" // clang-format on @@ -26,10 +25,10 @@ #include #include -#include -#include #include #include +#include +#include #include #include #include @@ -38,8 +37,8 @@ #include #include -#include "logger.hpp" #include "dbus_klipper_inf.h" +#include "logger.hpp" #include "qtlogger.hpp" using namespace std::chrono_literals; @@ -330,15 +329,22 @@ void fake_keyboard::ydo_type_char(uint32_t c) { } // Set the clipboard text -QString fake_keyboard::copy_to_clipboard(const QString& text) { +QString fake_keyboard::copy_to_clipboard(const QString &text) { QString prev_clip_text; - auto platform = QGuiApplication::platformName(); + bool wayland = settings::instance()->is_wayland(); + bool failed = false; - if (QString::compare(platform, "wayland", Qt::CaseInsensitive) == 0) { - OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); + if (wayland) { + OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", + QDBusConnection::sessionBus()); prev_clip_text = klipper.getClipboardContents(); - klipper.setClipboardContents(text); - } else { + auto reply = klipper.setClipboardContents(text); + + failed = reply.isError(); + } + + // Fallback to QClipboard if failed to execute klipper or if not on wayland + if (failed || !wayland) { auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); clip->setText(text); @@ -347,11 +353,6 @@ QString fake_keyboard::copy_to_clipboard(const QString& text) { return prev_clip_text; } -void fake_keyboard::restore_clipboard(const QString& prev_text) { - this->copy_to_clipboard(prev_text); -} - - // Send Ctrl+V (paste) to the active window using the configured method void fake_keyboard::send_ctrl_v() { // Delay to allow clipboard to update diff --git a/src/fake_keyboard.hpp b/src/fake_keyboard.hpp index e0b4bee0..e75da941 100644 --- a/src/fake_keyboard.hpp +++ b/src/fake_keyboard.hpp @@ -19,7 +19,6 @@ #include #include #include -#include "dbus_speech_adaptor.h" #ifdef USE_X11_FEATURES #include @@ -52,7 +51,6 @@ class fake_keyboard : public QObject { // Send Ctrl+V (paste) to the active window void send_ctrl_v(); QString copy_to_clipboard(const QString& text); - void restore_clipboard(const QString& prev_text); signals: void text_sending_completed(); From 3cecf22a64f9c95bdbdbba6dba0281bb80d353fd Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 18:24:02 -0300 Subject: [PATCH 04/19] new: building wl-clipboard embedded --- CMakeLists.txt | 7 +++++++ cmake/wl-clipboard.cmake | 20 ++++++++++++++++++++ flatpak/net.mkiol.SpeechNote-tiny.yaml | 9 +++++++++ flatpak/net.mkiol.SpeechNote.yaml | 10 ++++++++++ 4 files changed, 46 insertions(+) create mode 100644 cmake/wl-clipboard.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 696038aa..91cd961e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ option(BUILD_HTML2MD "download sources of html2md lib, build and link statically option(BUILD_MADDY "download sources of maddy lib" ON) option(BUILD_XDO "download sources of xdo lib and link statically" ON) option(BUILD_SAM "download sources of sam and link statically" ON) +option(BUILD_WL_CLIPBOARD "download sources of wl-clipboard, build and install" OFF) option(BUILD_XKBCOMMON "download sources of xkbcommon build and link statically" OFF) option(BUILD_QQC2_BREEZE_STYLE "download sources of QQC2 Breeze Style, build and install" OFF) @@ -400,6 +401,8 @@ set(dsnote_lib_sources ${sources_dir}/f5_engine.hpp ${sources_dir}/kokoro_engine.cpp ${sources_dir}/kokoro_engine.hpp + ${sources_dir}/wl-clipboard.cpp + ${sources_dir}/wl-clipboard.h ) if(WITH_DESKTOP) @@ -554,6 +557,10 @@ if(WITH_DESKTOP) list(APPEND includes ${xkbcommonx11_INCLUDE_DIRS}) endif() + if(BUILD_WL_CLIPBOARD) + include(${cmake_path}/wl-clipboard.cmake) + endif() + if(WITH_X11_FEATURES) find_package(X11 REQUIRED) list(APPEND deps_libs X11) diff --git a/cmake/wl-clipboard.cmake b/cmake/wl-clipboard.cmake new file mode 100644 index 00000000..021950b4 --- /dev/null +++ b/cmake/wl-clipboard.cmake @@ -0,0 +1,20 @@ +set(wl_clipboard_source_url "https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz") +set(wl_clipboard_checksum "6eb8081207fb5581d1d82c4bcd9587205a31a3d47bea3ebeb7f41aa1143783eb") + +if(${meson_bin} MATCHES "-NOTFOUND$") + message(FATAL_ERROR "meson not found but it is required to build wl-clipboard") +endif() + +ExternalProject_Add(wl_clipboard + SOURCE_DIR ${external_dir}/wl_clipboard + BINARY_DIR ${PROJECT_BINARY_DIR}/external/wl_clipboard + INSTALL_DIR ${PROJECT_BINARY_DIR}/external/ + URL ${wl_clipboard_source_url} + URL_HASH SHA256=${wl_clipboard_checksum} + CONFIGURE_COMMAND ${meson_bin} setup --prefix= --buildtype=release --libdir=/lib-dir + --datadir=/data-dir -Dfishcompletiondir=/completions + + BUILD_COMMAND ninja -C + BUILD_ALWAYS False + INSTALL_COMMAND ninja -C install +) \ No newline at end of file diff --git a/flatpak/net.mkiol.SpeechNote-tiny.yaml b/flatpak/net.mkiol.SpeechNote-tiny.yaml index dde29129..37d002a2 100644 --- a/flatpak/net.mkiol.SpeechNote-tiny.yaml +++ b/flatpak/net.mkiol.SpeechNote-tiny.yaml @@ -753,6 +753,15 @@ modules: - type: patch path: ../patches/sam.patch + - name: wl-clipboard + buildsystem: meson + config-opts: + - --buildtype=release + sources: + - type: archive + url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz + sha256: 6eb8081207fb5581d1d82c4bcd9587205a31a3d47bea3ebeb7f41aa1143783eb + - name: dsnote buildsystem: cmake-ninja config-opts: diff --git a/flatpak/net.mkiol.SpeechNote.yaml b/flatpak/net.mkiol.SpeechNote.yaml index 93327694..8084455e 100644 --- a/flatpak/net.mkiol.SpeechNote.yaml +++ b/flatpak/net.mkiol.SpeechNote.yaml @@ -71,6 +71,16 @@ add-extensions: version: stable versions: stable;beta;test;master modules: + - name: wl-clipboard + buildsystem: meson + config-opts: + - --buildtype=release + - -Dfishcompletiondir=no + sources: + - type: archive + url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz + sha256: 6eb8081207fb5581d1d82c4bcd9587205a31a3d47bea3ebeb7f41aa1143783eb + - name: fmt buildsystem: cmake-ninja config-opts: From 7f16ac1af15c7b6e3455e9b747b8fbc5290fa36c Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 18:24:30 -0300 Subject: [PATCH 05/19] new: wl-clipboard process implementation --- src/fake_keyboard.cpp | 34 +++++++++++++++++--- src/wl-clipboard.cpp | 59 ++++++++++++++++++++++++++++++++++ src/wl-clipboard.h | 73 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 src/wl-clipboard.cpp create mode 100644 src/wl-clipboard.h diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index e26a3789..0f03b1fb 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -40,6 +40,7 @@ #include "dbus_klipper_inf.h" #include "logger.hpp" #include "qtlogger.hpp" +#include "wl-clipboard.h" using namespace std::chrono_literals; @@ -330,21 +331,44 @@ void fake_keyboard::ydo_type_char(uint32_t c) { // Set the clipboard text QString fake_keyboard::copy_to_clipboard(const QString &text) { + if (text.isEmpty()) { + LOGE("Text is empty, skipping clipboard copy"); + return QString(""); + } + QString prev_clip_text; bool wayland = settings::instance()->is_wayland(); bool failed = false; + // Try Wayland Clipboard if (wayland) { + LOGE("Trying Wayland Clipboard"); + + if (!wl_paste_clipboard(prev_clip_text)) { + LOGE("Failed to paste from Wayland Clipboard"); + failed = true; + } + if (!wl_copy_to_clipboard(text)) { + LOGE("Failed to copy to Wayland Clipboard"); + failed = true; + } + } + + // Try Klipper Clipboard + if (failed && wayland) { + LOGE("Trying Klipper Clipboard"); OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); prev_clip_text = klipper.getClipboardContents(); auto reply = klipper.setClipboardContents(text); failed = reply.isError(); + failed = false; } - // Fallback to QClipboard if failed to execute klipper or if not on wayland + // Try QClipboard if (failed || !wayland) { + LOGE("Trying QClipboard"); auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); clip->setText(text); @@ -355,8 +379,10 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Send Ctrl+V (paste) to the active window using the configured method void fake_keyboard::send_ctrl_v() { + LOGE("Sending Control V"); + // Delay to allow clipboard to update - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); #ifdef USE_X11_FEATURES switch (m_method) { @@ -393,7 +419,7 @@ void fake_keyboard::send_ctrl_v_ydo() { press_and_wait(KEY_LEFTCTRL, 1); press_and_wait(KEY_V, 1); - std::this_thread::sleep_for(200ms); + std::this_thread::sleep_for(50ms); press_and_wait(KEY_V, 0); press_and_wait(KEY_LEFTCTRL, 0); @@ -441,7 +467,7 @@ void fake_keyboard::send_ctrl_v_legacy() { send_and_wait(KeyPress, ctrl_code); send_and_wait(KeyPress, v_code); - std::this_thread::sleep_for(200ms); + std::this_thread::sleep_for(50ms); send_and_wait(KeyRelease, v_code); send_and_wait(KeyRelease, ctrl_code); diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp new file mode 100644 index 00000000..a246c6a8 --- /dev/null +++ b/src/wl-clipboard.cpp @@ -0,0 +1,59 @@ +#include "wl-clipboard.h" + +#include +#include +#include + +#include "logger.hpp" + +// #ifdef WITH_FLATPAK +// const QString wl_copy_path = QString("/app/bin/wl-copy"); +// const QString wl_paste_path = QString("/app/bin/wl-paste"); +// #else +const QString wl_copy_path = QStandardPaths::findExecutable("wl-copy"); +const QString wl_paste_path = QStandardPaths::findExecutable("wl-paste"); +// #endif + +bool wl_copy_to_clipboard(const QString& text) { +#ifdef WITH_FLATPAK + LOGE("Using flatpak wl-copy"); +#else + LOGE("Using system wl-copy"); +#endif + LOGE("wl_copy_path: " << wl_copy_path.toStdString()); + + QProcess wl_copy; + wl_copy.start(wl_copy_path, QStringList() << text); + + if (!wl_copy.waitForStarted()) return false; + if (!wl_copy.waitForFinished()) return false; + + LOGE("wl-copy exit code: " << wl_copy.exitCode()); + LOGE("wl-copy error: " << wl_copy.readAllStandardError().toStdString()); + LOGE("wl-copy output: " << wl_copy.readAllStandardOutput().toStdString()); + + return wl_copy.exitCode() == 0; +} + +bool wl_paste_clipboard(QString& outText) { +#ifdef WITH_FLATPAK + LOGE("Using flatpak wl-paste"); +#else + LOGE("Using system wl-paste"); +#endif + + LOGE("wl-paste_path: " << wl_paste_path.toStdString()); + + QProcess wl_paste; + wl_paste.start(wl_paste_path, QStringList()); + + if (!wl_paste.waitForStarted()) return false; + if (!wl_paste.waitForFinished()) return false; + + // Copy text from standard output to outText as UTF-16 + outText = wl_paste.readAllStandardOutput(); + LOGE("wl-paste output: " << outText.toStdString()); + // auto buffer = wl_paste.readAllStandardOutput(); + // outText = QTextCodec::codecForMib(1015)->toUnicode(buffer); + return wl_paste.exitCode() == 0; +} \ No newline at end of file diff --git a/src/wl-clipboard.h b/src/wl-clipboard.h new file mode 100644 index 00000000..5f130fb8 --- /dev/null +++ b/src/wl-clipboard.h @@ -0,0 +1,73 @@ +#ifndef WL_CLIPBOARD_H +#define WL_CLIPBOARD_H + +#include + +/** + * @file wl-clipboard.h + * @brief Wayland clipboard operations wrapper + * + * This header provides functions for interacting with the Wayland clipboard + * using the wl-clipboard utilities (wl-copy and wl-paste). It supports both + * system installations and Flatpak environments. + */ + +/** + * @brief Copies text to the Wayland clipboard + * + * This function uses the wl-copy utility to copy the provided text to the + * Wayland clipboard. It automatically detects whether running in a Flatpak + * environment and uses the appropriate path for the wl-copy executable. + * + * @param text The QString containing the text to copy to the clipboard + * @return bool Returns true if the text was successfully copied, false + * otherwise + * + * @note The function logs debug information including the executable path, + * exit code, and any error output + * @warning Requires wl-copy to be installed (system-wide or in Flatpak + * environment) + * + * Example usage: + * @code + * QString myText = "Hello, World!"; + * if (wl_copy_to_clipboard(myText)) { + * qDebug() << "Text copied successfully"; + * } else { + * qDebug() << "Failed to copy text"; + * } + * @endcode + */ +bool wl_copy_to_clipboard(const QString& text); + +/** + * @brief Retrieves text from the Wayland clipboard + * + * This function uses the wl-paste utility to retrieve the current contents + * of the Wayland clipboard. It automatically detects whether running in a + * Flatpak environment and uses the appropriate path for the wl-paste + * executable. + * + * @param outText Reference to a QString that will be populated with the + * clipboard content + * @return bool Returns true if text was successfully retrieved, false otherwise + * + * @note The function logs debug information including the executable path + * and the retrieved text content + * @warning Requires wl-paste to be installed (system-wide or in Flatpak + * environment) + * @warning The output parameter is only modified if the function returns true + * + * Example usage: + * @code + * QString clipboardText; + * if (wl_paste_clipboard(clipboardText)) { + * qDebug() << "Clipboard content:" << clipboardText; + * } else { + * qDebug() << "Failed to retrieve clipboard content"; + * } + * @endcode + */ +bool wl_paste_clipboard(QString& outText); + +#endif \ No newline at end of file From 7b157de6288c095e51cf9ab6be97f5c86f6abf9c Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 18:27:36 -0300 Subject: [PATCH 06/19] fix: refactor wl-clipboard code --- src/fake_keyboard.cpp | 8 +++---- src/wl-clipboard.cpp | 49 +++++++++++++++++-------------------------- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 0f03b1fb..7e6ec9af 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -342,7 +342,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try Wayland Clipboard if (wayland) { - LOGE("Trying Wayland Clipboard"); + LOGD("Trying Wayland Clipboard"); if (!wl_paste_clipboard(prev_clip_text)) { LOGE("Failed to paste from Wayland Clipboard"); @@ -356,7 +356,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try Klipper Clipboard if (failed && wayland) { - LOGE("Trying Klipper Clipboard"); + LOGD("Trying Klipper Clipboard"); OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); prev_clip_text = klipper.getClipboardContents(); @@ -368,7 +368,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try QClipboard if (failed || !wayland) { - LOGE("Trying QClipboard"); + LOGD("Trying QClipboard"); auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); clip->setText(text); @@ -379,7 +379,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Send Ctrl+V (paste) to the active window using the configured method void fake_keyboard::send_ctrl_v() { - LOGE("Sending Control V"); + LOGD("Sending Control V"); // Delay to allow clipboard to update std::this_thread::sleep_for(std::chrono::milliseconds(10)); diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp index a246c6a8..af716ffc 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl-clipboard.cpp @@ -6,54 +6,43 @@ #include "logger.hpp" -// #ifdef WITH_FLATPAK -// const QString wl_copy_path = QString("/app/bin/wl-copy"); -// const QString wl_paste_path = QString("/app/bin/wl-paste"); -// #else const QString wl_copy_path = QStandardPaths::findExecutable("wl-copy"); const QString wl_paste_path = QStandardPaths::findExecutable("wl-paste"); -// #endif bool wl_copy_to_clipboard(const QString& text) { -#ifdef WITH_FLATPAK - LOGE("Using flatpak wl-copy"); -#else - LOGE("Using system wl-copy"); -#endif - LOGE("wl_copy_path: " << wl_copy_path.toStdString()); + LOGD("wl_copy_path: " << wl_copy_path.toStdString()); QProcess wl_copy; wl_copy.start(wl_copy_path, QStringList() << text); - if (!wl_copy.waitForStarted()) return false; - if (!wl_copy.waitForFinished()) return false; - - LOGE("wl-copy exit code: " << wl_copy.exitCode()); - LOGE("wl-copy error: " << wl_copy.readAllStandardError().toStdString()); - LOGE("wl-copy output: " << wl_copy.readAllStandardOutput().toStdString()); + if (!wl_copy.waitForStarted()) { + LOGW("wl-copy failed to start"); + return false; + } + if (!wl_copy.waitForFinished()) { + LOGW("wl-copy failed to finish"); + return false; + } return wl_copy.exitCode() == 0; } bool wl_paste_clipboard(QString& outText) { -#ifdef WITH_FLATPAK - LOGE("Using flatpak wl-paste"); -#else - LOGE("Using system wl-paste"); -#endif - - LOGE("wl-paste_path: " << wl_paste_path.toStdString()); + LOGD("wl-paste_path: " << wl_paste_path.toStdString()); QProcess wl_paste; wl_paste.start(wl_paste_path, QStringList()); - if (!wl_paste.waitForStarted()) return false; - if (!wl_paste.waitForFinished()) return false; + if (!wl_paste.waitForStarted()) { + LOGW("wl-paste failed to start"); + return false; + } + if (!wl_paste.waitForFinished()) { + LOGW("wl-paste failed to finish"); + return false; + } - // Copy text from standard output to outText as UTF-16 outText = wl_paste.readAllStandardOutput(); - LOGE("wl-paste output: " << outText.toStdString()); - // auto buffer = wl_paste.readAllStandardOutput(); - // outText = QTextCodec::codecForMib(1015)->toUnicode(buffer); + LOGD("wl-paste output: " << outText.toStdString()); return wl_paste.exitCode() == 0; } \ No newline at end of file From ddaa02135b97c9493e970d79a950e12b85d28799 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 18:42:10 -0300 Subject: [PATCH 07/19] new: minimal direct build & wl-clipboard build in README.md --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 8991eee1..78110782 100644 --- a/README.md +++ b/README.md @@ -418,6 +418,40 @@ To make build without support for Python components, add `-DWITH_PY=OFF` in cmak To see other build options search for `option(BUILD_XXX)` in `CMakeList.txt` file. +### Minimal Linux (direct build) + +For developing features locally, going through the whole build process can be time-consuming; use this to make a minimal direct build. + +```sh +git clone + +cd dsnote +mkdir build +cd build + +cmake ../ -DWITH_DESKTOP=ON \ + -DWITH_PY=ON \ + -DDOWNLOAD_VOSK=ON \ + -DBUILD_VOSK=OFF \ + -DBUILD_WHISPERCPP=OFF \ + -DBUILD_OPENBLAS=OFF \ + -DBUILD_RHVOICE=OFF \ + -DBUILD_RHVOICE_MODULE=OFF \ + -DBUILD_BERGAMOT=OFF +make +``` + +## Building wl-clipboard + +If you want to build wl-clipboard or if you do not have access to wl-clipboard in your system, and KDE Klipper or QClipboard are not working when you use simulated Ctrl+V when pasting text into the active window. + +You can add this flag to build wl-clipboard statically: + +```sh + +cmake ../ -DWITH_DESKTOP=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_WL_CLIPBOARD +``` + ## How to enable a custom model All models available for download are specified in the configuration file (config/models.json). From 075b0d8e5391e6d95e569010f5d48ece1162ca1c Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 18:47:59 -0300 Subject: [PATCH 08/19] fix: bug in kde klipper copying text to clipboard sucessful check --- src/fake_keyboard.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 7e6ec9af..ca1fa7c3 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -340,30 +340,29 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { bool wayland = settings::instance()->is_wayland(); bool failed = false; - // Try Wayland Clipboard + // Try wl-clipboard if (wayland) { - LOGD("Trying Wayland Clipboard"); + LOGD("Trying wl-clipboard"); if (!wl_paste_clipboard(prev_clip_text)) { - LOGE("Failed to paste from Wayland Clipboard"); + LOGE("Failed to paste from wl-clipboard"); failed = true; } if (!wl_copy_to_clipboard(text)) { - LOGE("Failed to copy to Wayland Clipboard"); + LOGE("Failed to copy to wl-clipboard"); failed = true; } } - // Try Klipper Clipboard + // Try Klipper if (failed && wayland) { - LOGD("Trying Klipper Clipboard"); + LOGD("Trying Klipper"); OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); prev_clip_text = klipper.getClipboardContents(); auto reply = klipper.setClipboardContents(text); failed = reply.isError(); - failed = false; } // Try QClipboard From 199cb9455901725224b84e19d51ddec511e65d5e Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sat, 15 Nov 2025 19:02:51 -0300 Subject: [PATCH 09/19] fix: added ugly solution to a QT Specific problem when copying and setting the clipboard in the same thread --- src/fake_keyboard.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index ca1fa7c3..489a17dc 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -368,9 +368,12 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try QClipboard if (failed || !wayland) { LOGD("Trying QClipboard"); + QEventLoop loop; auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); + loop.processEvents(); clip->setText(text); + loop.processEvents(); } return prev_clip_text; From 555156a7a47a2cd06ad607017775555981d03554 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 16 Nov 2025 11:19:33 -0300 Subject: [PATCH 10/19] new: added dbus permissions for kde klipper --- flatpak/net.mkiol.SpeechNote-tiny.yaml | 1 + flatpak/net.mkiol.SpeechNote.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/flatpak/net.mkiol.SpeechNote-tiny.yaml b/flatpak/net.mkiol.SpeechNote-tiny.yaml index 37d002a2..d572e135 100644 --- a/flatpak/net.mkiol.SpeechNote-tiny.yaml +++ b/flatpak/net.mkiol.SpeechNote-tiny.yaml @@ -23,6 +23,7 @@ finish-args: - --env=QT_QUICK_CONTROLS_HOVER_ENABLED=1 - --talk-name=org.freedesktop.Notifications - --talk-name=org.kde.StatusNotifierWatcher # system tray icon + - --talk-name=org.kde.klipper # advanced access to the system clipboard cleanup: - /cmake - /etc diff --git a/flatpak/net.mkiol.SpeechNote.yaml b/flatpak/net.mkiol.SpeechNote.yaml index 8084455e..8a343ed3 100644 --- a/flatpak/net.mkiol.SpeechNote.yaml +++ b/flatpak/net.mkiol.SpeechNote.yaml @@ -26,6 +26,7 @@ finish-args: - --env=QT_QUICK_CONTROLS_HOVER_ENABLED=1 - --talk-name=org.freedesktop.Notifications - --talk-name=org.kde.StatusNotifierWatcher # system tray icon + - --talk-name=org.kde.klipper # advanced access to the system clipboard cleanup: - /cmake - /etc From 2e470f8a4f8e08b9e70dbb4356f7662acac7d9a9 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 16 Nov 2025 16:24:00 -0300 Subject: [PATCH 11/19] new: addressing PR feedback --- cmake/wl-clipboard.cmake | 6 ++- flatpak/net.mkiol.SpeechNote-tiny.yaml | 2 + flatpak/net.mkiol.SpeechNote.yaml | 21 ++++---- src/dsnote_app.cpp | 8 ++- src/fake_keyboard.cpp | 20 ++++--- src/fake_keyboard.hpp | 2 +- src/wl-clipboard.cpp | 30 +++++++---- src/wl-clipboard.h | 73 +++----------------------- 8 files changed, 63 insertions(+), 99 deletions(-) diff --git a/cmake/wl-clipboard.cmake b/cmake/wl-clipboard.cmake index 021950b4..e01597ab 100644 --- a/cmake/wl-clipboard.cmake +++ b/cmake/wl-clipboard.cmake @@ -12,9 +12,11 @@ ExternalProject_Add(wl_clipboard URL ${wl_clipboard_source_url} URL_HASH SHA256=${wl_clipboard_checksum} CONFIGURE_COMMAND ${meson_bin} setup --prefix= --buildtype=release --libdir=/lib-dir - --datadir=/data-dir -Dfishcompletiondir=/completions + --datadir=/data-dir --datadir=/data-dir + -Dfishcompletiondir=no + -Dzshcompletiondir=no BUILD_COMMAND ninja -C BUILD_ALWAYS False INSTALL_COMMAND ninja -C install -) \ No newline at end of file +) diff --git a/flatpak/net.mkiol.SpeechNote-tiny.yaml b/flatpak/net.mkiol.SpeechNote-tiny.yaml index d572e135..752e71c7 100644 --- a/flatpak/net.mkiol.SpeechNote-tiny.yaml +++ b/flatpak/net.mkiol.SpeechNote-tiny.yaml @@ -758,6 +758,8 @@ modules: buildsystem: meson config-opts: - --buildtype=release + - -Dfishcompletiondir=no + - -Dzshcompletiondir=no sources: - type: archive url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz diff --git a/flatpak/net.mkiol.SpeechNote.yaml b/flatpak/net.mkiol.SpeechNote.yaml index 8a343ed3..295a2d06 100644 --- a/flatpak/net.mkiol.SpeechNote.yaml +++ b/flatpak/net.mkiol.SpeechNote.yaml @@ -72,16 +72,6 @@ add-extensions: version: stable versions: stable;beta;test;master modules: - - name: wl-clipboard - buildsystem: meson - config-opts: - - --buildtype=release - - -Dfishcompletiondir=no - sources: - - type: archive - url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz - sha256: 6eb8081207fb5581d1d82c4bcd9587205a31a3d47bea3ebeb7f41aa1143783eb - - name: fmt buildsystem: cmake-ninja config-opts: @@ -957,6 +947,17 @@ modules: - type: patch path: ../patches/sam.patch + - name: wl-clipboard + buildsystem: meson + config-opts: + - --buildtype=release + - -Dfishcompletiondir=no + - -Dzshcompletiondir=no + sources: + - type: archive + url: https://github.com/bugaevc/wl-clipboard/archive/refs/tags/v2.2.1.tar.gz + sha256: 6eb8081207fb5581d1d82c4bcd9587205a31a3d47bea3ebeb7f41aa1143783eb + - name: dsnote buildsystem: cmake-ninja config-opts: diff --git a/src/dsnote_app.cpp b/src/dsnote_app.cpp index 2a1dc645..28763a04 100644 --- a/src/dsnote_app.cpp +++ b/src/dsnote_app.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1036,8 +1037,11 @@ void dsnote_app::handle_stt_text_decoded(QString text, const QString &lang, auto prev_clip_text = m_fake_keyboard->copy_to_clipboard(text); - m_fake_keyboard->send_ctrl_v(); - m_fake_keyboard->copy_to_clipboard(prev_clip_text); + QTimer::singleShot(0, [this, prev_clip_text]{ + m_fake_keyboard->send_ctrl_v(); + m_fake_keyboard->copy_to_clipboard(prev_clip_text); + }); + emit text_decoded_to_active_window(); } else { diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 489a17dc..2da884c9 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "dbus_klipper_inf.h" #include "logger.hpp" @@ -333,7 +334,7 @@ void fake_keyboard::ydo_type_char(uint32_t c) { QString fake_keyboard::copy_to_clipboard(const QString &text) { if (text.isEmpty()) { LOGE("Text is empty, skipping clipboard copy"); - return QString(""); + return {}; } QString prev_clip_text; @@ -342,13 +343,18 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try wl-clipboard if (wayland) { - LOGD("Trying wl-clipboard"); + LOGD("trying wl-clipboard"); - if (!wl_paste_clipboard(prev_clip_text)) { + std::optional prev_clip_text_opt = wl_clipboard::getClipboard(); + + if (prev_clip_text_opt == std::nullopt) { LOGE("Failed to paste from wl-clipboard"); failed = true; + } else { + prev_clip_text = prev_clip_text_opt.value(); } - if (!wl_copy_to_clipboard(text)) { + + if (!wl_clipboard::setClipboard(text)) { LOGE("Failed to copy to wl-clipboard"); failed = true; } @@ -356,7 +362,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try Klipper if (failed && wayland) { - LOGD("Trying Klipper"); + LOGD("trying Klipper"); OrgKdeKlipperKlipperInterface klipper("org.kde.klipper", "/klipper", QDBusConnection::sessionBus()); prev_clip_text = klipper.getClipboardContents(); @@ -367,7 +373,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try QClipboard if (failed || !wayland) { - LOGD("Trying QClipboard"); + LOGD("trying QClipboard"); QEventLoop loop; auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); @@ -381,7 +387,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Send Ctrl+V (paste) to the active window using the configured method void fake_keyboard::send_ctrl_v() { - LOGD("Sending Control V"); + LOGD("sending Control V"); // Delay to allow clipboard to update std::this_thread::sleep_for(std::chrono::milliseconds(10)); diff --git a/src/fake_keyboard.hpp b/src/fake_keyboard.hpp index e75da941..968e1598 100644 --- a/src/fake_keyboard.hpp +++ b/src/fake_keyboard.hpp @@ -50,7 +50,7 @@ class fake_keyboard : public QObject { void send_text(const QString& text); // Send Ctrl+V (paste) to the active window void send_ctrl_v(); - QString copy_to_clipboard(const QString& text); + static QString copy_to_clipboard(const QString& text); signals: void text_sending_completed(); diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp index af716ffc..a97f6d42 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl-clipboard.cpp @@ -3,13 +3,17 @@ #include #include #include +#include #include "logger.hpp" -const QString wl_copy_path = QStandardPaths::findExecutable("wl-copy"); -const QString wl_paste_path = QStandardPaths::findExecutable("wl-paste"); +bool wl_clipboard::setClipboard(const QString& text) { + static const auto wl_copy_path = QStandardPaths::findExecutable("wl-copy"); + if (wl_copy_path.isEmpty()) { + LOGW("wl-copy not found"); + return false; + } -bool wl_copy_to_clipboard(const QString& text) { LOGD("wl_copy_path: " << wl_copy_path.toStdString()); QProcess wl_copy; @@ -27,7 +31,8 @@ bool wl_copy_to_clipboard(const QString& text) { return wl_copy.exitCode() == 0; } -bool wl_paste_clipboard(QString& outText) { +std::optional wl_clipboard::getClipboard() { + static const auto wl_paste_path = QStandardPaths::findExecutable("wl-paste"); LOGD("wl-paste_path: " << wl_paste_path.toStdString()); QProcess wl_paste; @@ -35,14 +40,19 @@ bool wl_paste_clipboard(QString& outText) { if (!wl_paste.waitForStarted()) { LOGW("wl-paste failed to start"); - return false; + return std::nullopt; } if (!wl_paste.waitForFinished()) { LOGW("wl-paste failed to finish"); - return false; + return std::nullopt; } - outText = wl_paste.readAllStandardOutput(); - LOGD("wl-paste output: " << outText.toStdString()); - return wl_paste.exitCode() == 0; -} \ No newline at end of file + if (wl_paste.exitCode() != 0) { + LOGW("wl-paste failed to exit"); + return std::nullopt; + } + + const auto output = wl_paste.readAllStandardOutput(); + LOGD("wl-paste output: " << output.toStdString()); + return output; +} diff --git a/src/wl-clipboard.h b/src/wl-clipboard.h index 5f130fb8..3b472f31 100644 --- a/src/wl-clipboard.h +++ b/src/wl-clipboard.h @@ -2,72 +2,11 @@ #define WL_CLIPBOARD_H #include +#include -/** - * @file wl-clipboard.h - * @brief Wayland clipboard operations wrapper - * - * This header provides functions for interacting with the Wayland clipboard - * using the wl-clipboard utilities (wl-copy and wl-paste). It supports both - * system installations and Flatpak environments. - */ +namespace wl_clipboard { + bool setClipboard(const QString& text); + std::optional getClipboard(); +} -/** - * @brief Copies text to the Wayland clipboard - * - * This function uses the wl-copy utility to copy the provided text to the - * Wayland clipboard. It automatically detects whether running in a Flatpak - * environment and uses the appropriate path for the wl-copy executable. - * - * @param text The QString containing the text to copy to the clipboard - * @return bool Returns true if the text was successfully copied, false - * otherwise - * - * @note The function logs debug information including the executable path, - * exit code, and any error output - * @warning Requires wl-copy to be installed (system-wide or in Flatpak - * environment) - * - * Example usage: - * @code - * QString myText = "Hello, World!"; - * if (wl_copy_to_clipboard(myText)) { - * qDebug() << "Text copied successfully"; - * } else { - * qDebug() << "Failed to copy text"; - * } - * @endcode - */ -bool wl_copy_to_clipboard(const QString& text); - -/** - * @brief Retrieves text from the Wayland clipboard - * - * This function uses the wl-paste utility to retrieve the current contents - * of the Wayland clipboard. It automatically detects whether running in a - * Flatpak environment and uses the appropriate path for the wl-paste - * executable. - * - * @param outText Reference to a QString that will be populated with the - * clipboard content - * @return bool Returns true if text was successfully retrieved, false otherwise - * - * @note The function logs debug information including the executable path - * and the retrieved text content - * @warning Requires wl-paste to be installed (system-wide or in Flatpak - * environment) - * @warning The output parameter is only modified if the function returns true - * - * Example usage: - * @code - * QString clipboardText; - * if (wl_paste_clipboard(clipboardText)) { - * qDebug() << "Clipboard content:" << clipboardText; - * } else { - * qDebug() << "Failed to retrieve clipboard content"; - * } - * @endcode - */ -bool wl_paste_clipboard(QString& outText); - -#endif \ No newline at end of file +#endif From 1856559cd8f2ac8fcbb05b0dbdb41e0cb5722c51 Mon Sep 17 00:00:00 2001 From: Lorenzo <57605930+lmtr0@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:34:51 -0300 Subject: [PATCH 12/19] Update src/dsnote_app.cpp Co-authored-by: mkiol --- src/dsnote_app.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dsnote_app.cpp b/src/dsnote_app.cpp index 28763a04..0490590f 100644 --- a/src/dsnote_app.cpp +++ b/src/dsnote_app.cpp @@ -1038,6 +1038,7 @@ void dsnote_app::handle_stt_text_decoded(QString text, const QString &lang, m_fake_keyboard->copy_to_clipboard(text); QTimer::singleShot(0, [this, prev_clip_text]{ + if (!m_fake_keyboard) return; m_fake_keyboard->send_ctrl_v(); m_fake_keyboard->copy_to_clipboard(prev_clip_text); }); From 1ae409a083a2ea1ed0f0e5df679896c14d4af61a Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Mon, 17 Nov 2025 15:37:38 -0300 Subject: [PATCH 13/19] fix: addressing PR comments --- src/fake_keyboard.cpp | 3 --- src/wl-clipboard.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 2da884c9..7e904265 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -374,12 +374,9 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { // Try QClipboard if (failed || !wayland) { LOGD("trying QClipboard"); - QEventLoop loop; auto *clip = QGuiApplication::clipboard(); prev_clip_text = clip->text(); - loop.processEvents(); clip->setText(text); - loop.processEvents(); } return prev_clip_text; diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp index a97f6d42..10ad976d 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl-clipboard.cpp @@ -14,8 +14,6 @@ bool wl_clipboard::setClipboard(const QString& text) { return false; } - LOGD("wl_copy_path: " << wl_copy_path.toStdString()); - QProcess wl_copy; wl_copy.start(wl_copy_path, QStringList() << text); @@ -33,7 +31,10 @@ bool wl_clipboard::setClipboard(const QString& text) { std::optional wl_clipboard::getClipboard() { static const auto wl_paste_path = QStandardPaths::findExecutable("wl-paste"); - LOGD("wl-paste_path: " << wl_paste_path.toStdString()); + if (wl_paste_path.isEmpty()) { + LOGW("wl-paste not found"); + return std::nullopt; + } QProcess wl_paste; wl_paste.start(wl_paste_path, QStringList()); @@ -53,6 +54,5 @@ std::optional wl_clipboard::getClipboard() { } const auto output = wl_paste.readAllStandardOutput(); - LOGD("wl-paste output: " << output.toStdString()); return output; } From 181b4fcf4fdd6df94f44456d8128f708c517ed93 Mon Sep 17 00:00:00 2001 From: Lorenzo <57605930+lmtr0@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:38:05 -0300 Subject: [PATCH 14/19] Update src/wl-clipboard.cpp Co-authored-by: mkiol --- src/wl-clipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp index a97f6d42..f92286a3 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl-clipboard.cpp @@ -52,7 +52,7 @@ std::optional wl_clipboard::getClipboard() { return std::nullopt; } - const auto output = wl_paste.readAllStandardOutput(); + return wl_paste.readAllStandardOutput(); LOGD("wl-paste output: " << output.toStdString()); return output; } From a28e32bb5a38285a2d08ae3fe92cbe17b6e19c9d Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Mon, 17 Nov 2025 15:40:27 -0300 Subject: [PATCH 15/19] fix: camelCase to snake_case --- src/fake_keyboard.cpp | 4 ++-- src/wl-clipboard.cpp | 4 ++-- src/wl-clipboard.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 7e904265..c8cc117d 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -345,7 +345,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { if (wayland) { LOGD("trying wl-clipboard"); - std::optional prev_clip_text_opt = wl_clipboard::getClipboard(); + std::optional prev_clip_text_opt = wl_clipboard::get_clipboard(); if (prev_clip_text_opt == std::nullopt) { LOGE("Failed to paste from wl-clipboard"); @@ -354,7 +354,7 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { prev_clip_text = prev_clip_text_opt.value(); } - if (!wl_clipboard::setClipboard(text)) { + if (!wl_clipboard::set_clipboard(text)) { LOGE("Failed to copy to wl-clipboard"); failed = true; } diff --git a/src/wl-clipboard.cpp b/src/wl-clipboard.cpp index 10ad976d..dd4b8df4 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl-clipboard.cpp @@ -7,7 +7,7 @@ #include "logger.hpp" -bool wl_clipboard::setClipboard(const QString& text) { +bool wl_clipboard::set_clipboard(const QString& text) { static const auto wl_copy_path = QStandardPaths::findExecutable("wl-copy"); if (wl_copy_path.isEmpty()) { LOGW("wl-copy not found"); @@ -29,7 +29,7 @@ bool wl_clipboard::setClipboard(const QString& text) { return wl_copy.exitCode() == 0; } -std::optional wl_clipboard::getClipboard() { +std::optional wl_clipboard::get_clipboard() { static const auto wl_paste_path = QStandardPaths::findExecutable("wl-paste"); if (wl_paste_path.isEmpty()) { LOGW("wl-paste not found"); diff --git a/src/wl-clipboard.h b/src/wl-clipboard.h index 3b472f31..b13d413a 100644 --- a/src/wl-clipboard.h +++ b/src/wl-clipboard.h @@ -5,8 +5,8 @@ #include namespace wl_clipboard { - bool setClipboard(const QString& text); - std::optional getClipboard(); + bool set_clipboard(const QString& text); + std::optional get_clipboard(); } #endif From 0a0de0dd7bb8c02b9a560bc9114374848914e1ca Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 23 Nov 2025 10:34:11 -0300 Subject: [PATCH 16/19] fix: addressed pr comments (#341) --- CMakeLists.txt | 4 ++-- src/fake_keyboard.cpp | 2 +- src/{wl-clipboard.cpp => wl_clipboard.cpp} | 2 +- src/{wl-clipboard.h => wl_clipboard.hpp} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{wl-clipboard.cpp => wl_clipboard.cpp} (98%) rename src/{wl-clipboard.h => wl_clipboard.hpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 91cd961e..9c830131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,8 +401,8 @@ set(dsnote_lib_sources ${sources_dir}/f5_engine.hpp ${sources_dir}/kokoro_engine.cpp ${sources_dir}/kokoro_engine.hpp - ${sources_dir}/wl-clipboard.cpp - ${sources_dir}/wl-clipboard.h + ${sources_dir}/wl_clipboard.cpp + ${sources_dir}/wl_clipboard.hpp ) if(WITH_DESKTOP) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index c8cc117d..9e0a46b4 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -41,7 +41,7 @@ #include "dbus_klipper_inf.h" #include "logger.hpp" #include "qtlogger.hpp" -#include "wl-clipboard.h" +#include "wl_clipboard.hpp" using namespace std::chrono_literals; diff --git a/src/wl-clipboard.cpp b/src/wl_clipboard.cpp similarity index 98% rename from src/wl-clipboard.cpp rename to src/wl_clipboard.cpp index b52d8aaa..4f043e68 100644 --- a/src/wl-clipboard.cpp +++ b/src/wl_clipboard.cpp @@ -1,4 +1,4 @@ -#include "wl-clipboard.h" +#include "wl_clipboard.hpp" #include #include diff --git a/src/wl-clipboard.h b/src/wl_clipboard.hpp similarity index 100% rename from src/wl-clipboard.h rename to src/wl_clipboard.hpp From baa1a880976da94cf5c1678438af530b923854f4 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Sun, 23 Nov 2025 10:37:12 -0300 Subject: [PATCH 17/19] fix: addressed pr comments (#341) (2) --- CMakeLists.txt | 2 +- README.md | 4 ++-- cmake/{wl-clipboard.cmake => wl_clipboard.cmake} | 0 src/fake_keyboard.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) rename cmake/{wl-clipboard.cmake => wl_clipboard.cmake} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c830131..dce3bdd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,7 +558,7 @@ if(WITH_DESKTOP) endif() if(BUILD_WL_CLIPBOARD) - include(${cmake_path}/wl-clipboard.cmake) + include(${cmake_path}/wl_clipboard.cmake) endif() if(WITH_X11_FEATURES) diff --git a/README.md b/README.md index 78110782..7f2871d9 100644 --- a/README.md +++ b/README.md @@ -441,11 +441,11 @@ cmake ../ -DWITH_DESKTOP=ON \ make ``` -## Building wl-clipboard +## Building WlClipboard If you want to build wl-clipboard or if you do not have access to wl-clipboard in your system, and KDE Klipper or QClipboard are not working when you use simulated Ctrl+V when pasting text into the active window. -You can add this flag to build wl-clipboard statically: +You can add this flag to build wl-clipboard: ```sh diff --git a/cmake/wl-clipboard.cmake b/cmake/wl_clipboard.cmake similarity index 100% rename from cmake/wl-clipboard.cmake rename to cmake/wl_clipboard.cmake diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 9e0a46b4..0facd62f 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -341,21 +341,21 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { bool wayland = settings::instance()->is_wayland(); bool failed = false; - // Try wl-clipboard + // Try wl_clipboard if (wayland) { - LOGD("trying wl-clipboard"); + LOGD("trying wl_clipboard"); std::optional prev_clip_text_opt = wl_clipboard::get_clipboard(); if (prev_clip_text_opt == std::nullopt) { - LOGE("Failed to paste from wl-clipboard"); + LOGE("Failed to paste from wl_clipboard"); failed = true; } else { prev_clip_text = prev_clip_text_opt.value(); } if (!wl_clipboard::set_clipboard(text)) { - LOGE("Failed to copy to wl-clipboard"); + LOGE("Failed to copy to wl_clipboard"); failed = true; } } From 778d6c85d60c10b772539b7ec0c907958186b4d1 Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Tue, 16 Dec 2025 16:31:26 -0300 Subject: [PATCH 18/19] fix: added delay to allow clipboard propagation to happen correctly --- src/wl_clipboard.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wl_clipboard.cpp b/src/wl_clipboard.cpp index 4f043e68..f8ead8c3 100644 --- a/src/wl_clipboard.cpp +++ b/src/wl_clipboard.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "logger.hpp" @@ -26,6 +28,9 @@ bool wl_clipboard::set_clipboard(const QString& text) { return false; } + // Sleep for 200 ms, this fixes an error, where the program doesn't have time to write the clipboard + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + return wl_copy.exitCode() == 0; } From 00516100418c31bff63e8da7dccd502bfbd941bc Mon Sep 17 00:00:00 2001 From: lmtr0 Date: Tue, 16 Dec 2025 16:34:22 -0300 Subject: [PATCH 19/19] fix: added delay to allow clipboard propagation to happen correctly --- src/fake_keyboard.cpp | 3 +++ src/wl_clipboard.cpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fake_keyboard.cpp b/src/fake_keyboard.cpp index 0facd62f..17ff0f8b 100644 --- a/src/fake_keyboard.cpp +++ b/src/fake_keyboard.cpp @@ -379,6 +379,9 @@ QString fake_keyboard::copy_to_clipboard(const QString &text) { clip->setText(text); } + // Sleep for 200 ms, fix clipboard not being written to the clipboard + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + return prev_clip_text; } diff --git a/src/wl_clipboard.cpp b/src/wl_clipboard.cpp index f8ead8c3..75c165c7 100644 --- a/src/wl_clipboard.cpp +++ b/src/wl_clipboard.cpp @@ -28,9 +28,6 @@ bool wl_clipboard::set_clipboard(const QString& text) { return false; } - // Sleep for 200 ms, this fixes an error, where the program doesn't have time to write the clipboard - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - return wl_copy.exitCode() == 0; }