Skip to content

Commit

Permalink
xcb: Add support for reading the clipboard
Browse files Browse the repository at this point in the history
Change-Id: I50a2c4b8f804b5a2281cbf91d30865b8c6d7c737
Reviewed-on: https://codereview.kdab.com/c/kdab/kdutils/+/133272
Reviewed-by: Miłosz Kosobucki <milosz.kosobucki@kdab.com>
Tested-by: Continuous Integration <build@kdab.com>
  • Loading branch information
redstrate committed Nov 13, 2023
1 parent 749677f commit d3a5b04
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/KDGui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
105 changes: 105 additions & 0 deletions src/KDGui/platform/linux/xcb/linux_xcb_clipboard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
This file is part of KDUtils.
SPDX-FileCopyrightText: 2018-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Joshua Goins <joshua.goins@kdab.com>
SPDX-License-Identifier: MIT
Contact KDAB at <info@kdab.com> 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<char> buffer(clipboardLength);
std::copy_n(reinterpret_cast<char *>(xcb_get_property_value(propertyReply)), clipboardLength, buffer.data());

return buffer.data();
}

void KDGui::LinuxXcbClipboard::setText(std::string_view text)
{
// TODO: xcb stub
}
40 changes: 40 additions & 0 deletions src/KDGui/platform/linux/xcb/linux_xcb_clipboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
This file is part of KDUtils.
SPDX-FileCopyrightText: 2018-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Joshua Goins <joshua.goins@kdab.com>
SPDX-License-Identifier: MIT
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#pragma once

#include <cstdint>
#include <string>

#include <KDGui/abstract_clipboard.h>

#include <xcb/xcb.h>

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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ LinuxXcbPlatformIntegration::LinuxXcbPlatformIntegration()
m_screen = iter.data;

dumpScreenInfo(m_screen);

m_clipboard = std::make_unique<LinuxXcbClipboard>(this);
}

LinuxXcbPlatformIntegration::~LinuxXcbPlatformIntegration()
Expand All @@ -49,6 +51,11 @@ LinuxXcbPlatformIntegration::~LinuxXcbPlatformIntegration()
SPDLOG_DEBUG("Destroyed xcb_connection");
}

AbstractClipboard *LinuxXcbPlatformIntegration::clipboard()
{
return m_clipboard.get();
}

LinuxXkbKeyboard *LinuxXcbPlatformIntegration::keyboard()
{
if (!m_keyboard)
Expand Down
10 changes: 4 additions & 6 deletions src/KDGui/platform/linux/xcb/linux_xcb_platform_integration.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <KDGui/abstract_gui_platform_integration.h>
#include <KDGui/platform/linux/xcb/linux_xcb_platform_event_loop.h>
#include <KDGui/platform/linux/xcb/linux_xcb_platform_window.h>
#include <KDGui/platform/linux/xcb/linux_xcb_clipboard.h>
#include <KDFoundation/logging.h>

#include <xcb/xcb.h>
Expand All @@ -30,6 +31,8 @@ class KDGUI_API LinuxXcbPlatformIntegration : public AbstractGuiPlatformIntegrat
LinuxXcbPlatformIntegration();
~LinuxXcbPlatformIntegration() override;

AbstractClipboard *clipboard() override;

std::shared_ptr<spdlog::logger> logger() { return m_logger; }

LinuxXkbKeyboard *keyboard();
Expand All @@ -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;
Expand All @@ -62,6 +59,7 @@ class KDGUI_API LinuxXcbPlatformIntegration : public AbstractGuiPlatformIntegrat
xcb_screen_t *m_screen{ nullptr };
std::unique_ptr<LinuxXkbKeyboard> m_keyboard;
std::map<xcb_window_t, LinuxXcbPlatformWindow *> m_windows;
std::unique_ptr<LinuxXcbClipboard> m_clipboard;
};

} // namespace KDGui

0 comments on commit d3a5b04

Please sign in to comment.