diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index cefcc979..cd266795 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -107,10 +107,17 @@ struct wlr_drag { struct { struct wl_signal focus; + struct wl_signal motion; struct wl_signal destroy; } events; }; +struct wlr_drag_motion_event { + struct wlr_drag *drag; + uint32_t time; + double sx, sy; +}; + /** * Create a wl data device manager global for this display. */ diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index e672a2df..d4795d3f 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -122,6 +122,9 @@ struct wlr_xwm { struct wl_list surfaces; // wlr_xwayland_surface::link struct wl_list unpaired_surfaces; // wlr_xwayland_surface::unpaired_link + struct wlr_drag *drag; + struct wlr_xwayland_surface *drag_focus; + const xcb_query_extension_reply_t *xfixes; #ifdef WLR_HAS_XCB_ERRORS xcb_errors_context_t *errors_context; @@ -133,6 +136,7 @@ struct wlr_xwm { struct wl_listener seat_primary_selection; struct wl_listener seat_start_drag; struct wl_listener seat_drag_focus; + struct wl_listener seat_drag_motion; struct wl_listener seat_drag_destroy; }; diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index 8129b173..beb13eec 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -495,9 +495,6 @@ static void wlr_drag_end(struct wlr_drag *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; @@ -507,12 +504,20 @@ static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab, static void pointer_drag_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { struct wlr_drag *drag = grab->data; - if (drag->focus != NULL&& drag->focus_client != NULL) { + if (drag->focus != NULL && drag->focus_client != NULL) { struct wl_resource *resource; wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_motion(resource, time, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); } + + struct wlr_drag_motion_event event = { + .drag = drag, + .time = time, + .sx = sx, + .sy = sy, + }; + wlr_signal_emit_safe(&drag->events.motion, &event); } } @@ -739,6 +744,7 @@ static bool seat_client_start_drag(struct wlr_seat_client *client, } wl_signal_init(&drag->events.focus); + wl_signal_init(&drag->events.motion); wl_signal_init(&drag->events.destroy); struct wlr_seat *seat = client->seat; diff --git a/xwayland/selection.c b/xwayland/selection.c index 8e5905df..d1dfbdaa 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]; @@ -24,7 +24,7 @@ static const size_t incr_chunk_size = 64 * 1024; return XCB_ATOM_NONE; } -static enum wl_data_device_manager_dnd_action +/*static enum wl_data_device_manager_dnd_action data_device_manager_dnd_action_from_atom(struct wlr_xwm *xwm, enum atom_name atom) { if (atom == xwm->atoms[DND_ACTION_COPY] || @@ -183,7 +183,8 @@ error_out: static void xwm_selection_source_send(struct wlr_xwm_selection *selection, const char *mime_type, int32_t fd) { - if (selection == &selection->xwm->clipboard_selection) { + if (selection == &selection->xwm->clipboard_selection || + selection == &selection->xwm->dnd_selection) { struct wlr_data_source *source = selection->xwm->seat->selection_data_source; if (source != NULL) { @@ -260,8 +261,9 @@ static xcb_atom_t xwm_get_mime_type_atom(struct wlr_xwm *xwm, char *mime_type) { return atom; } -static void xwm_dnd_send_enter(struct wlr_xwm *xwm, struct wlr_drag *drag, - struct wlr_xwayland_surface *dest) { +static void xwm_dnd_send_enter(struct wlr_xwm *xwm) { + struct wlr_drag *drag = xwm->drag; + struct wlr_xwayland_surface *dest = xwm->drag_focus; struct wl_array *mime_types = &drag->source->mime_types; xcb_client_message_data_t data = { 0 }; @@ -316,9 +318,38 @@ static void xwm_dnd_send_enter(struct wlr_xwm *xwm, struct wlr_drag *drag, (const char *)&event); } +static void xwm_dnd_send_position(struct wlr_xwm *xwm, uint32_t time, int16_t x, + int16_t y) { + struct wlr_drag *drag = xwm->drag; + struct wlr_xwayland_surface *dest = xwm->drag_focus; + + xcb_client_message_data_t data = { 0 }; + data.data32[0] = xwm->dnd_selection.window; + data.data32[2] = (x << 16) | y; + data.data32[3] = time; + data.data32[4] = + data_device_manager_dnd_action_to_atom(xwm, drag->source->actions); + + xcb_client_message_event_t event = { + .response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .sequence = 0, + .window = dest->window_id, + .type = xwm->atoms[DND_POSITION], + .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) { + if (selection == &selection->xwm->clipboard_selection || + selection == &selection->xwm->dnd_selection) { struct wlr_data_source *source = selection->xwm->seat->selection_data_source; if (source != NULL) { @@ -414,7 +445,7 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, selection->flush_property_on_delete = 0; // No xwayland surface focused, deny access to clipboard - if (xwm->focus_surface == NULL) { + if (xwm->focus_surface == NULL && xwm->drag_focus == NULL) { wlr_log(L_DEBUG, "denying read access to clipboard: " "no xwayland surface focused"); xwm_selection_send_notify(selection, XCB_ATOM_NONE); @@ -714,7 +745,8 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { // set the wayland selection to the X11 selection struct wlr_xwm *xwm = selection->xwm; - if (selection == &xwm->clipboard_selection) { + if (selection == &xwm->clipboard_selection || + selection == &xwm->dnd_selection) { struct x11_data_source *source = calloc(1, sizeof(struct x11_data_source)); if (source == NULL) { @@ -725,6 +757,8 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { source->base.send = data_source_send; source->base.cancel = data_source_cancel; + // TODO: DND + source->selection = selection; wl_array_init(&source->mime_types_atoms); @@ -816,6 +850,11 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, } else if (selection == &xwm->primary_selection) { wlr_seat_set_primary_selection(xwm->seat, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); + } else if (selection == &xwm->dnd_selection) { + // TODO: DND + } else { + wlr_log(L_DEBUG, "X11 selection has been cleared, but cannot " + "clear Wayland selection"); } } @@ -1009,7 +1048,21 @@ static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { return; } - xwm_dnd_send_enter(xwm, drag, surface); + xwm->drag_focus = surface; + xwm_dnd_send_enter(xwm); +} + +static void seat_handle_drag_motion(struct wl_listener *listener, void *data) { + struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_motion); + struct wlr_drag_motion_event *event = data; + struct wlr_xwayland_surface *surface = xwm->drag_focus; + + if (surface == NULL) { + return; // No xwayland surface focused + } + + xwm_dnd_send_position(xwm, event->time, surface->x + (int16_t)event->sx, + surface->y + (int16_t)event->sy); } static void seat_handle_drag_destroy(struct wl_listener *listener, void *data) { @@ -1017,6 +1070,7 @@ static void seat_handle_drag_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwm->seat_drag_focus.link); wl_list_remove(&xwm->seat_drag_destroy.link); + xwm->drag = NULL; } static void seat_handle_start_drag(struct wl_listener *listener, void *data) { @@ -1024,11 +1078,16 @@ static void seat_handle_start_drag(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_start_drag); xwm_selection_set_owner(&xwm->dnd_selection, drag != NULL); + xwm->drag = drag; - 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; + if (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.motion, &xwm->seat_drag_motion); + xwm->seat_drag_motion.notify = seat_handle_drag_motion; + 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) {