From 3076380acacb1d460a70e2d5209b5eb54539e3ec Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 22 Sep 2022 04:47:03 +0200 Subject: [PATCH] cursor: Prevent application crash on drag-and-drop Wlroots resets the focused surface on Drag and Drop. Before this patch we tried to enforce a client side cursor image update by forcefully clearing the focus which, even though `wlr_seat->pointer_state.focused_surface` was `NULL`, was sent to the drag source. After the clear we are sending an enter event for the surface under the cursor and if this surface was the drag source itself it was heavily confused. This caused all kinds of weird behavior from spawning new drag icons constantly to outright crashing the application. Reported-by: @Flrian Bisected-by: @Flrian --- src/cursor.c | 72 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/cursor.c b/src/cursor.c index 077c2853..6f9f398f 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -345,7 +345,6 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, return; } - /* TODO: verify drag_icon logic */ if (seat->pressed.surface && ctx->surface != seat->pressed.surface && !update_pressed_surface(seat, ctx) && !seat->drag_icon) { @@ -362,36 +361,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, return; } - if (ctx->surface && !input_inhibit_blocks_surface(seat, - ctx->surface->resource)) { - /* - * Cursor is over an input-enabled client surface. The - * cursor image will be set by request_cursor_notify() - * in response to the enter event. - */ - if (ctx->surface != wlr_seat->pointer_state.focused_surface - || seat->server_cursor != LAB_CURSOR_CLIENT) { - /* - * Enter the surface if necessary. Usually we - * prevent re-entering an already focused - * surface, because the extra leave and enter - * events can confuse clients (e.g. break - * double-click detection). - * - * We do however send a leave/enter event pair - * if a server-side cursor was set and we need - * to trigger a cursor image update. - */ - wlr_seat_pointer_notify_clear_focus(wlr_seat); - wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface, - ctx->sx, ctx->sy); - seat->server_cursor = LAB_CURSOR_CLIENT; - } - if (cursor_has_moved) { - wlr_seat_pointer_notify_motion(wlr_seat, time_msec, - ctx->sx, ctx->sy); - } - } else { + if (!ctx->surface) { /* * Cursor is over a server (labwc) surface. Clear focus * from the focused client (if any, no-op otherwise) and @@ -402,6 +372,46 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, if (!seat->drag_icon) { cursor_set(seat, cursor_get_from_ssd(ctx->type)); } + } else if (input_inhibit_blocks_surface(seat, ctx->surface->resource)) { + /* + * Cursor is over an inhibited surface + * + * TODO: we might want to check for locked surface as well here + */ + return; + } else { + /* + * Cursor is over an input-enabled client surface. The + * cursor image will be set by request_cursor_notify() + * in response to the enter event. + */ + + if (ctx->surface == wlr_seat->pointer_state.focused_surface + && seat->server_cursor != LAB_CURSOR_CLIENT) { + /* + * Leave and enter the surface if necessary. + * Usually we prevent re-entering an already + * focused surface, because the extra leave + * and enter events can confuse or even crash + * clients (e.g. break double-click detection, + * crash on touchpad scrolling or drag-and-drop). + * + * We do however send a leave/enter event pair + * if a server-side cursor was set and we need + * to trigger a client cursor image update. + */ + wlr_seat_pointer_notify_clear_focus(wlr_seat); + } + + /* Enter the surface if necessary, no-op otherwise */ + wlr_seat_pointer_notify_enter( + wlr_seat, ctx->surface, ctx->sx, ctx->sy); + seat->server_cursor = LAB_CURSOR_CLIENT; + + if (cursor_has_moved) { + wlr_seat_pointer_notify_motion( + wlr_seat, time_msec, ctx->sx, ctx->sy); + } } }