From 0d7a81ccdfda7d21ece47e0715a25bd301363cc2 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 28 Mar 2018 12:59:11 -0400 Subject: [PATCH] xwayland: send DND_ENTER --- include/wlr/types/wlr_data_device.h | 5 + include/wlr/types/wlr_seat.h | 4 + include/xwayland/xwm.h | 6 +- rootston/cursor.c | 3 +- types/wlr_data_device.c | 82 +++++++------ types/wlr_seat.c | 1 + xwayland/selection.c | 173 ++++++++++++++++++++-------- 7 files changed, 192 insertions(+), 82 deletions(-) diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index ff4a0f7e..cefcc979 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -104,6 +104,11 @@ struct wlr_drag { struct wl_listener source_destroy; struct wl_listener seat_client_destroy; struct wl_listener icon_destroy; + + struct { + struct wl_signal focus; + struct wl_signal destroy; + } events; }; /** diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 124c1cb8..435a3e7e 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -187,6 +187,9 @@ struct wlr_seat { struct wlr_primary_selection_source *primary_selection_source; uint32_t primary_selection_serial; + struct wlr_drag *drag; + uint32_t drag_serial; + struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; struct wlr_seat_touch_state touch_state; @@ -210,6 +213,7 @@ struct wlr_seat { struct wl_signal selection; struct wl_signal primary_selection; + struct wl_signal start_drag; struct wl_signal new_drag_icon; struct wl_signal destroy; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 81a4df0b..e672a2df 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -115,8 +115,7 @@ struct wlr_xwm { xcb_window_t selection_window; struct wlr_xwm_selection clipboard_selection; struct wlr_xwm_selection primary_selection; - - xcb_window_t dnd_window; + struct wlr_xwm_selection dnd_selection; struct wlr_xwayland_surface *focus_surface; @@ -132,6 +131,9 @@ struct wlr_xwm { struct wl_listener compositor_destroy; struct wl_listener seat_selection; struct wl_listener seat_primary_selection; + struct wl_listener seat_start_drag; + struct wl_listener seat_drag_focus; + struct wl_listener seat_drag_destroy; }; struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland); diff --git a/rootston/cursor.c b/rootston/cursor.c index 52439dff..ac597590 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -131,7 +131,8 @@ static void roots_cursor_update_position(struct roots_cursor *cursor, cursor->pointer_view = seat_view; seat_view_deco_motion(seat_view, sx, sy); } - } if (view && surface) { + } + if (view && surface) { // motion over a view surface wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy); diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index 50c94bc5..8129b173 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -427,7 +427,7 @@ static void wlr_drag_set_focus(struct wlr_drag *drag, wlr_seat_client_for_wl_client(drag->seat_client->seat, wl_resource_get_client(surface->resource)); - if (!focus_client || wl_list_empty(&focus_client->data_devices)) { + if (!focus_client) { return; } @@ -436,34 +436,36 @@ static void wlr_drag_set_focus(struct wlr_drag *drag, drag->source->accepted = false; struct wlr_data_offer *offer = wlr_data_source_send_offer(drag->source, focus_client); - if (offer == NULL) { - return; + if (offer != NULL) { + data_offer_update_action(offer); + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { + wl_data_offer_send_source_actions(offer->resource, + drag->source->actions); + } + + offer_resource = offer->resource; } - - data_offer_update_action(offer); - - if (wl_resource_get_version(offer->resource) >= - WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { - wl_data_offer_send_source_actions(offer->resource, - drag->source->actions); - } - - offer_resource = offer->resource; } - uint32_t serial = - wl_display_next_serial(drag->seat_client->seat->display); - struct wl_resource *resource; - wl_resource_for_each(resource, &focus_client->data_devices) { - wl_data_device_send_enter(resource, serial, surface->resource, - wl_fixed_from_double(sx), wl_fixed_from_double(sy), offer_resource); + if (!wl_list_empty(&focus_client->data_devices)) { + uint32_t serial = + wl_display_next_serial(drag->seat_client->seat->display); + struct wl_resource *resource; + wl_resource_for_each(resource, &focus_client->data_devices) { + wl_data_device_send_enter(resource, serial, surface->resource, + wl_fixed_from_double(sx), wl_fixed_from_double(sy), + offer_resource); + } } drag->focus = surface; drag->focus_client = focus_client; drag->seat_client_destroy.notify = handle_drag_seat_client_destroy; - wl_signal_add(&focus_client->events.destroy, - &drag->seat_client_destroy); + wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy); + + wlr_signal_emit_safe(&drag->events.focus, drag); } static void wlr_drag_end(struct wlr_drag *drag) { @@ -488,10 +490,14 @@ static void wlr_drag_end(struct wlr_drag *drag) { wlr_signal_emit_safe(&drag->icon->events.map, drag->icon); } + wlr_signal_emit_safe(&drag->events.destroy, drag); free(drag); } } +const struct +wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface; + static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy) { struct wlr_drag *drag = grab->data; @@ -732,22 +738,26 @@ static bool seat_client_start_drag(struct wlr_seat_client *client, return false; } - drag->seat = client->seat; + wl_signal_init(&drag->events.focus); + wl_signal_init(&drag->events.destroy); + + struct wlr_seat *seat = client->seat; + drag->seat = seat; drag->is_pointer_grab = !wl_list_empty(&client->pointers) && - client->seat->pointer_state.button_count == 1 && - client->seat->pointer_state.grab_serial == serial && - client->seat->pointer_state.focused_surface && - client->seat->pointer_state.focused_surface == origin; + 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(client->seat) == 1 && - client->seat->touch_state.grab_serial == serial; + 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, &client->seat->touch_state.touch_points, link) { + wl_list_for_each(point, &seat->touch_state.touch_points, link) { is_touch_grab = point->surface && point->surface == origin; break; } @@ -785,22 +795,26 @@ static bool seat_client_start_drag(struct wlr_seat_client *client, drag->touch_grab.data = drag; drag->touch_grab.interface = &wlr_data_device_touch_drag_interface; - drag->grab_touch_id = drag->seat->touch_state.grab_id; + drag->grab_touch_id = seat->touch_state.grab_id; drag->keyboard_grab.data = drag; drag->keyboard_grab.interface = &wlr_data_device_keyboard_drag_interface; - wlr_seat_keyboard_start_grab(drag->seat, &drag->keyboard_grab); + wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab); if (drag->is_pointer_grab) { - wlr_seat_pointer_clear_focus(drag->seat); - wlr_seat_pointer_start_grab(drag->seat, &drag->pointer_grab); + wlr_seat_pointer_clear_focus(seat); + wlr_seat_pointer_start_grab(seat, &drag->pointer_grab); } else { assert(point); - wlr_seat_touch_start_grab(drag->seat, &drag->touch_grab); + wlr_seat_touch_start_grab(seat, &drag->touch_grab); wlr_drag_set_focus(drag, point->surface, point->sx, point->sy); } + seat->drag = drag; // TODO: unset this thing somewhere + seat->drag_serial = serial; + wlr_signal_emit_safe(&seat->events.start_drag, drag); + return true; } diff --git a/types/wlr_seat.c b/types/wlr_seat.c index 93f6d872..53659f90 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -465,6 +465,7 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init(&wlr_seat->clients); wl_list_init(&wlr_seat->drag_icons); + wl_signal_init(&wlr_seat->events.start_drag); wl_signal_init(&wlr_seat->events.new_drag_icon); wl_signal_init(&wlr_seat->events.request_set_cursor); diff --git a/xwayland/selection.c b/xwayland/selection.c index a7ed2cf0..8e5905df 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -12,7 +12,7 @@ static const size_t incr_chunk_size = 64 * 1024; -static xcb_atom_t data_device_manager_dnd_action_to_atom( +/*static xcb_atom_t data_device_manager_dnd_action_to_atom( struct wlr_xwm *xwm, enum wl_data_device_manager_dnd_action action) { if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { return xwm->atoms[DND_ACTION_COPY]; @@ -36,7 +36,7 @@ static enum wl_data_device_manager_dnd_action return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; -} +}*/ static void xwm_selection_send_notify(struct wlr_xwm_selection *selection, xcb_atom_t property) { @@ -241,6 +241,81 @@ static void xwm_selection_send_timestamp(struct wlr_xwm_selection *selection) { xwm_selection_send_notify(selection, selection->request.property); } +static xcb_atom_t xwm_get_mime_type_atom(struct wlr_xwm *xwm, char *mime_type) { + if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { + return xwm->atoms[UTF8_STRING]; + } else if (strcmp(mime_type, "text/plain") == 0) { + return xwm->atoms[TEXT]; + } + + xcb_intern_atom_cookie_t cookie = + xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type); + xcb_intern_atom_reply_t *reply = + xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL); + if (reply == NULL) { + return XCB_ATOM_NONE; + } + xcb_atom_t atom = reply->atom; + free(reply); + return atom; +} + +static void xwm_dnd_send_enter(struct wlr_xwm *xwm, struct wlr_drag *drag, + struct wlr_xwayland_surface *dest) { + struct wl_array *mime_types = &drag->source->mime_types; + + xcb_client_message_data_t data = { 0 }; + data.data32[0] = xwm->dnd_selection.window; + data.data32[1] = XDND_VERSION << 24; + + size_t n = mime_types->size / sizeof(char *); + if (n <= 3) { + size_t i = 0; + char **mime_type_ptr; + wl_array_for_each(mime_type_ptr, mime_types) { + char *mime_type = *mime_type_ptr; + data.data32[2+i] = xwm_get_mime_type_atom(xwm, mime_type); + ++i; + } + } else { + // Let the client know that targets are not contained in the message + // data and must be retrieved with the DND_TYPE_LIST property + data.data32[1] |= 1; + + xcb_atom_t targets[n]; + size_t i = 0; + char **mime_type_ptr; + wl_array_for_each(mime_type_ptr, mime_types) { + char *mime_type = *mime_type_ptr; + targets[i] = xwm_get_mime_type_atom(xwm, mime_type); + ++i; + } + + xcb_change_property(xwm->xcb_conn, + XCB_PROP_MODE_REPLACE, + xwm->dnd_selection.window, + xwm->atoms[DND_TYPE_LIST], + XCB_ATOM_ATOM, + 32, // format + n, targets); + } + + xcb_client_message_event_t event = { + .response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .sequence = 0, + .window = dest->window_id, + .type = xwm->atoms[DND_ENTER], + .data = data, + }; + + xcb_send_event(xwm->xcb_conn, + 0, // propagate + dest->window_id, + XCB_EVENT_MASK_NO_EVENT, + (const char *)&event); +} + static struct wl_array *xwm_selection_source_get_mime_types( struct wlr_xwm_selection *selection) { if (selection == &selection->xwm->clipboard_selection) { @@ -271,35 +346,15 @@ static void xwm_selection_send_targets(struct wlr_xwm_selection *selection) { } size_t n = 2 + mime_types->size / sizeof(char *); - xcb_atom_t *targets = malloc(n * sizeof(xcb_atom_t)); - if (targets == NULL) { - return; - } + xcb_atom_t targets[n]; targets[0] = xwm->atoms[TIMESTAMP]; targets[1] = xwm->atoms[TARGETS]; - size_t i = 2; + size_t i = 0; char **mime_type_ptr; wl_array_for_each(mime_type_ptr, mime_types) { char *mime_type = *mime_type_ptr; - xcb_atom_t atom; - if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { - atom = xwm->atoms[UTF8_STRING]; - } else if (strcmp(mime_type, "text/plain") == 0) { - atom = xwm->atoms[TEXT]; - } else { - xcb_intern_atom_cookie_t cookie = - xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type); - xcb_intern_atom_reply_t *reply = - xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL); - if (reply == NULL) { - --n; - continue; - } - atom = reply->atom; - free(reply); - } - targets[i] = atom; + targets[2+i] = xwm_get_mime_type_atom(xwm, mime_type); ++i; } @@ -312,8 +367,6 @@ static void xwm_selection_send_targets(struct wlr_xwm_selection *selection) { n, targets); xwm_selection_send_notify(selection, selection->request.property); - - free(targets); } static struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm, @@ -322,6 +375,8 @@ static struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm, return &xwm->clipboard_selection; } else if (selection_atom == xwm->atoms[PRIMARY]) { return &xwm->primary_selection; + } else if (selection_atom == xwm->atoms[DND_SELECTION]) { + return &xwm->dnd_selection; } else { return NULL; } @@ -856,28 +911,18 @@ void xwm_selection_init(struct wlr_xwm *xwm) { selection_init(xwm, &xwm->primary_selection, xwm->atoms[PRIMARY]); // Drag'n'drop - uint32_t dnd_values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; - xwm->dnd_window = xcb_generate_id(xwm->xcb_conn); - xcb_create_window(xwm->xcb_conn, - XCB_COPY_FROM_PARENT, - xwm->dnd_window, - xwm->screen->root, - 0, 0, - 10, 10, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - xwm->screen->root_visual, - XCB_CW_EVENT_MASK, dnd_values); - uint32_t version = XDND_VERSION; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, - xwm->dnd_window, + xwm->selection_window, xwm->atoms[DND_AWARE], XCB_ATOM_ATOM, 32, 1, &version); + + selection_init(xwm, &xwm->dnd_selection, xwm->atoms[DND_SELECTION]); + wlr_log(L_DEBUG, "DND_SELECTION=%d", xwm->atoms[DND_SELECTION]); } void xwm_selection_finish(struct wlr_xwm *xwm) { @@ -887,9 +932,6 @@ void xwm_selection_finish(struct wlr_xwm *xwm) { if (xwm->selection_window) { xcb_destroy_window(xwm->xcb_conn, xwm->selection_window); } - if (xwm->dnd_window) { - xcb_destroy_window(xwm->xcb_conn, xwm->dnd_window); - } if (xwm->seat) { if (xwm->seat->selection_data_source && xwm->seat->selection_data_source->cancel == data_source_cancel) { @@ -903,7 +945,6 @@ void xwm_selection_finish(struct wlr_xwm *xwm) { } wlr_xwayland_set_seat(xwm->xwayland, NULL); } - } static void xwm_selection_set_owner(struct wlr_xwm_selection *selection, @@ -951,10 +992,50 @@ static void seat_handle_primary_selection(struct wl_listener *listener, xwm_selection_set_owner(&xwm->primary_selection, source != NULL); } +static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = data; + struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_focus); + + // TODO: check for subsurfaces? + bool found = false; + struct wlr_xwayland_surface *surface; + wl_list_for_each(surface, &xwm->surfaces, link) { + if (surface->surface == drag->focus) { + found = true; + break; + } + } + if (!found) { + return; + } + + xwm_dnd_send_enter(xwm, drag, surface); +} + +static void seat_handle_drag_destroy(struct wl_listener *listener, void *data) { + struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_destroy); + + wl_list_remove(&xwm->seat_drag_focus.link); + wl_list_remove(&xwm->seat_drag_destroy.link); +} + +static void seat_handle_start_drag(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = data; + struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_start_drag); + + xwm_selection_set_owner(&xwm->dnd_selection, drag != NULL); + + wl_signal_add(&drag->events.focus, &xwm->seat_drag_focus); + xwm->seat_drag_focus.notify = seat_handle_drag_focus; + wl_signal_add(&drag->events.destroy, &xwm->seat_drag_destroy); + xwm->seat_drag_destroy.notify = seat_handle_drag_destroy; +} + 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_start_drag.link); xwm->seat = NULL; } @@ -968,6 +1049,8 @@ void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { 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.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);