From 4cb0697e576820774013f399c89fe85673e2f338 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 29 Nov 2018 19:30:04 +0100 Subject: [PATCH] data-device, primary-selection: add request_set_selection This makes compositors able to block and/or customize set_selection requests coming from clients. For instance, it's possible for a compositor to disable rich selection content (by removing all MIME types except text/plain). This commit implements the design proposed in [1]. Two new events are added to wlr_seat: request_set_selection and request_set_primary_selection. Compositors need to listen to these events and either destroy the source or effectively set the selection. Fixes https://github.com/swaywm/wlroots/issues/1138 [1]: https://github.com/swaywm/wlroots/issues/1367#issuecomment-442403454 --- include/rootston/seat.h | 2 + include/wlr/types/wlr_data_control_v1.h | 2 +- include/wlr/types/wlr_data_device.h | 6 +++ include/wlr/types/wlr_gtk_primary_selection.h | 2 +- include/wlr/types/wlr_primary_selection.h | 2 + include/wlr/types/wlr_seat.h | 26 +++++++++--- include/xwayland/xwm.h | 4 +- rootston/seat.c | 29 +++++++++++++- types/data_device/wlr_data_device.c | 40 ++++++++++++------- types/seat/wlr_seat.c | 6 ++- types/wlr_data_control_v1.c | 16 ++++---- types/wlr_gtk_primary_selection.c | 18 ++++----- types/wlr_primary_selection.c | 21 +++++++++- xwayland/selection/incoming.c | 6 +-- xwayland/selection/selection.c | 25 ++++++------ 15 files changed, 142 insertions(+), 63 deletions(-) diff --git a/include/rootston/seat.h b/include/rootston/seat.h index 105ba3aa..4667cd38 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -38,6 +38,8 @@ struct roots_seat { struct wl_list tablets; struct wl_list tablet_pads; + struct wl_listener request_set_selection; + struct wl_listener request_set_primary_selection; struct wl_listener new_drag_icon; struct wl_listener destroy; }; diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h index 50c96736..7d336344 100644 --- a/include/wlr/types/wlr_data_control_v1.h +++ b/include/wlr/types/wlr_data_control_v1.h @@ -34,7 +34,7 @@ struct wlr_data_control_device_v1 { struct wl_resource *selection_offer_resource; // current selection offer struct wl_listener seat_destroy; - struct wl_listener seat_selection; + struct wl_listener seat_set_selection; }; struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 209d917d..4ab181db 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -165,6 +165,12 @@ void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager); */ void wlr_seat_client_send_selection(struct wlr_seat_client *seat_client); +/** + * Requests a selection to be set for the seat. + */ +void wlr_seat_request_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial); + /** * Sets the current selection for the seat. NULL can be provided to clear it. * This removes the previous one if there was any. In case the selection doesn't diff --git a/include/wlr/types/wlr_gtk_primary_selection.h b/include/wlr/types/wlr_gtk_primary_selection.h index 7cf34201..4d7f84af 100644 --- a/include/wlr/types/wlr_gtk_primary_selection.h +++ b/include/wlr/types/wlr_gtk_primary_selection.h @@ -39,7 +39,7 @@ struct wlr_gtk_primary_selection_device { struct wl_listener seat_destroy; struct wl_listener seat_focus_change; - struct wl_listener seat_primary_selection; + struct wl_listener seat_set_primary_selection; void *data; }; diff --git a/include/wlr/types/wlr_primary_selection.h b/include/wlr/types/wlr_primary_selection.h index a2b23574..b1d45b61 100644 --- a/include/wlr/types/wlr_primary_selection.h +++ b/include/wlr/types/wlr_primary_selection.h @@ -48,6 +48,8 @@ void wlr_primary_selection_source_send( struct wlr_primary_selection_source *source, const char *mime_type, int fd); +void wlr_seat_request_set_primary_selection(struct wlr_seat *seat, + struct wlr_primary_selection_source *source, uint32_t serial); /** * Sets the current primary selection for the seat. NULL can be provided to * clear it. This removes the previous one if there was any. In case the diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 1ad5542e..6bc4af1a 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -23,6 +23,7 @@ struct wlr_seat_client { struct wl_client *client; struct wlr_seat *seat; + struct wl_list link; // lists of wl_resource struct wl_list resources; @@ -34,8 +35,6 @@ struct wlr_seat_client { struct { struct wl_signal destroy; } events; - - struct wl_list link; }; struct wlr_touch_point { @@ -226,13 +225,18 @@ struct wlr_seat { struct wl_signal touch_grab_begin; struct wl_signal touch_grab_end; + // wlr_seat_pointer_request_set_cursor_event struct wl_signal request_set_cursor; - struct wl_signal selection; - struct wl_signal primary_selection; + // wlr_seat_request_set_selection_event + struct wl_signal request_set_selection; + struct wl_signal set_selection; + // wlr_seat_request_set_primary_selection_event + struct wl_signal request_set_primary_selection; + struct wl_signal set_primary_selection; - struct wl_signal start_drag; - struct wl_signal new_drag_icon; + struct wl_signal start_drag; // wlr_drag + struct wl_signal new_drag_icon; // wlr_drag_icon struct wl_signal destroy; } events; @@ -247,6 +251,16 @@ struct wlr_seat_pointer_request_set_cursor_event { int32_t hotspot_x, hotspot_y; }; +struct wlr_seat_request_set_selection_event { + struct wlr_data_source *source; + uint32_t serial; +}; + +struct wlr_seat_request_set_primary_selection_event { + struct wlr_primary_selection_source *source; + uint32_t serial; +}; + struct wlr_seat_pointer_focus_change_event { struct wlr_seat *seat; struct wlr_surface *old_surface, *new_surface; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index c1be572b..a3bdc48c 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -128,8 +128,8 @@ struct wlr_xwm { struct wl_listener compositor_new_surface; struct wl_listener compositor_destroy; - struct wl_listener seat_selection; - struct wl_listener seat_primary_selection; + struct wl_listener seat_set_selection; + struct wl_listener seat_set_primary_selection; struct wl_listener seat_start_drag; struct wl_listener seat_drag_focus; struct wl_listener seat_drag_motion; diff --git a/rootston/seat.c b/rootston/seat.c index dda2f8df..43760a5e 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -7,9 +7,11 @@ #include #include #include +#include #include #include -#include "wlr/types/wlr_switch.h" +#include +#include #include #include #include @@ -20,7 +22,6 @@ #include "rootston/text_input.h" #include "rootston/xcursor.h" - static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct roots_keyboard *keyboard = wl_container_of(listener, keyboard, keyboard_key); @@ -536,6 +537,22 @@ static void roots_seat_handle_new_drag_icon(struct wl_listener *listener, roots_drag_icon_update_position(icon); } +static void roots_seat_handle_request_set_selection( + struct wl_listener *listener, void *data) { + struct roots_seat *seat = + wl_container_of(listener, seat, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(seat->seat, event->source, event->serial); +} + +static void roots_seat_handle_request_set_primary_selection( + struct wl_listener *listener, void *data) { + struct roots_seat *seat = + wl_container_of(listener, seat, request_set_primary_selection); + struct wlr_seat_request_set_primary_selection_event *event = data; + wlr_seat_set_primary_selection(seat->seat, event->source, event->serial); +} + void roots_drag_icon_update_position(struct roots_drag_icon *icon) { roots_drag_icon_damage_whole(icon); @@ -621,6 +638,14 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) { wl_list_insert(&input->seats, &seat->link); + seat->request_set_selection.notify = + roots_seat_handle_request_set_selection; + wl_signal_add(&seat->seat->events.request_set_selection, + &seat->request_set_selection); + seat->request_set_primary_selection.notify = + roots_seat_handle_request_set_primary_selection; + wl_signal_add(&seat->seat->events.request_set_primary_selection, + &seat->request_set_primary_selection); seat->new_drag_icon.notify = roots_seat_handle_new_drag_icon; wl_signal_add(&seat->seat->events.new_drag_icon, &seat->new_drag_icon); seat->destroy.notify = roots_seat_handle_destroy; diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index cda755b1..30f7d035 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -32,13 +32,13 @@ static void data_device_set_selection(struct wl_client *client, source = client_data_source_from_resource(source_resource); } - struct wlr_data_source *wlr_source = - source != NULL ? &source->source : NULL; - wlr_seat_set_selection(seat_client->seat, wlr_source, serial); - if (source != NULL) { source->finalized = true; } + + struct wlr_data_source *wlr_source = + source != NULL ? &source->source : NULL; + wlr_seat_request_set_selection(seat_client->seat, wlr_source, serial); } static void data_device_start_drag(struct wl_client *client, @@ -116,7 +116,23 @@ void wlr_seat_client_send_selection(struct wlr_seat_client *seat_client) { } } -static void seat_client_selection_source_destroy( +void wlr_seat_request_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial) { + if (seat->selection_source && + seat->selection_serial - serial < UINT32_MAX / 2) { + wlr_log(WLR_DEBUG, "Rejecting set_selection request, invalid serial " + "(%"PRIu32" <= %"PRIu32")", serial, seat->selection_serial); + return; + } + + struct wlr_seat_request_set_selection_event event = { + .source = source, + .serial = serial, + }; + wlr_signal_emit_safe(&seat->events.request_set_selection, &event); +} + +static void seat_handle_selection_source_destroy( struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, selection_source_destroy); @@ -132,16 +148,11 @@ static void seat_client_selection_source_destroy( } } - wlr_signal_emit_safe(&seat->events.selection, seat); + wlr_signal_emit_safe(&seat->events.set_selection, seat); } void wlr_seat_set_selection(struct wlr_seat *seat, struct wlr_data_source *source, uint32_t serial) { - if (seat->selection_source && - seat->selection_serial - serial < UINT32_MAX / 2) { - return; - } - if (seat->selection_source) { wl_list_remove(&seat->selection_source_destroy.link); wlr_data_source_cancel(seat->selection_source); @@ -153,19 +164,18 @@ void wlr_seat_set_selection(struct wlr_seat *seat, struct wlr_seat_client *focused_client = seat->keyboard_state.focused_client; - if (focused_client) { wlr_seat_client_send_selection(focused_client); } - wlr_signal_emit_safe(&seat->events.selection, seat); - if (source) { seat->selection_source_destroy.notify = - seat_client_selection_source_destroy; + seat_handle_selection_source_destroy; wl_signal_add(&source->events.destroy, &seat->selection_source_destroy); } + + wlr_signal_emit_safe(&seat->events.set_selection, seat); } diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 67ffd38a..c2503658 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -274,8 +274,10 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&seat->events.request_set_cursor); - wl_signal_init(&seat->events.selection); - wl_signal_init(&seat->events.primary_selection); + wl_signal_init(&seat->events.request_set_selection); + wl_signal_init(&seat->events.set_selection); + wl_signal_init(&seat->events.request_set_primary_selection); + wl_signal_init(&seat->events.set_primary_selection); wl_signal_init(&seat->events.pointer_grab_begin); wl_signal_init(&seat->events.pointer_grab_end); diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index 9812220c..930675c3 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -184,9 +184,8 @@ static void control_handle_set_selection(struct wl_client *client, } struct wlr_data_source *wlr_source = source ? &source->source : NULL; - struct wl_display *display = wl_client_get_display(client); - wlr_seat_set_selection(device->seat, wlr_source, - wl_display_next_serial(display)); + wlr_seat_request_set_selection(device->seat, wlr_source, + wl_display_next_serial(device->seat->display)); } static void control_handle_destroy(struct wl_client *client, @@ -232,10 +231,10 @@ static void control_handle_seat_destroy(struct wl_listener *listener, wlr_data_control_device_v1_destroy(device); } -static void control_handle_seat_selection(struct wl_listener *listener, +static void control_handle_seat_set_selection(struct wl_listener *listener, void *data) { struct wlr_data_control_device_v1 *device = - wl_container_of(listener, device, seat_selection); + wl_container_of(listener, device, seat_set_selection); control_send_selection(device); } @@ -250,7 +249,7 @@ void wlr_data_control_device_v1_destroy(struct wlr_data_control_device_v1 *devic wl_resource_set_user_data(device->selection_offer_resource, NULL); } wl_list_remove(&device->seat_destroy.link); - wl_list_remove(&device->seat_selection.link); + wl_list_remove(&device->seat_set_selection.link); wl_list_remove(&device->link); free(device); } @@ -319,8 +318,9 @@ static void manager_handle_get_data_device(struct wl_client *client, device->seat_destroy.notify = control_handle_seat_destroy; wl_signal_add(&device->seat->events.destroy, &device->seat_destroy); - device->seat_selection.notify = control_handle_seat_selection; - wl_signal_add(&device->seat->events.selection, &device->seat_selection); + device->seat_set_selection.notify = control_handle_seat_set_selection; + wl_signal_add(&device->seat->events.set_selection, + &device->seat_set_selection); wl_list_insert(&manager->devices, &device->link); wlr_signal_emit_safe(&manager->events.new_device, device); diff --git a/types/wlr_gtk_primary_selection.c b/types/wlr_gtk_primary_selection.c index 91b192cd..6c2e3f43 100644 --- a/types/wlr_gtk_primary_selection.c +++ b/types/wlr_gtk_primary_selection.c @@ -208,7 +208,7 @@ static void device_handle_set_selection(struct wl_client *client, source = &client_source->source; } - wlr_seat_set_primary_selection(device->seat, source, serial); + wlr_seat_request_set_primary_selection(device->seat, source, serial); } static void device_handle_destroy(struct wl_client *client, @@ -272,10 +272,10 @@ static void device_handle_seat_focus_change(struct wl_listener *listener, device_send_selection(device); } -static void device_handle_seat_primary_selection(struct wl_listener *listener, - void *data) { +static void device_handle_seat_set_primary_selection( + struct wl_listener *listener, void *data) { struct wlr_gtk_primary_selection_device *device = - wl_container_of(listener, device, seat_primary_selection); + wl_container_of(listener, device, seat_set_primary_selection); struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &device->offers) { @@ -314,10 +314,10 @@ static struct wlr_gtk_primary_selection_device *get_or_create_device( wl_signal_add(&seat->keyboard_state.events.focus_change, &device->seat_focus_change); - device->seat_primary_selection.notify = - device_handle_seat_primary_selection; - wl_signal_add(&seat->events.primary_selection, - &device->seat_primary_selection); + device->seat_set_primary_selection.notify = + device_handle_seat_set_primary_selection; + wl_signal_add(&seat->events.set_primary_selection, + &device->seat_set_primary_selection); return device; } @@ -329,7 +329,7 @@ static void device_destroy(struct wlr_gtk_primary_selection_device *device) { wl_list_remove(&device->link); wl_list_remove(&device->seat_destroy.link); wl_list_remove(&device->seat_focus_change.link); - wl_list_remove(&device->seat_primary_selection.link); + wl_list_remove(&device->seat_set_primary_selection.link); struct wl_resource *resource, *resource_tmp; wl_resource_for_each_safe(resource, resource_tmp, &device->offers) { destroy_offer(resource); diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c index be8b014d..9b33d362 100644 --- a/types/wlr_primary_selection.c +++ b/types/wlr_primary_selection.c @@ -41,13 +41,30 @@ void wlr_primary_selection_source_send( } +void wlr_seat_request_set_primary_selection(struct wlr_seat *seat, + struct wlr_primary_selection_source *source, uint32_t serial) { + if (seat->primary_selection_source && + seat->primary_selection_serial - serial < UINT32_MAX / 2) { + wlr_log(WLR_DEBUG, "Rejecting set_primary_selection request, " + "invalid serial (%"PRIu32" <= %"PRIu32")", + serial, seat->primary_selection_serial); + return; + } + + struct wlr_seat_request_set_primary_selection_event event = { + .source = source, + .serial = serial, + }; + wlr_signal_emit_safe(&seat->events.request_set_primary_selection, &event); +} + static void seat_handle_primary_selection_source_destroy( struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, primary_selection_source_destroy); wl_list_remove(&seat->primary_selection_source_destroy.link); seat->primary_selection_source = NULL; - wlr_signal_emit_safe(&seat->events.primary_selection, seat); + wlr_signal_emit_safe(&seat->events.set_primary_selection, seat); } void wlr_seat_set_primary_selection(struct wlr_seat *seat, @@ -75,5 +92,5 @@ void wlr_seat_set_primary_selection(struct wlr_seat *seat, &seat->primary_selection_source_destroy); } - wlr_signal_emit_safe(&seat->events.primary_selection, seat); + wlr_signal_emit_safe(&seat->events.set_primary_selection, seat); } diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 79197e60..e3f68ea7 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -350,7 +350,7 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { bool ok = source_get_targets(selection, &source->base.mime_types, &source->mime_types_atoms); if (ok) { - wlr_seat_set_selection(xwm->seat, &source->base, + wlr_seat_request_set_selection(xwm->seat, &source->base, wl_display_next_serial(xwm->xwayland->wl_display)); } else { wlr_data_source_cancel(&source->base); @@ -425,10 +425,10 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, // A real X client selection went away, not our // proxy selection if (selection == &xwm->clipboard_selection) { - wlr_seat_set_selection(xwm->seat, NULL, + wlr_seat_request_set_selection(xwm->seat, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } else if (selection == &xwm->primary_selection) { - wlr_seat_set_primary_selection(xwm->seat, NULL, + wlr_seat_request_set_primary_selection(xwm->seat, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } else if (selection == &xwm->dnd_selection) { // TODO: DND diff --git a/xwayland/selection/selection.c b/xwayland/selection/selection.c index dee7f9c3..a8cb5df4 100644 --- a/xwayland/selection/selection.c +++ b/xwayland/selection/selection.c @@ -255,11 +255,11 @@ static void xwm_selection_set_owner(struct wlr_xwm_selection *selection, } } -static void seat_handle_selection(struct wl_listener *listener, +static void handle_seat_set_selection(struct wl_listener *listener, void *data) { struct wlr_seat *seat = data; struct wlr_xwm *xwm = - wl_container_of(listener, xwm, seat_selection); + wl_container_of(listener, xwm, seat_set_selection); struct wlr_data_source *source = seat->selection_source; if (source != NULL && data_source_is_xwayland(source)) { @@ -269,11 +269,11 @@ static void seat_handle_selection(struct wl_listener *listener, xwm_selection_set_owner(&xwm->clipboard_selection, source != NULL); } -static void seat_handle_primary_selection(struct wl_listener *listener, +static void handle_seat_set_primary_selection(struct wl_listener *listener, void *data) { struct wlr_seat *seat = data; struct wlr_xwm *xwm = - wl_container_of(listener, xwm, seat_primary_selection); + wl_container_of(listener, xwm, seat_set_primary_selection); struct wlr_primary_selection_source *source = seat->primary_selection_source; @@ -294,8 +294,8 @@ static void seat_handle_start_drag(struct wl_listener *listener, void *data) { void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { if (xwm->seat != NULL) { - wl_list_remove(&xwm->seat_selection.link); - wl_list_remove(&xwm->seat_primary_selection.link); + wl_list_remove(&xwm->seat_set_selection.link); + wl_list_remove(&xwm->seat_set_primary_selection.link); wl_list_remove(&xwm->seat_start_drag.link); xwm->seat = NULL; } @@ -306,13 +306,14 @@ void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { xwm->seat = seat; - wl_signal_add(&seat->events.selection, &xwm->seat_selection); - xwm->seat_selection.notify = seat_handle_selection; - wl_signal_add(&seat->events.primary_selection, &xwm->seat_primary_selection); - xwm->seat_primary_selection.notify = seat_handle_primary_selection; + wl_signal_add(&seat->events.set_selection, &xwm->seat_set_selection); + xwm->seat_set_selection.notify = handle_seat_set_selection; + wl_signal_add(&seat->events.set_primary_selection, + &xwm->seat_set_primary_selection); + xwm->seat_set_primary_selection.notify = handle_seat_set_primary_selection; wl_signal_add(&seat->events.start_drag, &xwm->seat_start_drag); xwm->seat_start_drag.notify = seat_handle_start_drag; - seat_handle_selection(&xwm->seat_selection, seat); - seat_handle_primary_selection(&xwm->seat_primary_selection, seat); + handle_seat_set_selection(&xwm->seat_set_selection, seat); + handle_seat_set_primary_selection(&xwm->seat_set_primary_selection, seat); }