From acb3da7903887a7ff658391c0d68fcda9e434433 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 21 Nov 2025 01:25:19 +0900 Subject: [PATCH] cursor: generalize seat->pressed This commit moduralize seat_{set,reset}_pressed() into cursor_context_save() so that we can also have seat->hovered later. --- include/input/cursor.h | 15 ++++++++ include/labwc.h | 10 +---- src/dnd.c | 2 +- src/input/cursor.c | 86 ++++++++++++++++++++++++++++++++++++------ src/seat.c | 40 -------------------- src/view.c | 4 -- src/xdg.c | 4 +- src/xwayland.c | 4 +- 8 files changed, 97 insertions(+), 68 deletions(-) diff --git a/include/input/cursor.h b/include/input/cursor.h index 13ab72d1..12b96aac 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -38,6 +38,14 @@ struct cursor_context { double sx, sy; }; +/* Used to persistently store cursor context (e.g. in seat->pressed) */ +struct cursor_context_saved { + struct cursor_context ctx; + struct wl_listener view_destroy; + struct wl_listener node_destroy; + struct wl_listener surface_destroy; +}; + /** * get_cursor_context - find view, surface and scene_node at cursor * @@ -65,6 +73,13 @@ void cursor_set(struct seat *seat, enum lab_cursors cursor); void cursor_set_visible(struct seat *seat, bool visible); +/* + * Safely store a cursor context to saved_ctx. saved_ctx is cleared when either + * of its node, surface and view is destroyed. + */ +void cursor_context_save(struct cursor_context_saved *saved_ctx, + const struct cursor_context *ctx); + /** * cursor_get_resize_edges - calculate resize edge based on cursor position * @cursor - the current cursor (usually server->seat.cursor) diff --git a/include/labwc.h b/include/labwc.h index d6fd78ff..999db395 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -65,8 +65,7 @@ struct seat { struct input_method_relay *input_method_relay; /** - * This is usually zeroed and is only set on button press while the - * mouse is over a view or surface, and zeroed on button release. + * Cursor context saved when a mouse button is pressed on a view/surface. * It is used to send cursor motion events to a surface even though * the cursor has left the surface in the meantime. * @@ -76,10 +75,8 @@ struct seat { * It is also used to: * - determine the target view for action in "Drag" mousebind * - validate view move/resize requests from CSD clients - * - * Both (view && !surface) and (surface && !view) are possible. */ - struct cursor_context pressed; + struct cursor_context_saved pressed; struct lab_set bound_buttons; @@ -139,7 +136,6 @@ struct seat { struct wl_list tablet_pads; struct wl_listener constraint_commit; - struct wl_listener pressed_surface_destroy; struct wlr_virtual_pointer_manager_v1 *virtual_pointer; struct wl_listener new_virtual_pointer; @@ -392,8 +388,6 @@ void seat_pointer_end_grab(struct seat *seat, struct wlr_surface *surface); void seat_focus_lock_surface(struct seat *seat, struct wlr_surface *surface); void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer); -void seat_set_pressed(struct seat *seat, struct cursor_context *ctx); -void seat_reset_pressed(struct seat *seat); void seat_output_layout_changed(struct seat *seat); /* diff --git a/src/dnd.c b/src/dnd.c index 8ed7c311..ff65cffa 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -34,7 +34,7 @@ handle_drag_start(struct wl_listener *listener, void *data) struct wlr_drag *drag = data; seat->drag.active = true; - seat_reset_pressed(seat); + cursor_context_save(&seat->pressed, NULL); if (drag->icon) { /* Cleans up automatically on drag->icon->events.destroy */ wlr_scene_drag_icon_create(seat->drag.icons, drag->icon); diff --git a/src/input/cursor.c b/src/input/cursor.c index 500ac08e..5a09bf50 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -437,8 +437,72 @@ cursor_update_image(struct seat *seat) cursor_names[cursor]); } +static void +clear_cursor_context(struct cursor_context_saved *saved_ctx) +{ + if (saved_ctx->node_destroy.notify) { + wl_list_remove(&saved_ctx->node_destroy.link); + } + if (saved_ctx->surface_destroy.notify) { + wl_list_remove(&saved_ctx->surface_destroy.link); + } + if (saved_ctx->view_destroy.notify) { + wl_list_remove(&saved_ctx->view_destroy.link); + } + *saved_ctx = (struct cursor_context_saved) {0}; +} + +static void +handle_ctx_node_destroy(struct wl_listener *listener, void *data) +{ + struct cursor_context_saved *saved_ctx = + wl_container_of(listener, saved_ctx, node_destroy); + clear_cursor_context(saved_ctx); +} + +static void +handle_ctx_surface_destroy(struct wl_listener *listener, void *data) +{ + struct cursor_context_saved *saved_ctx = + wl_container_of(listener, saved_ctx, surface_destroy); + clear_cursor_context(saved_ctx); +} + +static void +handle_ctx_view_destroy(struct wl_listener *listener, void *data) +{ + struct cursor_context_saved *saved_ctx = + wl_container_of(listener, saved_ctx, view_destroy); + clear_cursor_context(saved_ctx); +} + +void +cursor_context_save(struct cursor_context_saved *saved_ctx, + const struct cursor_context *ctx) +{ + assert(saved_ctx); + + clear_cursor_context(saved_ctx); + if (!ctx) { + return; + } + saved_ctx->ctx = *ctx; + if (ctx->node) { + saved_ctx->node_destroy.notify = handle_ctx_node_destroy; + wl_signal_add(&ctx->node->events.destroy, &saved_ctx->node_destroy); + } + if (ctx->surface) { + saved_ctx->surface_destroy.notify = handle_ctx_surface_destroy; + wl_signal_add(&ctx->surface->events.destroy, &saved_ctx->surface_destroy); + } + if (ctx->view) { + saved_ctx->view_destroy.notify = handle_ctx_view_destroy; + wl_signal_add(&ctx->view->events.destroy, &saved_ctx->view_destroy); + } +} + static bool -update_pressed_surface(struct seat *seat, struct cursor_context *ctx) +update_pressed_surface(struct seat *seat, const struct cursor_context *ctx) { /* * In most cases, we don't want to leave one surface and enter @@ -454,10 +518,10 @@ update_pressed_surface(struct seat *seat, struct cursor_context *ctx) if (!wlr_seat_pointer_has_grab(seat->seat)) { return false; } - if (seat->pressed.surface && ctx->surface != seat->pressed.surface) { + if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface) { struct wlr_surface *toplevel = get_toplevel(ctx->surface); - if (toplevel && toplevel == get_toplevel(seat->pressed.surface)) { - seat_set_pressed(seat, ctx); + if (toplevel && toplevel == get_toplevel(seat->pressed.ctx.surface)) { + cursor_context_save(&seat->pressed, ctx); return true; } } @@ -487,7 +551,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, } /* TODO: verify drag_icon logic */ - if (seat->pressed.surface && ctx->surface != seat->pressed.surface + if (seat->pressed.ctx.surface && ctx->surface != seat->pressed.ctx.surface && !update_pressed_surface(seat, ctx) && !seat->drag.active) { if (cursor_has_moved) { @@ -499,7 +563,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, * if the cursor moves outside of the surface. */ int lx, ly; - wlr_scene_node_coords(seat->pressed.node, &lx, &ly); + wlr_scene_node_coords(seat->pressed.ctx.node, &lx, &ly); *sx = server->seat.cursor->x - lx; *sy = server->seat.cursor->y - ly; return true; @@ -597,8 +661,8 @@ cursor_process_motion(struct server *server, uint32_t time, double *sx, double * * moving/resizing the wrong view */ mousebind->pressed_in_context = false; - actions_run(seat->pressed.view, server, - &mousebind->actions, &seat->pressed); + actions_run(seat->pressed.ctx.view, server, + &mousebind->actions, &seat->pressed.ctx); } } @@ -1073,7 +1137,7 @@ cursor_process_button_press(struct seat *seat, uint32_t button, uint32_t time_ms if (ctx.view || ctx.surface) { /* Store cursor context for later action processing */ - seat_set_pressed(seat, &ctx); + cursor_context_save(&seat->pressed, &ctx); } if (server->input_mode == LAB_INPUT_STATE_MENU) { @@ -1138,12 +1202,12 @@ cursor_process_button_release(struct seat *seat, uint32_t button, { struct server *server = seat->server; struct cursor_context ctx = get_cursor_context(server); - struct wlr_surface *pressed_surface = seat->pressed.surface; + struct wlr_surface *pressed_surface = seat->pressed.ctx.surface; /* Always notify button release event when it's not bound */ const bool notify = !lab_set_contains(&seat->bound_buttons, button); - seat_reset_pressed(seat); + cursor_context_save(&seat->pressed, NULL); if (server->input_mode == LAB_INPUT_STATE_MENU) { /* TODO: take into account overflow of time_msec */ diff --git a/src/seat.c b/src/seat.c index 239155f9..a5cdf3ee 100644 --- a/src/seat.c +++ b/src/seat.c @@ -848,46 +848,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) seat->focused_layer = layer; } -static void -pressed_surface_destroy(struct wl_listener *listener, void *data) -{ - struct seat *seat = wl_container_of(listener, seat, - pressed_surface_destroy); - - /* - * Using data directly prevents 'unused variable' - * warning when compiling without asserts - */ - assert(data == seat->pressed.surface); - - seat_reset_pressed(seat); -} - -void -seat_set_pressed(struct seat *seat, struct cursor_context *ctx) -{ - assert(ctx); - assert(ctx->view || ctx->surface); - seat_reset_pressed(seat); - - seat->pressed = *ctx; - - if (ctx->surface) { - seat->pressed_surface_destroy.notify = pressed_surface_destroy; - wl_signal_add(&ctx->surface->events.destroy, - &seat->pressed_surface_destroy); - } -} - -void -seat_reset_pressed(struct seat *seat) -{ - if (seat->pressed.surface) { - wl_list_remove(&seat->pressed_surface_destroy.link); - } - seat->pressed = (struct cursor_context){0}; -} - void seat_output_layout_changed(struct seat *seat) { diff --git a/src/view.c b/src/view.c index 662e85fb..473a1b37 100644 --- a/src/view.c +++ b/src/view.c @@ -2612,10 +2612,6 @@ view_destroy(struct view *view) server->session_lock_manager->last_active_view = NULL; } - if (server->seat.pressed.view == view) { - seat_reset_pressed(&server->seat); - } - if (view->tiled_region_evacuate) { zfree(view->tiled_region_evacuate); } diff --git a/src/xdg.c b/src/xdg.c index f11d522c..2bb6d660 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -400,7 +400,7 @@ handle_request_move(struct wl_listener *listener, void *data) * want. */ struct view *view = wl_container_of(listener, view, request_move); - if (view == view->server->seat.pressed.view) { + if (view == view->server->seat.pressed.ctx.view) { interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); } } @@ -418,7 +418,7 @@ handle_request_resize(struct wl_listener *listener, void *data) */ struct wlr_xdg_toplevel_resize_event *event = data; struct view *view = wl_container_of(listener, view, request_resize); - if (view == view->server->seat.pressed.view) { + if (view == view->server->seat.pressed.ctx.view) { interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges); } } diff --git a/src/xwayland.c b/src/xwayland.c index efd8d8be..91e40897 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -289,7 +289,7 @@ handle_request_move(struct wl_listener *listener, void *data) * want. */ struct view *view = wl_container_of(listener, view, request_move); - if (view == view->server->seat.pressed.view) { + if (view == view->server->seat.pressed.ctx.view) { interactive_begin(view, LAB_INPUT_STATE_MOVE, LAB_EDGE_NONE); } } @@ -307,7 +307,7 @@ handle_request_resize(struct wl_listener *listener, void *data) */ struct wlr_xwayland_resize_event *event = data; struct view *view = wl_container_of(listener, view, request_resize); - if (view == view->server->seat.pressed.view) { + if (view == view->server->seat.pressed.ctx.view) { interactive_begin(view, LAB_INPUT_STATE_RESIZE, event->edges); } }