diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 6a38190b4..318dac9e1 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -22,6 +22,15 @@ struct sway_cursor { double x, y; struct sway_node *node; } previous; + + // Not to be confused with `previous` above; they store entirely unrelated + // data. + struct { + double sx, sy; + struct sway_node *node; + struct wlr_surface *surface; + } current; + struct wlr_xcursor_manager *xcursor_manager; struct wl_list tablets; struct wl_list tablet_pads; @@ -79,10 +88,6 @@ struct sway_cursor { struct sway_node; -struct sway_node *node_at_coords( - struct sway_seat *seat, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - void sway_cursor_destroy(struct sway_cursor *cursor); struct sway_cursor *sway_cursor_create(struct sway_seat *seat); diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 4c67b3ce7..620a71bf0 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -618,11 +618,7 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) struct sway_container *con = NULL; if (binding->type == BINDING_MOUSESYM || binding->type == BINDING_MOUSECODE) { - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - seat->cursor->cursor->x, seat->cursor->cursor->y, - &surface, &sx, &sy); + struct sway_node *node = seat->cursor->current.node; if (node && node->type == N_CONTAINER) { con = node->sway_container; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index cbb5c6e97..cb14474f1 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -74,7 +74,7 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). */ -struct sway_node *node_at_coords( +static struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first @@ -340,6 +340,50 @@ void cursor_unhide(struct sway_cursor *cursor) { wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); } +static bool cursor_motion_is_constrained(struct sway_cursor *cursor, + enum wlr_input_device_type type) { + // Only apply pointer constraints to real pointer input. + return cursor->active_constraint && type == WLR_INPUT_DEVICE_POINTER; +} + +static void cursor_update_current_node(struct sway_cursor *cursor, + enum wlr_input_device_type type) { + struct sway_node *node = NULL; + struct wlr_surface *surface = NULL; + double sx = 0, sy = 0; + + if (cursor_motion_is_constrained(cursor, type)) { + struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; + struct sway_view *view = view_from_wlr_surface(constraint->surface); + + if (view) { + struct sway_container *con = view->container; + sx = cursor->cursor->x - con->pending.content_x + view->geometry.x; + sy = cursor->cursor->y - con->pending.content_y + view->geometry.y; + + if (pixman_region32_contains_point(&constraint->region, floor(sx), + floor(sy), NULL)) { + node = &view->container->node; + surface = constraint->surface; + } + } + } + + // This is subtle: this means that if an actively constrained surface is + // partially occluded by another node (say, by a floating node), the cursor + // shall remain constrained and pass "under" the floating node. Need to + // check both `node` and `surface` since neither implies the other. + if (!node && !surface) { + node = node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, + &surface, &sx, &sy); + } + + cursor->current.sx = sx; + cursor->current.sy = sy; + cursor->current.node = node; + cursor->current.surface = surface; +} + static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { @@ -348,17 +392,12 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, dx, dy, dx_unaccel, dy_unaccel); - // Only apply pointer constraints to real pointer input. - if (cursor->active_constraint && device->type == WLR_INPUT_DEVICE_POINTER) { - struct wlr_surface *surface = NULL; - double sx, sy; - node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_update_current_node(cursor, device->type); - if (cursor->active_constraint->surface != surface) { - return; - } + if (cursor_motion_is_constrained(cursor, device->type)) { + assert(cursor->active_constraint->surface == cursor->current.surface); + double sx = cursor->current.sx, sy = cursor->current.sy; double sx_confined, sy_confined; if (!wlr_region_confine(&cursor->confine, sx, sy, sx + dx, sy + dy, &sx_confined, &sy_confined)) { @@ -370,6 +409,7 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } wlr_cursor_move(cursor->cursor, device, dx, dy); + cursor_update_current_node(cursor, device->type); seatop_pointer_motion(cursor->seat, time_msec); } @@ -608,10 +648,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, break; } - double sx, sy; - struct wlr_surface *surface = NULL; + struct wlr_surface *surface = cursor->current.surface; struct sway_seat *seat = cursor->seat; - node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); // The logic for whether we should send a tablet event or an emulated pointer // event is tricky. It comes down to: @@ -626,6 +664,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, if (!cursor->simulating_pointer_from_tool_tip && ((surface && wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) || wlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) { + cursor_update_current_node(cursor, input_device->wlr_device->type); seatop_tablet_tool_motion(seat, tool, time_msec); } else { wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); @@ -839,8 +878,11 @@ static void check_constraint_region(struct sway_cursor *cursor) { sx + con->pending.content_x - view->geometry.x, sy + con->pending.content_y - view->geometry.y); + cursor_update_current_node(cursor, WLR_INPUT_DEVICE_POINTER); cursor_rebase(cursor); } + } else { + cursor_update_current_node(cursor, WLR_INPUT_DEVICE_POINTER); } } @@ -1279,6 +1321,7 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { // Warp the pointer as well, so that on the next pointer rebase we don't // send an unexpected synthetic motion event to clients. wlr_seat_pointer_warp(constraint->seat, sx, sy); + cursor_update_current_node(cursor, WLR_INPUT_DEVICE_POINTER); } } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index f9eb8c8ad..fe40dd80a 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -213,10 +213,9 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, } struct sway_cursor *cursor = seat->cursor; - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct wlr_surface *surface = cursor->current.surface; + double sx = cursor->current.sx, sy = cursor->current.sy; + struct sway_node *node = cursor->current.node; if (!sway_assert(surface, "Expected null-surface tablet input to route through pointer emulation")) { @@ -328,10 +327,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct sway_cursor *cursor = seat->cursor; // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct wlr_surface *surface = cursor->current.surface; + double sx = cursor->current.sx, sy = cursor->current.sy; + struct sway_node *node = cursor->current.node; struct sway_container *cont = node && node->type == N_CONTAINER ? node->sway_container : NULL; @@ -574,10 +572,9 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_default_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct wlr_surface *surface = cursor->current.surface; + double sx = cursor->current.sx, sy = cursor->current.sy; + struct sway_node *node = cursor->current.node; if (config->focus_follows_mouse != FOLLOWS_NO) { check_focus_follows_mouse(seat, e, node); @@ -608,10 +605,9 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, struct seatop_default_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct wlr_surface *surface = cursor->current.surface; + double sx = cursor->current.sx, sy = cursor->current.sy; + struct sway_node *node = cursor->current.node; if (config->focus_follows_mouse != FOLLOWS_NO) { check_focus_follows_mouse(seat, e, node); @@ -664,10 +660,8 @@ static void handle_pointer_axis(struct sway_seat *seat, struct seatop_default_event *e = seat->seatop_data; // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct wlr_surface *surface = cursor->current.surface; + struct sway_node *node = cursor->current.node; struct sway_container *cont = node && node->type == N_CONTAINER ? node->sway_container : NULL; enum wlr_edges edge = cont ? find_edge(cont, surface, cursor) : WLR_EDGE_NONE; @@ -756,8 +750,7 @@ static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) { struct sway_cursor *cursor = seat->cursor; struct wlr_surface *surface = NULL; double sx = 0.0, sy = 0.0; - e->previous_node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + e->previous_node = cursor->current.node; if (surface) { if (seat_is_input_allowed(seat, surface)) { diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 446612c6e..cbb4fa44e 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -94,11 +94,8 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, static void handle_motion_postthreshold(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; - struct wlr_surface *surface = NULL; - double sx, sy; struct sway_cursor *cursor = seat->cursor; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_node *node = cursor->current.node; // Damage the old location desktop_damage_box(&e->drop_box);