[client] clipboard: add transport provider interface
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / idd (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run

Route clipboard exchange through a provider interface. Connected
transports take priority while SPICE remains the fallback.

Track asynchronous X11 and Wayland transfers by request, preserving
clipboard ownership across provider changes.

Use PureSpice clipboard status notifications for capability and
lifecycle updates.
This commit is contained in:
Geoffrey McRae
2026-08-09 20:10:46 +10:00
parent 0da4bfa8dd
commit 4c53f5b9f8
22 changed files with 2287 additions and 539 deletions

View File

@@ -165,9 +165,11 @@ void app_overlayConfigRegisterTab(const char * title,
void app_clipboardRelease(void);
void app_clipboardNotifyTypes(const LG_ClipboardData types[], int count);
void app_clipboardNotifySize(const LG_ClipboardData type, size_t size);
void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size);
bool app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque);
void app_clipboardData(LG_ClipboardRequest request,
LG_ClipboardData type, const void * data, size_t size);
void app_clipboardAbort(LG_ClipboardRequest request);
bool app_clipboardRequest(LG_ClipboardData type,
LG_ClipboardReplyFn replyFn, void * opaque);
/**
* Show an alert on screen

View File

@@ -0,0 +1,108 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* 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)
* any later version.
*
* 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, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_CLIENT_CLIPBOARD_INTERFACE_
#define _H_LG_CLIENT_CLIPBOARD_INTERFACE_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef enum LG_ClipboardData
{
LG_CLIPBOARD_DATA_TEXT = 0,
LG_CLIPBOARD_DATA_PNG,
LG_CLIPBOARD_DATA_BMP,
LG_CLIPBOARD_DATA_TIFF,
LG_CLIPBOARD_DATA_JPEG,
LG_CLIPBOARD_DATA_NONE
}
LG_ClipboardData;
typedef void (*LG_ClipboardReplyFn)(void * opaque,
LG_ClipboardData type, const uint8_t * data, uint32_t size);
typedef uint64_t LG_ClipboardRequest;
#define LG_CLIPBOARD_REQUEST_INVALID UINT64_C(0)
typedef struct LG_ClipboardStatus
{
bool available;
uint32_t generation;
}
LG_ClipboardStatus;
typedef void (*LG_ClipboardStatusFn)(void * opaque,
const LG_ClipboardStatus * status);
typedef struct LG_ClipboardEventOps
{
/* The type list and data are borrowed for the duration of the callback.
* Event callbacks are always invoked after releasing backend locks. */
void (*notice)(void * opaque, const LG_ClipboardData types[], size_t count);
void (*data)(void * opaque, LG_ClipboardRequest request,
LG_ClipboardData type, const void * data, size_t size);
void (*release)(void * opaque);
bool (*request)(void * opaque, LG_ClipboardRequest request,
LG_ClipboardData type);
}
LG_ClipboardEventOps;
typedef struct LG_ClipboardOps
{
const char * name;
/* Operations must fail safely if the remote endpoint disappears. */
/* Registration must synchronously report the current status after releasing
* any backend locks. Status callbacks must be serialized and generations
* must advance whenever availability changes. Passing NULL unregisters the
* listener and synchronously quiesces its callbacks. Status callbacks must
* not be delivered from another operation or event callback. */
void (*setStatusListener)(void * opaque, LG_ClipboardStatusFn callback,
void * callbackOpaque);
/* Attach begins serialized event delivery and may synchronously replay the
* current remote notice. Detach synchronously quiesces event callbacks.
* Other operations must not deliver event callbacks synchronously. */
bool (*attach)(void * opaque, const LG_ClipboardEventOps * events,
void * eventOpaque);
void (*detach)(void * opaque);
bool (*release)(void * opaque);
/* All outbound arrays and data are borrowed only until the call returns. */
bool (*notifyTypes)(void * opaque, const LG_ClipboardData types[],
size_t count);
/* Data is a complete response to a remote request. NONE with no payload
* reports that the request could not be completed. The provider must finish
* any transport framing before returning. */
bool (*data)(void * opaque, LG_ClipboardRequest request,
LG_ClipboardData type, const void * data, size_t size);
/* A successful request produces exactly one matching data event unless the
* provider is detached, becomes unavailable, or publishes a newer notice
* or release first. */
bool (*request)(void * opaque, LG_ClipboardRequest request,
LG_ClipboardData type);
}
LG_ClipboardOps;
#endif

View File

@@ -26,18 +26,7 @@
#include <EGL/egl.h>
#include "common/types.h"
#include "common/debug.h"
typedef enum LG_ClipboardData
{
LG_CLIPBOARD_DATA_TEXT = 0,
LG_CLIPBOARD_DATA_PNG,
LG_CLIPBOARD_DATA_BMP,
LG_CLIPBOARD_DATA_TIFF,
LG_CLIPBOARD_DATA_JPEG,
LG_CLIPBOARD_DATA_NONE // enum max, not a data type
}
LG_ClipboardData;
#include "interface/clipboard.h"
typedef enum LG_DSProperty
{
@@ -136,9 +125,6 @@ typedef struct LG_DSInitParams
}
LG_DSInitParams;
typedef void (* LG_ClipboardReplyFn)(void * opaque, const LG_ClipboardData type,
uint8_t * data, uint32_t size);
typedef struct LG_DSGLContext
* LG_DSGLContext;
@@ -262,7 +248,7 @@ struct LG_DisplayServerOps
bool (*cbInit)(void);
void (*cbNotice)(LG_ClipboardData type);
void (*cbRelease)(void);
void (*cbRequest)(LG_ClipboardData type);
void (*cbRequest)(LG_ClipboardRequest request, LG_ClipboardData type);
};
#ifdef ENABLE_EGL

View File

@@ -28,6 +28,7 @@
#include "common/framebuffer.h"
#include "common/types.h"
#include "interface/audio.h"
#include "interface/clipboard.h"
#include "interface/input.h"
#define LG_TRANSPORT_MAX_DAMAGE_RECTS LG_MAX_FRAME_DAMAGE_RECTS
@@ -255,6 +256,11 @@ typedef struct LG_TransportOps
* valid until disconnect; NULL indicates that this session has no audio. */
const LG_AudioOps *(*getAudioOps)(LG_Transport * transport,
void ** opaque);
/* Queried after connect. The returned operations and opaque value remain
* valid until disconnect; NULL indicates that this session has no
* clipboard. */
const LG_ClipboardOps *(*getClipboardOps)(LG_Transport * transport,
void ** opaque);
bool (*attachRenderer)(LG_Transport * transport,
const LG_RendererInterop * interop);
void (*detachRenderer)(LG_Transport * transport);