diff --git a/include/display-internal.h b/include/display-internal.h index 5d8001b4b..9cf4f08ef 100644 --- a/include/display-internal.h +++ b/include/display-internal.h @@ -28,6 +28,7 @@ #ifndef ROFI_DISPLAY_INTERNAL_H #define ROFI_DISPLAY_INTERNAL_H +#include "display.h" #include "helper.h" #include "nkutils-bindings.h" #include @@ -48,7 +49,7 @@ typedef struct _display_proxy { void (*set_input_focus)(guint window); void (*revert_input_focus)(void); - char *(*get_clipboard_data)(int type); + void (*get_clipboard_data)(int type, ClipboardCb callback, void *user_data); void (*set_fullscreen_mode)(void); guint (*scale)(void); diff --git a/include/display.h b/include/display.h index 4c1c185f3..146240a50 100644 --- a/include/display.h +++ b/include/display.h @@ -127,7 +127,8 @@ enum clipboard_type { CLIPBOARD_PRIMARY, }; -char *display_get_clipboard_data(enum clipboard_type); +typedef void (* ClipboardCb)(char *clipboard_data, void *user_data); +void display_get_clipboard_data(enum clipboard_type, ClipboardCb callback, void* user_data); void display_set_fullscreen_mode(void); diff --git a/include/wayland-internal.h b/include/wayland-internal.h index e6d248cec..2a5e173d3 100644 --- a/include/wayland-internal.h +++ b/include/wayland-internal.h @@ -34,6 +34,10 @@ typedef struct { typedef struct _wayland_seat wayland_seat; +typedef struct { + void *offer; +} clipboard_data; + typedef struct { GMainLoop *main_loop; GWaterWaylandSource *main_loop_source; @@ -77,8 +81,7 @@ typedef struct { int32_t scale; NkBindingsSeat *bindings_seat; - char *clipboard_default_data; - char *clipboard_primary_data; + clipboard_data clipboards[2]; uint32_t layer_width; uint32_t layer_height; diff --git a/source/display.c b/source/display.c index cd290e059..8d69f4173 100644 --- a/source/display.c +++ b/source/display.c @@ -1,8 +1,8 @@ #include "keyb.h" #include -#include "display-internal.h" #include "display.h" +#include "display-internal.h" #include "view.h" @@ -41,8 +41,8 @@ void display_startup_notification(RofiHelperExecuteContext *context, guint display_scale(void) { return proxy->scale(); } -char *display_get_clipboard_data(enum clipboard_type type) { - return proxy->get_clipboard_data(type); +void display_get_clipboard_data(enum clipboard_type type, ClipboardCb callback, void* user_data) { + proxy->get_clipboard_data(type, callback, user_data); } void display_set_fullscreen_mode(void) { proxy->set_fullscreen_mode(); } diff --git a/source/view.c b/source/view.c index 559ada990..5294be0f1 100644 --- a/source/view.c +++ b/source/view.c @@ -921,6 +921,18 @@ static void rofi_view_input_changed(void) { } } +#ifdef ENABLE_WAYLAND +static void rofi_view_clipboard_callback(char *clipboard_data, G_GNUC_UNUSED void *user_data) { + RofiViewState *state = rofi_view_get_active(); + if (clipboard_data != NULL) { + if (state != NULL) { + rofi_view_handle_text(state, clipboard_data); + } + g_free(clipboard_data); + } +} +#endif + static void rofi_view_trigger_global_action(KeyBindingAction action) { RofiViewState *state = rofi_view_get_active(); switch (action) { @@ -936,10 +948,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) { #endif #ifdef ENABLE_WAYLAND if (config.backend == DISPLAY_WAYLAND) { - char *d = display_get_clipboard_data(CLIPBOARD_PRIMARY); - if (d != NULL) { - rofi_view_handle_text(current_active_menu, d); - } + display_get_clipboard_data(CLIPBOARD_PRIMARY, rofi_view_clipboard_callback, NULL); } #endif break; @@ -954,10 +963,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) { #endif #ifdef ENABLE_WAYLAND if (config.backend == DISPLAY_WAYLAND) { - char *d = display_get_clipboard_data(CLIPBOARD_DEFAULT); - if (d != NULL) { - rofi_view_handle_text(current_active_menu, d); - } + display_get_clipboard_data(CLIPBOARD_DEFAULT, rofi_view_clipboard_callback, NULL); } #endif break; diff --git a/source/wayland/display.c b/source/wayland/display.c index 6cdc7d2f4..65058d12e 100644 --- a/source/wayland/display.c +++ b/source/wayland/display.c @@ -885,13 +885,12 @@ static void wayland_keyboard_release(wayland_seat *self) { #define CLIPBOARD_READ_INCREMENT 1024 -typedef void (*clipboard_read_callback)(char *data); - struct clipboard_read_info { char *buffer; size_t size; int fd; - clipboard_read_callback callback; + ClipboardCb callback; + void *user_data; }; static gboolean clipboard_read_glib_callback(GIOChannel *channel, @@ -900,8 +899,9 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, struct clipboard_read_info *info = opaque; gsize read; - switch (g_io_channel_read_chars(channel, info->buffer + info->size, - CLIPBOARD_READ_INCREMENT, &read, NULL)) { + GIOStatus status = g_io_channel_read_chars(channel, info->buffer + info->size, + CLIPBOARD_READ_INCREMENT, &read, NULL); + switch (status) { case G_IO_STATUS_AGAIN: return TRUE; @@ -921,7 +921,12 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, default: info->buffer[info->size] = '\0'; - info->callback(info->buffer); + if (status == G_IO_STATUS_EOF) { + info->callback(info->buffer, info->user_data); + } else { // G_IO_STATUS_ERROR + g_warning("Could not read data from clipboard"); + g_free(info->buffer); + } g_io_channel_shutdown(channel, FALSE, NULL); g_io_channel_unref(channel); close(info->fd); @@ -930,7 +935,7 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, } } -static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) { +static gboolean clipboard_read_data(int fd, ClipboardCb callback, void *user_data) { GIOChannel *channel = g_io_channel_unix_new(fd); struct clipboard_read_info *info = g_malloc(sizeof *info); @@ -943,6 +948,7 @@ static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) { info->fd = fd; info->size = 0; info->callback = callback; + info->user_data = user_data; info->buffer = g_malloc(CLIPBOARD_READ_INCREMENT); if (info->buffer == NULL) { @@ -980,32 +986,24 @@ static void data_device_handle_data_offer(void *data, wl_data_offer_add_listener(offer, &data_offer_listener, NULL); } -static void clipboard_default_callback(char *data) { - if (wayland->clipboard_default_data != NULL) { - g_free(wayland->clipboard_default_data); +static void clipboard_handle_selection(enum clipboard_type cb_type, void *offer) { + clipboard_data *clipboard = &wayland->clipboards[cb_type]; + + if (clipboard->offer != NULL) { + if (cb_type == CLIPBOARD_DEFAULT) { + wl_data_offer_destroy(clipboard->offer); + } else { + zwp_primary_selection_offer_v1_destroy(clipboard->offer); + } } - wayland->clipboard_default_data = data; + clipboard->offer = offer; + } static void data_device_handle_selection(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer) { - if (offer == NULL) { - // clipboard is empty - return; - } - - int fds[2]; - if (pipe(fds) < 0) { - return; - } - wl_data_offer_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - wl_display_roundtrip(wayland->display); - - clipboard_read_data(fds[0], clipboard_default_callback); - wl_data_offer_destroy(offer); + clipboard_handle_selection(CLIPBOARD_DEFAULT, offer); } static const struct wl_data_device_listener data_device_listener = { @@ -1030,32 +1028,10 @@ static void primary_selection_device_handle_data_offer( offer, &primary_selection_offer_listener, NULL); } -static void clipboard_primary_callback(char *data) { - if (wayland->clipboard_primary_data != NULL) { - g_free(wayland->clipboard_primary_data); - } - wayland->clipboard_primary_data = data; -} - static void primary_selection_device_handle_selection( void *data, struct zwp_primary_selection_device_v1 *data_device, struct zwp_primary_selection_offer_v1 *offer) { - if (offer == NULL) { - // clipboard is empty - return; - } - - int fds[2]; - if (pipe(fds) < 0) { - return; - } - zwp_primary_selection_offer_v1_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - wl_display_roundtrip(wayland->display); - - clipboard_read_data(fds[0], clipboard_primary_callback); - zwp_primary_selection_offer_v1_destroy(offer); + clipboard_handle_selection(CLIPBOARD_PRIMARY, offer); } static const struct zwp_primary_selection_device_v1_listener @@ -1748,15 +1724,26 @@ static const struct _view_proxy *wayland_display_view_proxy(void) { static guint wayland_display_scale(void) { return wayland->scale; } -static char *wayland_get_clipboard_data(int type) { - switch (type) { - case CLIPBOARD_DEFAULT: - return wayland->clipboard_default_data; - case CLIPBOARD_PRIMARY: - return wayland->clipboard_primary_data; +static void wayland_get_clipboard_data(int cb_type, ClipboardCb callback, void *user_data) { + clipboard_data *clipboard = &wayland->clipboards[cb_type]; + + if (clipboard->offer == NULL) { + return; } - return NULL; + int fds[2]; + if (pipe(fds) < 0) { + return; + } + + if (cb_type == CLIPBOARD_DEFAULT) { + wl_data_offer_receive(clipboard->offer, "text/plain", fds[1]); + } else { + zwp_primary_selection_offer_v1_receive(clipboard->offer, "text/plain", fds[1]); + } + close(fds[1]); + + clipboard_read_data(fds[0], callback, user_data); } static void wayland_set_fullscreen_mode(void) {