WINGs: cross-client clipboard via wlr_seat selection
Bridges WINGs internal clipboard to the Wayland seat selection so copy/paste works between compositor-internal dialogs and real Wayland clients. Store creates a wlr_data_source and sets seat selection. Fetch reads from foreign selection sources via pipe.
This commit is contained in:
parent
0dc7e33e4b
commit
cee271f2cd
|
|
@ -16,6 +16,10 @@ static unsigned int wl_view_next_id = WL_VIEW_ID_BASE;
|
|||
void (*W_ViewCommitBackingFn)(unsigned long id) = NULL;
|
||||
void (*W_ViewDestroyBackingFn)(unsigned long id) = NULL;
|
||||
|
||||
/* Clipboard hooks set by WM backend for cross-client clipboard */
|
||||
void (*W_ClipboardStoreFn)(const char *text, int len) = NULL;
|
||||
char *(*W_ClipboardFetchFn)(int *len_out) = NULL;
|
||||
|
||||
extern void wl_view_destroy_backing_impl(unsigned long id);
|
||||
|
||||
/* Simple view lookup table (ID → W_View*) */
|
||||
|
|
@ -492,6 +496,8 @@ static void wl_clipboard_store(void *vctx, const char *text, int len)
|
|||
memcpy(wl_clipboard_buf, text, len);
|
||||
wl_clipboard_buf[len] = '\0';
|
||||
wl_clipboard_len = len;
|
||||
if (W_ClipboardStoreFn)
|
||||
W_ClipboardStoreFn(text, len);
|
||||
} else {
|
||||
wl_clipboard_buf = NULL;
|
||||
wl_clipboard_len = 0;
|
||||
|
|
@ -501,6 +507,17 @@ static void wl_clipboard_store(void *vctx, const char *text, int len)
|
|||
static char *wl_clipboard_fetch(void *vctx, int *len_out)
|
||||
{
|
||||
(void)vctx;
|
||||
/* Try seat selection first (cross-client paste) */
|
||||
if (W_ClipboardFetchFn) {
|
||||
int n = 0;
|
||||
char *text = W_ClipboardFetchFn(&n);
|
||||
if (text) {
|
||||
if (len_out)
|
||||
*len_out = n;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
/* Fall back to internal buffer */
|
||||
if (wl_clipboard_buf && wl_clipboard_len > 0) {
|
||||
char *copy = wmalloc(wl_clipboard_len + 1);
|
||||
memcpy(copy, wl_clipboard_buf, wl_clipboard_len);
|
||||
|
|
|
|||
|
|
@ -667,6 +667,9 @@ static void handle_seat_request_cursor(struct wl_listener *listener, void *data)
|
|||
static void handle_seat_request_set_selection(struct wl_listener *listener, void *data);
|
||||
/* Phase 26: primary selection request handler. */
|
||||
static void handle_seat_request_primary_selection(struct wl_listener *listener, void *data);
|
||||
/* Compositor-internal clipboard hooks for WINGs. */
|
||||
static void wl_clipboard_store_to_seat(const char *text, int len);
|
||||
static char *wl_clipboard_fetch_from_seat(int *len_out);
|
||||
/* Phase 28: idle inhibitor created/destroyed handler. */
|
||||
static void handle_idle_new_inhibitor(struct wl_listener *listener, void *data);
|
||||
/* Phase 29: scroll-wheel axis event handler. */
|
||||
|
|
@ -2323,6 +2326,11 @@ wl_display_open(const char *name)
|
|||
W_ViewCommitBackingFn = wl_view_commit_backing_impl;
|
||||
extern void (*W_ViewDestroyBackingFn)(unsigned long id);
|
||||
W_ViewDestroyBackingFn = wl_view_destroy_backing_impl;
|
||||
|
||||
extern void (*W_ClipboardStoreFn)(const char *text, int len);
|
||||
extern char *(*W_ClipboardFetchFn)(int *len_out);
|
||||
W_ClipboardStoreFn = wl_clipboard_store_to_seat;
|
||||
W_ClipboardFetchFn = wl_clipboard_fetch_from_seat;
|
||||
}
|
||||
if (wl_state.scene) {
|
||||
wwarning("wl_backend: scene graph created");
|
||||
|
|
@ -4710,6 +4718,103 @@ handle_seat_request_cursor(struct wl_listener *listener, void *data)
|
|||
ev->hotspot_x, ev->hotspot_y);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Compositor-internal clipboard (WINGs ↔ Wayland clients) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static char *wl_seat_clipboard_text = NULL;
|
||||
static int wl_seat_clipboard_len = 0;
|
||||
static struct wlr_data_source wl_seat_clipboard_source;
|
||||
|
||||
static void wl_clipboard_source_send(struct wlr_data_source *source,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
(void)source; (void)mime_type;
|
||||
if (wl_seat_clipboard_text && wl_seat_clipboard_len > 0)
|
||||
write(fd, wl_seat_clipboard_text, wl_seat_clipboard_len);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void wl_clipboard_source_destroy(struct wlr_data_source *source)
|
||||
{
|
||||
(void)source;
|
||||
/* Don't free — static lifetime, text freed separately */
|
||||
}
|
||||
|
||||
static const struct wlr_data_source_impl wl_clipboard_source_impl = {
|
||||
.send = wl_clipboard_source_send,
|
||||
.destroy = wl_clipboard_source_destroy,
|
||||
};
|
||||
|
||||
static void wl_clipboard_store_to_seat(const char *text, int len)
|
||||
{
|
||||
if (!wl_state.seat || !text || len <= 0)
|
||||
return;
|
||||
|
||||
if (wl_seat_clipboard_text)
|
||||
free(wl_seat_clipboard_text);
|
||||
wl_seat_clipboard_text = malloc(len + 1);
|
||||
memcpy(wl_seat_clipboard_text, text, len);
|
||||
wl_seat_clipboard_text[len] = '\0';
|
||||
wl_seat_clipboard_len = len;
|
||||
|
||||
wlr_data_source_init(&wl_seat_clipboard_source, &wl_clipboard_source_impl);
|
||||
wl_array_release(&wl_seat_clipboard_source.mime_types);
|
||||
wl_array_init(&wl_seat_clipboard_source.mime_types);
|
||||
char **mime = wl_array_add(&wl_seat_clipboard_source.mime_types, sizeof(char *));
|
||||
*mime = strdup("text/plain;charset=utf-8");
|
||||
mime = wl_array_add(&wl_seat_clipboard_source.mime_types, sizeof(char *));
|
||||
*mime = strdup("text/plain");
|
||||
|
||||
wlr_seat_set_selection(wl_state.seat, &wl_seat_clipboard_source,
|
||||
wl_display_next_serial(wl_state.display));
|
||||
}
|
||||
|
||||
static char *wl_clipboard_fetch_from_seat(int *len_out)
|
||||
{
|
||||
if (!wl_state.seat || !wl_state.seat->selection_source)
|
||||
return NULL;
|
||||
|
||||
/* If we own the selection, return our buffer directly */
|
||||
if (wl_state.seat->selection_source == &wl_seat_clipboard_source) {
|
||||
if (wl_seat_clipboard_text && wl_seat_clipboard_len > 0) {
|
||||
char *copy = malloc(wl_seat_clipboard_len + 1);
|
||||
memcpy(copy, wl_seat_clipboard_text, wl_seat_clipboard_len);
|
||||
copy[wl_seat_clipboard_len] = '\0';
|
||||
if (len_out)
|
||||
*len_out = wl_seat_clipboard_len;
|
||||
return copy;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Foreign source — read via pipe */
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0)
|
||||
return NULL;
|
||||
|
||||
wlr_data_source_send(wl_state.seat->selection_source,
|
||||
"text/plain;charset=utf-8", fds[1]);
|
||||
close(fds[1]);
|
||||
|
||||
char buf[4096];
|
||||
int total = 0;
|
||||
ssize_t n;
|
||||
while ((n = read(fds[0], buf + total, sizeof(buf) - total - 1)) > 0)
|
||||
total += n;
|
||||
close(fds[0]);
|
||||
|
||||
if (total <= 0)
|
||||
return NULL;
|
||||
|
||||
buf[total] = '\0';
|
||||
char *result = malloc(total + 1);
|
||||
memcpy(result, buf, total + 1);
|
||||
if (len_out)
|
||||
*len_out = total;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 25: seat request_set_selection — client calls wl_data_device.set_selection
|
||||
* (i.e. Ctrl-C or other clipboard write). Forward to wlr_seat so the
|
||||
|
|
|
|||
Loading…
Reference in New Issue