From d3a5b0486082413df6ef65a427d5abef1ebdbdfe Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Mon, 6 Nov 2023 08:16:14 -0500 Subject: [PATCH] xcb: Add support for reading the clipboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I50a2c4b8f804b5a2281cbf91d30865b8c6d7c737 Reviewed-on: https://codereview.kdab.com/c/kdab/kdutils/+/133272 Reviewed-by: Miłosz Kosobucki Tested-by: Continuous Integration --- src/KDGui/CMakeLists.txt | 2 + .../linux/xcb/linux_xcb_clipboard.cpp | 105 ++++++++++++++++++ .../platform/linux/xcb/linux_xcb_clipboard.h | 40 +++++++ .../xcb/linux_xcb_platform_integration.cpp | 7 ++ .../xcb/linux_xcb_platform_integration.h | 10 +- 5 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 src/KDGui/platform/linux/xcb/linux_xcb_clipboard.cpp create mode 100644 src/KDGui/platform/linux/xcb/linux_xcb_clipboard.h diff --git a/src/KDGui/CMakeLists.txt b/src/KDGui/CMakeLists.txt index 47cfd0a..b60a91c 100644 --- a/src/KDGui/CMakeLists.txt +++ b/src/KDGui/CMakeLists.txt @@ -26,6 +26,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") list( APPEND SOURCES + platform/linux/xcb/linux_xcb_clipboard.cpp platform/linux/xcb/linux_xcb_platform_event_loop.cpp platform/linux/xcb/linux_xcb_platform_integration.cpp platform/linux/xcb/linux_xcb_platform_window.cpp @@ -36,6 +37,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") list( APPEND HEADERS + platform/linux/xcb/linux_xcb_clipboard.h platform/linux/xcb/linux_xcb_platform_event_loop.h platform/linux/xcb/linux_xcb_platform_integration.h platform/linux/xcb/linux_xcb_platform_window.h diff --git a/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.cpp b/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.cpp new file mode 100644 index 0000000..c278651 --- /dev/null +++ b/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.cpp @@ -0,0 +1,105 @@ +/* + This file is part of KDUtils. + + SPDX-FileCopyrightText: 2018-2023 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Joshua Goins + + SPDX-License-Identifier: MIT + + Contact KDAB at for commercial licensing options. +*/ + +#include "linux_xcb_clipboard.h" + +#include "linux_xcb_platform_integration.h" + +KDGui::LinuxXcbClipboard::LinuxXcbClipboard(KDGui::LinuxXcbPlatformIntegration *integration) + : m_integration{ integration } +{ + auto connection = m_integration->connection(); + + // Create a dummy window that will be dedicated to clipboard I/O + m_window = xcb_generate_id(connection); + + xcb_create_window( + connection, + XCB_COPY_FROM_PARENT, + m_window, + m_integration->screen()->root, + 0, 0, + 1, + 1, + 10, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + m_integration->screen()->root_visual, + 0, + nullptr); + + // Get the CLIPBOARD atom, which holds the clipboard data + const xcb_intern_atom_cookie_t selCookie = xcb_intern_atom(connection, false, 9, "CLIPBOARD"); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, selCookie, nullptr); + m_clipboard = reply->atom; + + // Get the UTF8_STRING atom, which is used to exchange UTF8 data between clients + const xcb_intern_atom_cookie_t utf8Cookie = xcb_intern_atom(connection, false, 11, "UTF8_STRING"); + reply = xcb_intern_atom_reply(connection, utf8Cookie, nullptr); + m_utf8 = reply->atom; +} + +KDGui::LinuxXcbClipboard::~LinuxXcbClipboard() +{ + xcb_destroy_window(m_integration->connection(), m_window); +} + +std::string KDGui::LinuxXcbClipboard::text() +{ + auto connection = m_integration->connection(); + + const xcb_get_selection_owner_cookie_t ownerCookie = xcb_get_selection_owner(m_integration->connection(), m_clipboard); + auto ownerReply = xcb_get_selection_owner_reply(connection, ownerCookie, nullptr); + + // No one has any clipboard data + if (ownerReply == nullptr || ownerReply->owner == 0) { + return ""; + } + + xcb_convert_selection(connection, m_window, m_clipboard, m_utf8, m_clipboard, XCB_TIME_CURRENT_TIME); + xcb_flush(connection); + + // Await the selection reply from the other client + for (;;) { + if (auto xcbEvent = xcb_poll_for_event(connection)) { + const auto eventType = xcbEvent->response_type & 0x7f; + + if (eventType == XCB_SELECTION_NOTIFY) { + break; + } + } + } + + auto propertyCookie = xcb_get_property(connection, 1, m_window, m_clipboard, m_utf8, 0, 0); + auto propertyReply = xcb_get_property_reply(connection, propertyCookie, nullptr); + + const uint32_t ret = propertyReply->bytes_after; + if (ret == 0) { + return ""; + } + + propertyCookie = xcb_get_property(connection, 1, m_window, m_clipboard, m_utf8, 0, (ret + 4) >> 2); + propertyReply = xcb_get_property_reply(connection, propertyCookie, nullptr); + + const int clipboardLength = xcb_get_property_value_length(propertyReply); + if (clipboardLength == 0) { + return ""; + } + + std::vector buffer(clipboardLength); + std::copy_n(reinterpret_cast(xcb_get_property_value(propertyReply)), clipboardLength, buffer.data()); + + return buffer.data(); +} + +void KDGui::LinuxXcbClipboard::setText(std::string_view text) +{ + // TODO: xcb stub +} diff --git a/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.h b/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.h new file mode 100644 index 0000000..e2703f4 --- /dev/null +++ b/src/KDGui/platform/linux/xcb/linux_xcb_clipboard.h @@ -0,0 +1,40 @@ +/* + This file is part of KDUtils. + + SPDX-FileCopyrightText: 2018-2023 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Joshua Goins + + SPDX-License-Identifier: MIT + + Contact KDAB at for commercial licensing options. +*/ + +#pragma once + +#include +#include + +#include + +#include + +namespace KDGui { + +class LinuxXcbPlatformIntegration; + +class LinuxXcbClipboard : public AbstractClipboard +{ +public: + LinuxXcbClipboard(KDGui::LinuxXcbPlatformIntegration *integration); + ~LinuxXcbClipboard(); + + std::string text() override; + void setText(std::string_view text) override; + +private: + KDGui::LinuxXcbPlatformIntegration *m_integration{ nullptr }; + xcb_atom_t m_clipboard, m_utf8; + uint32_t m_window; +}; + +} // namespace KDGui diff --git a/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.cpp b/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.cpp index 18fa577..452e67a 100644 --- a/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.cpp +++ b/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.cpp @@ -38,6 +38,8 @@ LinuxXcbPlatformIntegration::LinuxXcbPlatformIntegration() m_screen = iter.data; dumpScreenInfo(m_screen); + + m_clipboard = std::make_unique(this); } LinuxXcbPlatformIntegration::~LinuxXcbPlatformIntegration() @@ -49,6 +51,11 @@ LinuxXcbPlatformIntegration::~LinuxXcbPlatformIntegration() SPDLOG_DEBUG("Destroyed xcb_connection"); } +AbstractClipboard *LinuxXcbPlatformIntegration::clipboard() +{ + return m_clipboard.get(); +} + LinuxXkbKeyboard *LinuxXcbPlatformIntegration::keyboard() { if (!m_keyboard) diff --git a/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.h b/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.h index ce871f5..e179375 100644 --- a/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.h +++ b/src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,8 @@ class KDGUI_API LinuxXcbPlatformIntegration : public AbstractGuiPlatformIntegrat LinuxXcbPlatformIntegration(); ~LinuxXcbPlatformIntegration() override; + AbstractClipboard *clipboard() override; + std::shared_ptr logger() { return m_logger; } LinuxXkbKeyboard *keyboard(); @@ -44,12 +47,6 @@ class KDGUI_API LinuxXcbPlatformIntegration : public AbstractGuiPlatformIntegrat void unregisterWindowForEvents(xcb_window_t xcbWindow) { m_windows.erase(xcbWindow); } LinuxXcbPlatformWindow *window(xcb_window_t xcbWindow) { return m_windows.at(xcbWindow); } - AbstractClipboard *clipboard() override - { - // TODO(xcb): Implement clipboard - return nullptr; - } - private: LinuxXcbPlatformEventLoop *createPlatformEventLoopImpl() override; LinuxXcbPlatformWindow *createPlatformWindowImpl(Window *window) override; @@ -62,6 +59,7 @@ class KDGUI_API LinuxXcbPlatformIntegration : public AbstractGuiPlatformIntegrat xcb_screen_t *m_screen{ nullptr }; std::unique_ptr m_keyboard; std::map m_windows; + std::unique_ptr m_clipboard; }; } // namespace KDGui