deskflow/src/lib/platform/PortalClipboard.h
Nick Bolton 03a4800677 feat: clipboard support for portal remote desktop
Add libportal clipboard handling to the remote desktop.
Supports both directions (server to Wayland client paste,
and copy from the Wayland client back to the server)
for UTF-8 text, plain text, and PNG/BMP images.

The shared mime-type table, BMP/DIB and PNG encode/decode helpers, and
portal selection read/write live in a new PortalClipboard
helper used by both PortalInputCapture and PortalRemoteDesktop.

If the portal returns a restored RemoteDesktop session without clipboard
enabled (a token persisted before clipboard support existed), discard
the restore token and reconnect so the user gets a fresh permission
dialog. Otherwise the session would loop with a clipboard-less token
forever.
2026-06-18 22:06:37 +01:00

63 lines
2.1 KiB
C++

/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "deskflow/IClipboard.h"
#include <QByteArray>
#include <libportal/portal.h>
namespace deskflow {
class EiClipboard;
class PortalClipboard
{
public:
struct SupportedMime
{
const char *mime;
IClipboard::Format format;
};
// Listed in preference order: richer formats first.
static constexpr SupportedMime kSupportedMimes[] = {
{"image/png", IClipboard::Format::Bitmap},
{"text/plain;charset=utf-8", IClipboard::Format::Text},
{"text/plain", IClipboard::Format::Text},
};
static constexpr int kReadTimeoutMs = 200;
static constexpr int kWriteTimeoutMs = 200;
static constexpr qint64 kChunkBytes = 64 * 1024;
static QByteArray formatMimeTypes(const char *const *mimeTypes);
static const SupportedMime *findSupportedMime(const char *mime);
static const SupportedMime *pickSupportedMime(const char *const *available);
static QByteArray encodeFormat(IClipboard::Format format, const QByteArray &data);
static QByteArray decodeFormat(IClipboard::Format format, const QByteArray &bytes);
static QByteArray readSelectionBytes(XdpSession *session, const char *mime, qint64 maxBytes);
/// Advertise the cache's formats to the portal selection.
static void claimOwnership(EiClipboard *cache, XdpSession *session);
/// Respond to a selection-transfer signal by writing the cache's bytes for
/// \p mime to the portal-provided fd. Always calls write_done.
static void serveSelectionTransfer(EiClipboard *cache, XdpSession *session, const char *mime, uint32_t serial);
/// Read every supported format offered by the portal into the cache.
/// Returns true if any data was deposited.
static bool
readSelectionIntoCache(EiClipboard *cache, XdpSession *session, const char *const *mimeTypes, qint64 maxBytes);
private:
static QByteArray dibToBmp(const QByteArray &dib);
static QByteArray bmpToDib(const QByteArray &bmp);
};
} // namespace deskflow