From 0207105291d02d88aff5ef9ff3ba924d0ee1cf24 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 19 May 2018 14:52:50 +0100 Subject: [PATCH] xwayland: start drag on dnd selection notify --- include/wlr/types/wlr_data_device.h | 4 ++ include/wlr/types/wlr_seat.h | 1 - include/xwayland/selection.h | 18 +++++++ include/xwayland/xwm.h | 1 + types/data_device/wlr_drag.c | 84 ++++++++++++++++------------- xwayland/selection/dnd.c | 68 +++++++++++++++++++---- xwayland/selection/incoming.c | 72 ++++++++++++------------- xwayland/selection/selection.c | 5 ++ 8 files changed, 170 insertions(+), 83 deletions(-) diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 80d4bc8b1..c4068c7d3 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -221,4 +221,8 @@ void wlr_data_source_dnd_finish(struct wlr_data_source *source); void wlr_data_source_dnd_action(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action); +struct wlr_drag *wlr_seat_client_start_grab(struct wlr_seat_client *client, + struct wlr_data_source *source, struct wlr_surface *icon_surface, + struct wlr_touch_point *point); + #endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index f4840c89b..144e8c24c 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -188,7 +188,6 @@ struct wlr_seat { // `drag` goes away before `drag_source`, when the implicit grab ends struct wlr_drag *drag; struct wlr_data_source *drag_source; - uint32_t drag_serial; struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; diff --git a/include/xwayland/selection.h b/include/xwayland/selection.h index 52b17f912..f14d0d088 100644 --- a/include/xwayland/selection.h +++ b/include/xwayland/selection.h @@ -1,6 +1,8 @@ #ifndef XWAYLAND_SELECTION_H #define XWAYLAND_SELECTION_H +#include +#include #include #define INCR_CHUNK_SIZE (64 * 1024) @@ -39,6 +41,18 @@ struct wlr_xwm_selection { struct wl_list outgoing; }; +struct wlr_xwayland_data_source { + struct wlr_data_source base; + struct wlr_xwm_selection *selection; + struct wl_array mime_types_atoms; +}; + +struct wlr_xwayland_primary_selection_source { + struct wlr_primary_selection_source base; + struct wlr_xwm_selection *selection; + struct wl_array mime_types_atoms; +}; + void xwm_selection_transfer_remove_source( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_close_source_fd( @@ -63,6 +77,10 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, bool data_source_is_xwayland(struct wlr_data_source *wlr_source); bool primary_selection_source_is_xwayland( struct wlr_primary_selection_source *wlr_source); +struct wlr_xwayland_data_source *xwayland_data_source_create( + struct wlr_xwm_selection *selection); +struct wlr_xwayland_data_source *xwayland_data_source_from_wlr_data_source( + struct wlr_data_source *wlr_source); void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag); int xwm_dnd_handle_xfixes_selection_notify(struct wlr_xwm *xwm, diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 08d377368..43ef718c3 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -116,6 +116,7 @@ struct wlr_xwm { struct wl_list surfaces; // wlr_xwayland_surface::link struct wl_list unpaired_surfaces; // wlr_xwayland_surface::unpaired_link + struct wlr_drag *incoming_drag; struct wlr_drag *drag; struct wlr_xwayland_surface *drag_focus; diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 331fba2f2..c9bf235eb 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -56,7 +56,7 @@ static void drag_set_focus(struct wlr_drag *drag, } struct wlr_seat_client *focus_client = wlr_seat_client_for_wl_client( - drag->seat_client->seat, wl_resource_get_client(surface->resource)); + drag->seat, wl_resource_get_client(surface->resource)); if (!focus_client) { return; } @@ -80,8 +80,7 @@ static void drag_set_focus(struct wlr_drag *drag, } if (!wl_list_empty(&focus_client->data_devices)) { - uint32_t serial = - wl_display_next_serial(drag->seat_client->seat->display); + uint32_t serial = wl_display_next_serial(drag->seat->display); struct wl_resource *resource; wl_resource_for_each(resource, &focus_client->data_devices) { wl_data_device_send_enter(resource, serial, surface->resource, @@ -384,54 +383,33 @@ static void seat_handle_drag_source_destroy(struct wl_listener *listener, seat->drag_source = NULL; } -bool seat_client_start_drag(struct wlr_seat_client *client, +struct wlr_drag *wlr_seat_client_start_grab(struct wlr_seat_client *client, struct wlr_data_source *source, struct wlr_surface *icon_surface, - struct wlr_surface *origin, uint32_t serial) { + struct wlr_touch_point *point) { + struct wlr_seat *seat = client->seat; + struct wlr_drag *drag = calloc(1, sizeof(struct wlr_drag)); if (drag == NULL) { - return false; + return NULL; } - + drag->seat = seat; wl_signal_init(&drag->events.focus); wl_signal_init(&drag->events.motion); wl_signal_init(&drag->events.drop); wl_signal_init(&drag->events.destroy); - struct wlr_seat *seat = client->seat; - drag->seat = seat; - - drag->is_pointer_grab = !wl_list_empty(&client->pointers) && - seat->pointer_state.button_count == 1 && - seat->pointer_state.grab_serial == serial && - seat->pointer_state.focused_surface && - seat->pointer_state.focused_surface == origin; - - bool is_touch_grab = !wl_list_empty(&client->touches) && - wlr_seat_touch_num_points(seat) == 1 && - seat->touch_state.grab_serial == serial; - - // set in the iteration - struct wlr_touch_point *point = NULL; - if (is_touch_grab) { - wl_list_for_each(point, &seat->touch_state.touch_points, link) { - is_touch_grab = point->surface && point->surface == origin; - break; - } - } - - if (!drag->is_pointer_grab && !is_touch_grab) { - free(drag); - return true; + if (point == NULL) { + drag->is_pointer_grab = true; } if (icon_surface) { - int32_t touch_id = (point ? point->touch_id : 0); + int32_t touch_id = point ? point->touch_id : 0; struct wlr_drag_icon *icon = drag_icon_create(icon_surface, client, drag->is_pointer_grab, touch_id); if (!icon) { free(drag); - return false; + return NULL; } drag->icon = icon; @@ -446,6 +424,7 @@ bool seat_client_start_drag(struct wlr_seat_client *client, } drag->seat_client = client; + drag->pointer_grab.data = drag; drag->pointer_grab.interface = &data_device_pointer_drag_interface; @@ -468,7 +447,6 @@ bool seat_client_start_drag(struct wlr_seat_client *client, } seat->drag = drag; // TODO: unset this thing somewhere - seat->drag_serial = serial; seat->drag_source = source; if (source != NULL) { @@ -477,6 +455,38 @@ bool seat_client_start_drag(struct wlr_seat_client *client, } wlr_signal_emit_safe(&seat->events.start_drag, drag); - - return true; + return drag; +} + +bool seat_client_start_drag(struct wlr_seat_client *client, + struct wlr_data_source *source, struct wlr_surface *icon_surface, + struct wlr_surface *origin, uint32_t serial) { + struct wlr_seat *seat = client->seat; + + bool is_pointer_grab = !wl_list_empty(&client->pointers) && + seat->pointer_state.button_count == 1 && + seat->pointer_state.grab_serial == serial && + seat->pointer_state.focused_surface && + seat->pointer_state.focused_surface == origin; + + bool is_touch_grab = !wl_list_empty(&client->touches) && + wlr_seat_touch_num_points(seat) == 1 && + seat->touch_state.grab_serial == serial; + + // set in the iteration + struct wlr_touch_point *point = NULL; + if (is_touch_grab) { + wl_list_for_each(point, &seat->touch_state.touch_points, link) { + is_touch_grab = point->surface && point->surface == origin; + break; + } + } + + if (!is_pointer_grab && !is_touch_grab) { + return true; + } + + struct wlr_drag *drag = wlr_seat_client_start_grab(client, source, + icon_surface, point); + return drag != NULL; } diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index f4a1196c2..0e54aa469 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -237,22 +237,33 @@ static void xwm_handle_dnd_finished(struct wlr_xwm *xwm, } static bool xwm_add_atom_to_mime_types(struct wlr_xwm *xwm, - struct wl_array *mime_types, xcb_atom_t atom) { + struct wl_array *mime_types, struct wl_array *mime_types_atoms, + xcb_atom_t atom) { char *mime_type = xwm_mime_type_from_atom(xwm, atom); if (mime_type == NULL) { return false; } + char **mime_type_ptr = wl_array_add(mime_types, sizeof(*mime_type_ptr)); if (mime_type_ptr == NULL) { return false; } *mime_type_ptr = mime_type; + + xcb_atom_t *mime_type_atom_ptr = + wl_array_add(mime_types_atoms, sizeof(*mime_type_atom_ptr)); + if (mime_type_atom_ptr == NULL) { + return false; + } + *mime_type_atom_ptr = atom; + return true; } static bool xwm_dnd_get_mime_types(struct wlr_xwm *xwm, - struct wl_array *mime_types, xcb_window_t source) { + struct wl_array *mime_types, struct wl_array *mime_types_atoms, + xcb_window_t source) { xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 1, // delete source, @@ -274,7 +285,8 @@ static bool xwm_dnd_get_mime_types(struct wlr_xwm *xwm, xcb_atom_t *atoms = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; ++i) { - if (!xwm_add_atom_to_mime_types(xwm, mime_types, atoms[i])) { + if (!xwm_add_atom_to_mime_types(xwm, mime_types, mime_types_atoms, + atoms[i])) { wlr_log(L_ERROR, "failed to add MIME type atom to list"); goto error; } @@ -307,8 +319,15 @@ static void xwm_handle_dnd_enter(struct wlr_xwm *xwm, return; } - struct wl_array mime_types; - wl_array_init(&mime_types); + if (xwm->incoming_drag == NULL) { + wlr_log(L_DEBUG, "ignoring XdndEnter client message because " + "no xwayland drag is being performed"); + return; + } + + struct wlr_xwayland_data_source *source = + xwayland_data_source_from_wlr_data_source(xwm->incoming_drag->source); + if ((data->data32[1] & 1) == 0) { // Less than 3 MIME types, those are in the message data for (size_t i = 0; i < 3; ++i) { @@ -316,13 +335,15 @@ static void xwm_handle_dnd_enter(struct wlr_xwm *xwm, if (atom == XCB_ATOM_NONE) { break; } - if (!xwm_add_atom_to_mime_types(xwm, &mime_types, atom)) { + if (!xwm_add_atom_to_mime_types(xwm, &source->base.mime_types, + &source->mime_types_atoms, atom)) { wlr_log(L_ERROR, "failed to add MIME type atom to list"); break; } } } else { - if (!xwm_dnd_get_mime_types(xwm, &mime_types, source_window)) { + if (!xwm_dnd_get_mime_types(xwm, &source->base.mime_types, + &source->mime_types_atoms, source_window)) { wlr_log(L_ERROR, "failed to add MIME type atom to list"); } } @@ -358,9 +379,38 @@ int xwm_dnd_handle_xfixes_selection_notify(struct wlr_xwm *xwm, selection->owner = event->owner; if (event->owner != XCB_ATOM_NONE) { - // TODO: start grab + wlr_log(L_INFO, "start grab"); + + xcb_map_window(xwm->xcb_conn, xwm->dnd_window); + static const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; + xcb_configure_window(xwm->xcb_conn, xwm->dnd_window, + XCB_CONFIG_WINDOW_STACK_MODE, values); + + struct wlr_seat *seat = selection->xwm->seat; + struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client( + seat, selection->xwm->xwayland->client); + + struct wlr_xwayland_data_source *source = + xwayland_data_source_create(selection); + if (source == NULL) { + return 0; + } + + selection->xwm->incoming_drag = + wlr_seat_client_start_grab(seat_client, &source->base, NULL, NULL); + if (selection->xwm->incoming_drag == NULL) { + wlr_log(L_ERROR, "could not start grab"); + } } else { - // TODO: end grab + wlr_log(L_INFO, "end grab"); + + static const uint32_t values[] = { XCB_STACK_MODE_BELOW }; + xcb_configure_window(xwm->xcb_conn, xwm->dnd_window, + XCB_CONFIG_WINDOW_STACK_MODE, values); + xcb_unmap_window(xwm->xcb_conn, xwm->dnd_window); + + // TODO: drag_end(selection->xwm->incoming_drag) + selection->xwm->incoming_drag = NULL; } return 1; diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 89361a577..93eb8bc10 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -174,12 +174,6 @@ static void source_send(struct wlr_xwm_selection *selection, transfer->source_fd = fd; } -struct x11_data_source { - struct wlr_data_source base; - struct wlr_xwm_selection *selection; - struct wl_array mime_types_atoms; -}; - static const struct wlr_data_source_impl data_source_impl; bool data_source_is_xwayland( @@ -187,16 +181,16 @@ bool data_source_is_xwayland( return wlr_source->impl == &data_source_impl; } -static struct x11_data_source *data_source_from_wlr_data_source( +struct wlr_xwayland_data_source *xwayland_data_source_from_wlr_data_source( struct wlr_data_source *wlr_source) { assert(data_source_is_xwayland(wlr_source)); - return (struct x11_data_source *)wlr_source; + return (struct wlr_xwayland_data_source *)wlr_source; } static void data_source_send(struct wlr_data_source *wlr_source, const char *mime_type, int32_t fd) { - struct x11_data_source *source = - data_source_from_wlr_data_source(wlr_source); + struct wlr_xwayland_data_source *source = + xwayland_data_source_from_wlr_data_source(wlr_source); struct wlr_xwm_selection *selection = source->selection; source_send(selection, &wlr_source->mime_types, &source->mime_types_atoms, @@ -204,24 +198,34 @@ static void data_source_send(struct wlr_data_source *wlr_source, } static void data_source_cancel(struct wlr_data_source *wlr_source) { - struct x11_data_source *source = - data_source_from_wlr_data_source(wlr_source); + struct wlr_xwayland_data_source *source = + xwayland_data_source_from_wlr_data_source(wlr_source); wlr_data_source_finish(&source->base); wl_array_release(&source->mime_types_atoms); free(source); } +struct wlr_xwayland_data_source *xwayland_data_source_create( + struct wlr_xwm_selection *selection) { + struct wlr_xwayland_data_source *source = + calloc(1, sizeof(struct wlr_xwayland_data_source)); + if (source == NULL) { + wlr_log(L_ERROR, "allocation failed"); + return NULL; + } + wlr_data_source_init(&source->base, &data_source_impl); + + source->selection = selection; + wl_array_init(&source->mime_types_atoms); + + return source; +} + static const struct wlr_data_source_impl data_source_impl = { .send = data_source_send, .cancel = data_source_cancel, }; -struct x11_primary_selection_source { - struct wlr_primary_selection_source base; - struct wlr_xwm_selection *selection; - struct wl_array mime_types_atoms; -}; - static void primary_selection_source_cancel( struct wlr_primary_selection_source *wlr_source); @@ -233,8 +237,8 @@ bool primary_selection_source_is_xwayland( static void primary_selection_source_send( struct wlr_primary_selection_source *wlr_source, const char *mime_type, int32_t fd) { - struct x11_primary_selection_source *source = - (struct x11_primary_selection_source *)wlr_source; + struct wlr_xwayland_primary_selection_source *source = + (struct wlr_xwayland_primary_selection_source *)wlr_source; struct wlr_xwm_selection *selection = source->selection; source_send(selection, &wlr_source->mime_types, &source->mime_types_atoms, @@ -243,8 +247,8 @@ static void primary_selection_source_send( static void primary_selection_source_cancel( struct wlr_primary_selection_source *wlr_source) { - struct x11_primary_selection_source *source = - (struct x11_primary_selection_source *)wlr_source; + struct wlr_xwayland_primary_selection_source *source = + (struct wlr_xwayland_primary_selection_source *)wlr_source; wlr_primary_selection_source_finish(&source->base); wl_array_release(&source->mime_types_atoms); free(source); @@ -332,15 +336,11 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { struct wlr_xwm *xwm = selection->xwm; if (selection == &xwm->clipboard_selection) { - struct x11_data_source *source = - calloc(1, sizeof(struct x11_data_source)); + struct wlr_xwayland_data_source *source = + xwayland_data_source_create(selection); if (source == NULL) { return; } - wlr_data_source_init(&source->base, &data_source_impl); - - source->selection = selection; - wl_array_init(&source->mime_types_atoms); bool ok = source_get_targets(selection, &source->base.mime_types, &source->mime_types_atoms); @@ -351,8 +351,8 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { wlr_data_source_cancel(&source->base); } } else if (selection == &xwm->primary_selection) { - struct x11_primary_selection_source *source = - calloc(1, sizeof(struct x11_primary_selection_source)); + struct wlr_xwayland_primary_selection_source *source = + calloc(1, sizeof(struct wlr_xwayland_primary_selection_source)); if (source == NULL) { return; } @@ -371,8 +371,6 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { } else { source->base.cancel(&source->base); } - } else if (selection == &xwm->dnd_selection) { - // TODO } } @@ -442,15 +440,17 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, } else { selection->owner = event->owner; - // This is our selection window. - // We have to use XCB_TIME_CURRENT_TIME when we claim the - // selection, so grab the actual timestamp here so we can - // answer TIMESTAMP conversion requests correctly. if (event->owner == selection->window) { + // This is our selection window. + // We have to use XCB_TIME_CURRENT_TIME when we claim the + // selection, so grab the actual timestamp here so we can + // answer TIMESTAMP conversion requests correctly. selection->timestamp = event->timestamp; return 1; } + // This is a real X client's selection + struct wlr_xwm_selection_transfer *transfer = &selection->incoming; transfer->incr = false; // doing this will give a selection notify where we actually handle the sync diff --git a/xwayland/selection/selection.c b/xwayland/selection/selection.c index 6b57f7c32..589f9c9a7 100644 --- a/xwayland/selection/selection.c +++ b/xwayland/selection/selection.c @@ -289,6 +289,11 @@ static void seat_handle_start_drag(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_start_drag); struct wlr_drag *drag = data; + if (drag && (drag->source == NULL || + !data_source_is_xwayland(drag->source))) { + return; + } + xwm_selection_set_owner(&xwm->dnd_selection, drag != NULL); xwm_seat_handle_start_drag(xwm, drag); }