diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 6733980a..6fb41c29 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -30,23 +30,29 @@ struct wlr_data_offer { struct wl_listener source_destroy; }; -struct wlr_data_source { - // source metadata - struct wl_array mime_types; - int32_t actions; - - // source implementation +/** + * A data source implementation. Only the `send` function is mandatory. Refer to + * the matching wl_data_source_* functions documentation to know what they do. + */ +struct wlr_data_source_impl { void (*send)(struct wlr_data_source *source, const char *mime_type, int32_t fd); void (*accept)(struct wlr_data_source *source, uint32_t serial, const char *mime_type); void (*cancel)(struct wlr_data_source *source); - // drag'n'drop implementation void (*dnd_drop)(struct wlr_data_source *source); void (*dnd_finish)(struct wlr_data_source *source); void (*dnd_action)(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action); +}; + +struct wlr_data_source { + const struct wlr_data_source_impl *impl; + + // source metadata + struct wl_array mime_types; + int32_t actions; // source status bool accepted; @@ -128,7 +134,7 @@ struct wlr_drag_drop_event { * Create a wl data device manager global for this display. */ struct wlr_data_device_manager *wlr_data_device_manager_create( - struct wl_display *display); + struct wl_display *display); /** * Destroys a wlr_data_device_manager and removes its wl_data_device_manager global. @@ -144,11 +150,67 @@ void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager); */ void wlr_seat_client_send_selection(struct wlr_seat_client *seat_client); +/** + * Sets the current selection for the seat. This removes the previous one if + * there was any. + */ void wlr_seat_set_selection(struct wlr_seat *seat, - struct wlr_data_source *source, uint32_t serial); + struct wlr_data_source *source, uint32_t serial); -void wlr_data_source_init(struct wlr_data_source *source); +/** + * Initializes the data source with the provided implementation. + */ +void wlr_data_source_init(struct wlr_data_source *source, + const struct wlr_data_source_impl *impl); +/** + * Finishes the data source. + */ void wlr_data_source_finish(struct wlr_data_source *source); +/** + * Sends the data as the specified MIME type over the passed file descriptor, + * then close it. + */ +void wlr_data_source_send(struct wlr_data_source *source, const char *mime_type, + int32_t fd); + +/** + * Notifies the data source that a target accepts one of the offered MIME types. + * If a target doesn't accept any of the offered types, `mime_type` is NULL. + */ +void wlr_data_source_accept(struct wlr_data_source *source, uint32_t serial, + const char *mime_type); + +/** + * Notifies the data source it is no longer valid and should be destroyed. That + * potentially destroys immediately the data source. + */ +void wlr_data_source_cancel(struct wlr_data_source *source); + +/** + * Notifies the data source that the drop operation was performed. This does not + * indicate acceptance. + * + * The data source may still be used in the future and should not be destroyed + * here. + */ +void wlr_data_source_dnd_drop(struct wlr_data_source *source); + +/** + * Notifies the data source that the drag-and-drop operation concluded. That + * potentially destroys immediately the data source. + */ +void wlr_data_source_dnd_finish(struct wlr_data_source *source); + +/** + * Notifies the data source that a target accepts the drag with the specified + * action. + * + * This shouldn't be called after `wlr_data_source_dnd_drop` unless the + * drag-and-drop operation ended in an "ask" action. + */ +void wlr_data_source_dnd_action(struct wlr_data_source *source, + enum wl_data_device_manager_dnd_action action); + #endif diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index 3e78760b..13b1d08a 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -84,9 +84,7 @@ static void data_offer_update_action(struct wlr_data_offer *offer) { return; } - if (offer->source->dnd_action) { - offer->source->dnd_action(offer->source, action); - } + wlr_data_source_dnd_action(offer->source, action); if (wl_resource_get_version(offer->resource) >= WL_DATA_OFFER_ACTION_SINCE_VERSION) { @@ -104,10 +102,7 @@ static void data_offer_accept(struct wl_client *client, // TODO check that client is currently focused by the input device - if (offer->source->accept) { - offer->source->accept(offer->source, serial, mime_type); - } - offer->source->accepted = (mime_type != NULL); + wlr_data_source_accept(offer->source, serial, mime_type); } static void data_offer_receive(struct wl_client *client, @@ -115,7 +110,7 @@ static void data_offer_receive(struct wl_client *client, struct wlr_data_offer *offer = data_offer_from_resource(resource); if (offer->source && offer == offer->source->offer) { - offer->source->send(offer->source, mime_type, fd); + wlr_data_source_send(offer->source, mime_type, fd); } else { close(fd); } @@ -131,15 +126,12 @@ static void data_source_notify_finish(struct wlr_data_source *source) { return; } - if (source->offer->in_ask && source->dnd_action) { - source->dnd_action(source, source->current_dnd_action); - } - - if (source->dnd_finish) { - source->dnd_finish(source); + if (source->offer->in_ask) { + wlr_data_source_dnd_action(source, source->current_dnd_action); } source->offer = NULL; + wlr_data_source_dnd_finish(source); } static void data_offer_finish(struct wl_client *client, @@ -199,10 +191,10 @@ static void data_offer_resource_destroy(struct wl_resource *resource) { WL_DATA_OFFER_ACTION_SINCE_VERSION) { data_source_notify_finish(offer->source); offer->source->offer = NULL; - } else if (offer->source->dnd_finish) { + } else if (offer->source->impl->dnd_finish) { // source->cancel can free the source offer->source->offer = NULL; - offer->source->cancel(offer->source); + wlr_data_source_cancel(offer->source); } else { offer->source->offer = NULL; } @@ -314,20 +306,15 @@ static void seat_client_selection_source_destroy( void wlr_seat_set_selection(struct wlr_seat *seat, struct wlr_data_source *source, uint32_t serial) { - if (source) { - assert(source->send); - assert(source->cancel); - } - if (seat->selection_source && seat->selection_serial - serial < UINT32_MAX / 2) { return; } if (seat->selection_source) { - seat->selection_source->cancel(seat->selection_source); - seat->selection_source = NULL; wl_list_remove(&seat->selection_source_destroy.link); + wlr_data_source_cancel(seat->selection_source); + seat->selection_source = NULL; } seat->selection_source = source; @@ -533,9 +520,7 @@ static uint32_t pointer_drag_button(struct wlr_seat_pointer_grab *grab, wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_drop(resource); } - if (drag->source->dnd_drop) { - drag->source->dnd_drop(drag->source); - } + wlr_data_source_dnd_drop(drag->source); if (drag->source->offer != NULL) { drag->source->offer->in_ask = @@ -548,8 +533,8 @@ static uint32_t pointer_drag_button(struct wlr_seat_pointer_grab *grab, .time = time, }; wlr_signal_emit_safe(&drag->events.drop, &event); - } else if (drag->source->dnd_finish) { - drag->source->cancel(drag->source); + } else if (drag->source->impl->dnd_finish) { + wlr_data_source_cancel(drag->source); } } @@ -899,36 +884,51 @@ static void data_device_destroy(struct wl_resource *resource) { struct client_data_source { struct wlr_data_source source; + struct wlr_data_source_impl impl; struct wl_resource *resource; }; +static void client_data_source_accept(struct wlr_data_source *wlr_source, + uint32_t serial, const char *mime_type); + +static struct client_data_source *client_data_source_from_wlr_data_source( + struct wlr_data_source *wlr_source) { + assert(wlr_source->impl->accept == client_data_source_accept); + return (struct client_data_source *)wlr_source; +} + static void client_data_source_accept(struct wlr_data_source *wlr_source, uint32_t serial, const char *mime_type) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_target(source->resource, mime_type); } static void client_data_source_send(struct wlr_data_source *wlr_source, const char *mime_type, int32_t fd) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_send(source->resource, mime_type, fd); close(fd); } static void client_data_source_cancel(struct wlr_data_source *wlr_source) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_cancelled(source->resource); } static void client_data_source_dnd_drop(struct wlr_data_source *wlr_source) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION); wl_data_source_send_dnd_drop_performed(source->resource); } static void client_data_source_dnd_finish(struct wlr_data_source *wlr_source) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION); wl_data_source_send_dnd_finished(source->resource); @@ -936,7 +936,8 @@ static void client_data_source_dnd_finish(struct wlr_data_source *wlr_source) { static void client_data_source_dnd_action(struct wlr_data_source *wlr_source, enum wl_data_device_manager_dnd_action action) { - struct client_data_source *source = (struct client_data_source *)wlr_source; + struct client_data_source *source = + client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION); wl_data_source_send_action(source->resource, action); @@ -947,6 +948,37 @@ static void data_source_destroy(struct wl_client *client, wl_resource_destroy(resource); } +static struct client_data_source *client_data_source_create( + struct wl_resource *source_resource) { + struct client_data_source *source = + calloc(1, sizeof(struct client_data_source)); + if (source == NULL) { + return NULL; + } + + source->resource = source_resource; + + source->impl.accept = client_data_source_accept; + source->impl.send = client_data_source_send; + source->impl.cancel = client_data_source_cancel; + + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { + source->impl.dnd_drop = client_data_source_dnd_drop; + } + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + source->impl.dnd_finish = client_data_source_dnd_finish; + } + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + source->impl.dnd_action = client_data_source_dnd_action; + } + + wlr_data_source_init(&source->source, &source->impl); + return source; +} + static void data_source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions) { struct client_data_source *source = @@ -1007,7 +1039,11 @@ static void data_source_resource_handle_destroy(struct wl_resource *resource) { free(source); } -void wlr_data_source_init(struct wlr_data_source *source) { +void wlr_data_source_init(struct wlr_data_source *source, + const struct wlr_data_source_impl *impl) { + assert(impl->send); + + source->impl = impl; wl_array_init(&source->mime_types); wl_signal_init(&source->events.destroy); source->actions = -1; @@ -1027,6 +1063,45 @@ void wlr_data_source_finish(struct wlr_data_source *source) { wl_array_release(&source->mime_types); } +void wlr_data_source_send(struct wlr_data_source *source, const char *mime_type, + int32_t fd) { + source->impl->send(source, mime_type, fd); +} + +void wlr_data_source_accept(struct wlr_data_source *source, uint32_t serial, + const char *mime_type) { + source->accepted = (mime_type != NULL); + if (source->impl->accept) { + source->impl->accept(source, serial, mime_type); + } +} + +void wlr_data_source_cancel(struct wlr_data_source *source) { + if (source->impl->cancel) { + source->impl->cancel(source); + } +} + +void wlr_data_source_dnd_drop(struct wlr_data_source *source) { + if (source->impl->dnd_drop) { + source->impl->dnd_drop(source); + } +} + +void wlr_data_source_dnd_finish(struct wlr_data_source *source) { + if (source->impl->dnd_finish) { + source->impl->dnd_finish(source); + } +} + +void wlr_data_source_dnd_action(struct wlr_data_source *source, + enum wl_data_device_manager_dnd_action action) { + source->current_dnd_action = action; + if (source->impl->dnd_action) { + source->impl->dnd_action(source, action); + } +} + void data_device_manager_get_data_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, @@ -1048,40 +1123,23 @@ void data_device_manager_get_data_device(struct wl_client *client, static void data_device_manager_create_data_source(struct wl_client *client, struct wl_resource *resource, uint32_t id) { + struct wl_resource *source_resource = wl_resource_create(client, + &wl_data_source_interface, wl_resource_get_version(resource), id); + if (source_resource == NULL) { + wl_resource_post_no_memory(resource); + return; + } + struct client_data_source *source = - calloc(1, sizeof(struct client_data_source)); + client_data_source_create(source_resource); if (source == NULL) { + wl_resource_destroy(source_resource); wl_resource_post_no_memory(resource); return; } - wlr_data_source_init(&source->source); - source->resource = wl_resource_create(client, &wl_data_source_interface, - wl_resource_get_version(resource), id); - if (source->resource == NULL) { - free(source); - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(source->resource, &data_source_impl, + wl_resource_set_implementation(source_resource, &data_source_impl, source, data_source_resource_handle_destroy); - - source->source.accept = client_data_source_accept; - source->source.send = client_data_source_send; - source->source.cancel = client_data_source_cancel; - - if (wl_resource_get_version(source->resource) >= - WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { - source->source.dnd_drop = client_data_source_dnd_drop; - } - if (wl_resource_get_version(source->resource) >= - WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { - source->source.dnd_finish = client_data_source_dnd_finish; - } - if (wl_resource_get_version(source->resource) >= - WL_DATA_SOURCE_ACTION_SINCE_VERSION) { - source->source.dnd_action = client_data_source_dnd_action; - } } static const struct wl_data_device_manager_interface diff --git a/types/wlr_seat.c b/types/wlr_seat.c index ed7fb470..3c1f17fa 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -366,9 +366,9 @@ void wlr_seat_destroy(struct wlr_seat *seat) { wl_list_remove(&seat->display_destroy.link); if (seat->selection_source) { - seat->selection_source->cancel(seat->selection_source); - seat->selection_source = NULL; wl_list_remove(&seat->selection_source_destroy.link); + wlr_data_source_cancel(seat->selection_source); + seat->selection_source = NULL; } if (seat->primary_selection_source) { seat->primary_selection_source->cancel(seat->primary_selection_source);