From 851c3799ad0a0a1af4326511c310a303150e138b Mon Sep 17 00:00:00 2001 From: Ethan Burns Date: Sat, 31 Aug 2024 15:37:13 -0400 Subject: [PATCH] Link the snarf buffer to the compositor's copy/paste buffer on Wayland. --- src/cmd/devdraw/wayland.c | 172 +++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/src/cmd/devdraw/wayland.c b/src/cmd/devdraw/wayland.c index 8f7399f2..31ca25d4 100644 --- a/src/cmd/devdraw/wayland.c +++ b/src/cmd/devdraw/wayland.c @@ -94,6 +94,11 @@ static struct wl_shm *wl_shm; static struct wl_compositor *wl_compositor; static struct xdg_wm_base *xdg_wm_base; static struct wl_seat *wl_seat; +static struct wl_data_device_manager *wl_data_device_manager; +static struct wl_data_device *wl_data_device; + +static char *snarf; +uint32_t keyboard_enter_serial; // Optional global wayland objects. // Need to NULL check them before using. @@ -153,6 +158,9 @@ static void registry_global(void *data, struct wl_registry *wl_registry, } else if (strcmp(interface, wl_seat_interface.name) == 0) { wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 1); + } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { + wl_data_device_manager = wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 2); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1); @@ -207,6 +215,132 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; +void wl_data_device_listener_data_offer(void *data, + struct wl_data_device *wl_data_device, struct wl_data_offer *id) {} + +void wl_data_device_listener_data_enter(void *data, + struct wl_data_device *wl_data_device, uint32_t serial, + struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, + struct wl_data_offer *id) {} + +void wl_data_device_listener_data_leave(void *data, + struct wl_data_device *wl_data_device) {} + +void wl_data_device_listener_data_motion(void *data, + struct wl_data_device *wl_data_device, uint32_t time, + wl_fixed_t x, wl_fixed_t y) {} + +void wl_data_device_listener_data_drop(void *data, + struct wl_data_device *wl_data_device) {} + +void wl_data_device_listener_selection(void *data, + struct wl_data_device *wl_data_device, struct wl_data_offer *id) { + DEBUG("wl_data_device_listener_selection\n"); + + if (id == NULL) { + qlock(&wayland_lock); + free(snarf); + snarf = NULL; + qunlock(&wayland_lock); + DEBUG("wl_data_device_listener_selection: no data\n"); + return; + } + + int fds[2]; + if (pipe(fds) < 0) { + sysfatal("Failed to create pipe"); + } + wl_data_offer_receive(id, "text/plain", fds[1]); + close(fds[1]); + wl_display_roundtrip(wl_display); + + qlock(&wayland_lock); + + int total = 0; + snarf = NULL; + for (; ;) { + char buf[128]; + int n = read(fds[0], &buf, sizeof(buf)); + if (n < 0 && errno == EAGAIN) { + continue; + } + if (n < 0) { + sysfatal("Read failed"); + } + if (n == 0) { + break; + } + // +1 to ensure it's always at least null terminated. + char *tmp = calloc(1, total + n + 1); + if (snarf != NULL) { + strncpy(tmp, snarf, total); + } + memcpy(tmp+total, buf, n); + total += n; + snarf = tmp; + } + + DEBUG("wl_data_device_listener_selection: read [%s]\n", snarf); + qunlock(&wayland_lock); + close(fds[0]); +} + +static const struct wl_data_device_listener wl_data_device_listener = { + .data_offer = wl_data_device_listener_data_offer, + .enter = wl_data_device_listener_data_enter, + .leave = wl_data_device_listener_data_leave, + .motion = wl_data_device_listener_data_motion, + .drop = wl_data_device_listener_data_drop, + .selection = wl_data_device_listener_selection, +}; + +void wl_data_source_target(void *data, + struct wl_data_source *wl_data_source, + const char *mime_type) {} + +void wl_data_source_send(void *data, + struct wl_data_source *wl_data_source, + const char *mime_type, int32_t fd) { + DEBUG("wl_data_source_send(mime_type=%s)\n", mime_type); + + if (strcmp(mime_type, "text/plain") != 0) { + DEBUG("unknown mime type\n"); + close(fd); + return; + } + + qlock(&wayland_lock); + + int total = 0; + if (snarf != NULL) { + total = strlen(snarf); + } + DEBUG("wl_data_source_send: writing %d bytes\n", total); + char *p = snarf; + while (total > 0) { + int n = write(fd, p, total); + if (n < 0 && errno == EAGAIN) { + continue; + } + if (n < 0) { + sysfatal("Write error"); + } + p += n; + total -= n; + } + + qunlock(&wayland_lock); + close(fd); +} + +void wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) {} + +static const struct wl_data_source_listener wl_data_source_listener = { + .target = wl_data_source_target, + .send = wl_data_source_send, + .cancelled = wl_data_source_cancelled, +}; + void delete_buffer(WaylandBuffer *b) { munmap(b->data, b->size); wl_buffer_destroy(b->wl_buffer); @@ -503,6 +637,9 @@ void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { DEBUG("wl_keyboard_enter\n"); + qlock(&wayland_lock); + keyboard_enter_serial = serial; + qunlock(&wayland_lock); } void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, @@ -697,8 +834,14 @@ void gfx_main(void) { if (wl_seat == NULL) { sysfatal("Unable to bind wl_seat"); } + if (wl_data_device_manager == NULL) { + sysfatal("Unable to bind wl_data_device_manager"); + } wl_output_add_listener(wl_output, &wl_output_listener, NULL); xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); + wl_data_device = wl_data_device_manager_get_data_device( + wl_data_device_manager, wl_seat); + wl_data_device_add_listener(wl_data_device, &wl_data_device_listener, NULL); wl_display_roundtrip(wl_display); entered_gfx_loop = 1; @@ -954,11 +1097,36 @@ Memimage *rpc_attach(Client *c, char *label, char *winsize) { char *rpc_getsnarf(void) { DEBUG("rpc_getsnarf\n"); - return NULL; + qlock(&wayland_lock); + + if (snarf == NULL) { + qunlock(&wayland_lock); + return NULL; + } + + int n = strlen(snarf); + char *copy = calloc(1, n+1); + strncpy(copy, snarf, n); + qunlock(&wayland_lock); + return copy; } -void rpc_putsnarf(char*) { +void rpc_putsnarf(char *snarf_in) { DEBUG("rpc_putsnarf\n"); + qlock(&wayland_lock); + + int n = strlen(snarf_in); + free(snarf); + snarf = calloc(1, n+1); + strncpy(snarf, snarf_in, n); + + struct wl_data_source *source = + wl_data_device_manager_create_data_source(wl_data_device_manager); + wl_data_source_add_listener(source, &wl_data_source_listener, NULL); + wl_data_source_offer(source, "text/plain"); + wl_data_device_set_selection(wl_data_device, source, keyboard_enter_serial); + + qunlock(&wayland_lock); } void rpc_shutdown(void) {