mirror of
https://github.com/swaywm/sway.git
synced 2026-04-23 06:46:27 -04:00
input: stash current node and surface in cursor
If an actively constrained surface is partially occluded by another node (say, by a floating node), the cursor should remain constrained and pass "under" the floating node. Previously, this did not always happen, as `node_at_coords` is a simple top-down hit test that doesn't (and shouldn't) cover constraints. Furthermore, whether the cursor should remain constrained is a function of the input device type: only pointer devices should be constrained. This commit addresses this issue by stashing the surface and node that currently have cursor input focus, and using the stashed values everywhere. When `wlr_cursor_move` and friends are called, one must now take care to also call `cursor_update_current_node` with the `wlr_input_device` that initiated the movement. Fixes #6073.
This commit is contained in:
parent
1afedcb94c
commit
bfc22725b8
5 changed files with 82 additions and 48 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue