xwayland: fix xdg->xwayland drag-and-drop

As struct wlr_drag is destroyed on drop and in the process resets
the focus, a xwayland dnd listener would also reset xwm->drag_focus.

This prevents the xcb replies from being processed and also prevents
the transfer if a compositor would not additionally request new focus
in its wlr_drag destroy handler (which is something usually only done
when in a focus-follows-mouse setting).

This patch creates a new xwm->drop_focus pointer which is a copy of
xwm->drag_focus at drop time. The xcb reply handler and transfer
logic now use the new xwm->drop_focus for their authorization checks.
This commit is contained in:
Consolatis 2024-11-28 04:46:33 +01:00
parent 71943b3b1e
commit 546c5d000d
4 changed files with 31 additions and 4 deletions

View file

@ -128,6 +128,7 @@ struct wlr_xwm {
struct wlr_drag *drag;
struct wlr_xwayland_surface *drag_focus;
struct wlr_xwayland_surface *drop_focus;
const xcb_query_extension_reply_t *xfixes;
const xcb_query_extension_reply_t *xres;
@ -150,6 +151,7 @@ struct wlr_xwm {
struct wl_listener seat_drag_destroy;
struct wl_listener seat_drag_source_destroy;
struct wl_listener drag_focus_destroy;
struct wl_listener drop_focus_destroy;
};
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd);

View file

@ -212,10 +212,10 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm,
bool performed = data->data32[1] & 1;
xcb_atom_t action_atom = data->data32[2];
if (xwm->drag_focus == NULL ||
target_window != xwm->drag_focus->window_id) {
if (xwm->drop_focus == NULL ||
target_window != xwm->drop_focus->window_id) {
wlr_log(WLR_DEBUG, "ignoring XdndFinished client message because "
"it doesn't match the finished drag focus window ID");
"it doesn't match the finished drop focus window ID");
return 1;
}
@ -241,6 +241,13 @@ static void drag_focus_handle_destroy(struct wl_listener *listener, void *data)
xwm_set_drag_focus(xwm, NULL);
}
static void drop_focus_handle_destroy(struct wl_listener *listener, void *data) {
struct wlr_xwm *xwm = wl_container_of(listener, xwm, drop_focus_destroy);
wl_list_remove(&xwm->drop_focus_destroy.link);
wl_list_init(&xwm->drop_focus_destroy.link);
xwm->drop_focus = NULL;
}
static void xwm_set_drag_focus(struct wlr_xwm *xwm, struct wlr_xwayland_surface *focus) {
if (focus == xwm->drag_focus) {
return;
@ -299,6 +306,12 @@ static void seat_handle_drag_drop(struct wl_listener *listener, void *data) {
}
wlr_log(WLR_DEBUG, "Wayland drag dropped over an Xwayland window");
xwm->drop_focus = xwm->drag_focus;
xwm->drop_focus_destroy.notify = drop_focus_handle_destroy;
wl_list_remove(&xwm->drop_focus_destroy.link);
wl_signal_add(&xwm->drop_focus->events.destroy, &xwm->drop_focus_destroy);
xwm_dnd_send_drop(xwm, event->time);
}
@ -329,14 +342,21 @@ static void seat_handle_drag_source_destroy(struct wl_listener *listener,
wl_list_remove(&xwm->drag_focus_destroy.link);
wl_list_init(&xwm->drag_focus_destroy.link);
xwm->drag_focus = NULL;
wl_list_remove(&xwm->drop_focus_destroy.link);
wl_list_init(&xwm->drop_focus_destroy.link);
xwm->drop_focus = NULL;
}
void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) {
wl_list_remove(&xwm->drag_focus_destroy.link);
wl_list_init(&xwm->drag_focus_destroy.link);
wl_list_remove(&xwm->drop_focus_destroy.link);
wl_list_init(&xwm->drop_focus_destroy.link);
xwm->drag = drag;
xwm->drag_focus = NULL;
xwm->drop_focus = NULL;
if (drag != NULL) {
wl_signal_add(&drag->events.focus, &xwm->seat_drag_focus);
@ -357,6 +377,7 @@ void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) {
void xwm_seat_unlink_drag_handlers(struct wlr_xwm *xwm) {
wl_list_remove(&xwm->seat_drag_source_destroy.link);
wl_list_remove(&xwm->drag_focus_destroy.link);
wl_list_remove(&xwm->drop_focus_destroy.link);
if (!xwm->drag) {
return;

View file

@ -410,8 +410,11 @@ void xwm_handle_selection_request(struct wlr_xwm *xwm,
return;
}
bool dnd_allowed = selection == &xwm->dnd_selection
&& (xwm->drag_focus != NULL || xwm->drop_focus != NULL);
// No xwayland surface focused, deny access to clipboard
if (xwm->focus_surface == NULL && xwm->drag_focus == NULL) {
if (xwm->focus_surface == NULL && !dnd_allowed) {
if (wlr_log_get_verbosity() >= WLR_DEBUG) {
char *selection_name = xwm_get_atom_name(xwm, selection->atom);
wlr_log(WLR_DEBUG, "denying read access to selection %u (%s): "

View file

@ -2266,6 +2266,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
wl_list_init(&xwm->pending_startup_ids);
wl_list_init(&xwm->seat_drag_source_destroy.link);
wl_list_init(&xwm->drag_focus_destroy.link);
wl_list_init(&xwm->drop_focus_destroy.link);
xwm->ping_timeout = 10000;