From 703aeecb9547fa386c0780d991910793d101d870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 11 Jul 2019 17:02:21 +0200 Subject: [PATCH] selection: add support for pasting *from* primary --- main.c | 29 ++++++++++++++---- meson.build | 1 + selection.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ selection.h | 1 + terminal.h | 13 +++++++- 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index 4c4eff4a..f86f6e5f 100644 --- a/main.c +++ b/main.c @@ -131,6 +131,11 @@ handle_global(void *data, struct wl_registry *registry, term->wl.data_device_manager = wl_registry_bind( term->wl.registry, name, &wl_data_device_manager_interface, 1); } + + else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) { + term->wl.primary_selection_device_manager = wl_registry_bind( + term->wl.registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); + } } static void @@ -403,12 +408,22 @@ main(int argc, char *const *argv) "(wl_data_device_manager not implemented by server)"); goto out; } + if (term.wl.primary_selection_device_manager == NULL) { + LOG_ERR("no primary selection available"); + goto out; + } /* Clipboard */ term.wl.data_device = wl_data_device_manager_get_data_device( term.wl.data_device_manager, term.wl.seat); wl_data_device_add_listener(term.wl.data_device, &data_device_listener, &term); + /* Primary selection */ + term.wl.primary_selection_device = zwp_primary_selection_device_manager_v1_get_device( + term.wl.primary_selection_device_manager, term.wl.seat); + zwp_primary_selection_device_v1_add_listener( + term.wl.primary_selection_device, &primary_selection_device_listener, &term); + /* Cursor */ term.wl.pointer.surface = wl_compositor_create_surface(term.wl.compositor); if (term.wl.pointer.surface == NULL) { @@ -623,11 +638,6 @@ out: wl_surface_destroy(term.wl.pointer.surface); if (term.wl.keyboard != NULL) wl_keyboard_destroy(term.wl.keyboard); - if (term.selection.primary.data_source != NULL) - wl_data_source_destroy(term.selection.primary.data_source); - if (term.selection.primary.data_offer != NULL) - wl_data_offer_destroy(term.selection.primary.data_offer); - free(term.selection.primary.text); if (term.selection.clipboard.data_source != NULL) wl_data_source_destroy(term.selection.clipboard.data_source); if (term.selection.clipboard.data_offer != NULL) @@ -637,6 +647,15 @@ out: wl_data_device_destroy(term.wl.data_device); if (term.wl.data_device_manager != NULL) wl_data_device_manager_destroy(term.wl.data_device_manager); + if (term.selection.primary.data_source != NULL) + zwp_primary_selection_source_v1_destroy(term.selection.primary.data_source); + if (term.selection.primary.data_offer != NULL) + zwp_primary_selection_offer_v1_destroy(term.selection.primary.data_offer); + free(term.selection.primary.text); + if (term.wl.primary_selection_device != NULL) + zwp_primary_selection_device_v1_destroy(term.wl.primary_selection_device); + if (term.wl.primary_selection_device_manager != NULL) + zwp_primary_selection_device_manager_v1_destroy(term.wl.primary_selection_device_manager); if (term.wl.seat != NULL) wl_seat_destroy(term.wl.seat); if (term.wl.surface != NULL) diff --git a/meson.build b/meson.build index 51ee526f..58671d40 100644 --- a/meson.build +++ b/meson.build @@ -40,6 +40,7 @@ wl_proto_src = [] foreach prot : [ #'external/wlr-layer-shell-unstable-v1.xml', wayland_protocols_datadir + '/stable/xdg-shell/xdg-shell.xml', + wayland_protocols_datadir + '/unstable/primary-selection/primary-selection-unstable-v1.xml', #wayland_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.xml'] ] diff --git a/selection.c b/selection.c index 2b5bb140..7bc1df3a 100644 --- a/selection.c +++ b/selection.c @@ -342,6 +342,49 @@ void selection_from_primary(struct terminal *term) { LOG_WARN("selection from PRIMARY"); + struct primary *primary = &term->selection.primary; + if (primary->data_offer == NULL) + return; + + /* Prepare a pipe the other client can write its selection to us */ + int fds[2]; + if (pipe2(fds, O_CLOEXEC) == -1) { + LOG_ERRNO("failed to create pipe"); + return; + } + + int read_fd = fds[0]; + int write_fd = fds[1]; + + /* Give write-end of pipe to other client */ + zwp_primary_selection_offer_v1_receive( + primary->data_offer, "text/plain;charset=utf-8", write_fd); + wl_display_roundtrip(term->wl.display); + + /* Don't keep our copy of the write-end open (or we'll never get EOF) */ + close(write_fd); + + if (term->bracketed_paste) + write(term->ptmx, "\033[200~", 6); + + /* Read until EOF */ + while (true) { + char text[256]; + ssize_t amount = read(read_fd, text, sizeof(text)); + + if (amount == -1) { + LOG_ERRNO("failed to read clipboard data: %d", errno); + break; + } else if (amount == 0) + break; + + write(term->ptmx, text, amount); + } + + if (term->bracketed_paste) + write(term->ptmx, "\033[201~", 6); + + close(read_fd); } static void @@ -420,3 +463,46 @@ const struct wl_data_device_listener data_device_listener = { .drop = &drop, .selection = &selection, }; + +static void +primary_offer(void *data, + struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer, + const char *mime_type) +{ +} + +static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = { + .offer = &primary_offer, +}; + +static void +primary_data_offer(void *data, + struct zwp_primary_selection_device_v1 *zwp_primary_selection_device, + struct zwp_primary_selection_offer_v1 *offer) +{ +} + +static void +primary_selection(void *data, + struct zwp_primary_selection_device_v1 *zwp_primary_selection_device, + struct zwp_primary_selection_offer_v1 *id) +{ + /* Selection offer from other client, for primary */ + + struct terminal *term = data; + struct primary *primary = &term->selection.primary; + + if (primary->data_offer != NULL) + zwp_primary_selection_offer_v1_destroy(primary->data_offer); + + primary->data_offer = id; + if (id != NULL) { + zwp_primary_selection_offer_v1_add_listener( + id, &primary_selection_offer_listener, term); + } +} + +const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = { + .data_offer = &primary_data_offer, + .selection = &primary_selection, +}; diff --git a/selection.h b/selection.h index 8a8cae48..62b5eb44 100644 --- a/selection.h +++ b/selection.h @@ -5,6 +5,7 @@ #include "terminal.h" extern const struct wl_data_device_listener data_device_listener; +extern const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener; void selection_start(struct terminal *term, int col, int row); void selection_update(struct terminal *term, int col, int row); diff --git a/terminal.h b/terminal.h index 4d73f998..9af18d7e 100644 --- a/terminal.h +++ b/terminal.h @@ -8,9 +8,11 @@ #include #include +#include #include #include + #include "tllist.h" #define likely(c) __builtin_expect(!!(c), 1) @@ -25,6 +27,8 @@ struct wayland { struct wl_seat *seat; struct wl_data_device_manager *data_device_manager; struct wl_data_device *data_device; + struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; + struct zwp_primary_selection_device_v1 *primary_selection_device; struct wl_keyboard *keyboard; struct { struct wl_pointer *pointer; @@ -193,6 +197,13 @@ struct clipboard { uint32_t serial; }; +struct primary { + struct zwp_primary_selection_source_v1 *data_source; + struct zwp_primary_selection_offer_v1 *data_offer; + char *text; + uint32_t serial; +}; + struct terminal { pid_t slave; int ptmx; @@ -240,8 +251,8 @@ struct terminal { struct { struct coord start; struct coord end; - struct clipboard primary; struct clipboard clipboard; + struct primary primary; } selection; struct grid normal;