touch: handle cancel event, fix assertion failure

Currently we can hit an assertion failure in the putNoClobber() call in
response to the down event since we fail to handle the cancel event.

This commit fixes that issue.
This commit is contained in:
Isaac Freund 2024-03-15 19:06:23 +01:00
parent 0e7a692831
commit d64cbd2100
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11

View File

@ -171,12 +171,14 @@ swipe_update: wl.Listener(*wlr.Pointer.event.SwipeUpdate) =
swipe_end: wl.Listener(*wlr.Pointer.event.SwipeEnd) =
wl.Listener(*wlr.Pointer.event.SwipeEnd).init(handleSwipeEnd),
touch_up: wl.Listener(*wlr.Touch.event.Up) =
wl.Listener(*wlr.Touch.event.Up).init(handleTouchUp),
touch_down: wl.Listener(*wlr.Touch.event.Down) =
wl.Listener(*wlr.Touch.event.Down).init(handleTouchDown),
touch_motion: wl.Listener(*wlr.Touch.event.Motion) =
wl.Listener(*wlr.Touch.event.Motion).init(handleTouchMotion),
touch_up: wl.Listener(*wlr.Touch.event.Up) =
wl.Listener(*wlr.Touch.event.Up).init(handleTouchUp),
touch_cancel: wl.Listener(*wlr.Touch.event.Cancel) =
wl.Listener(*wlr.Touch.event.Cancel).init(handleTouchCancel),
touch_frame: wl.Listener(void) = wl.Listener(void).init(handleTouchFrame),
tablet_tool_axis: wl.Listener(*wlr.Tablet.event.Axis) =
@ -228,9 +230,10 @@ pub fn init(cursor: *Cursor, seat: *Seat) !void {
wlr_cursor.events.pinch_end.add(&cursor.pinch_end);
seat.wlr_seat.events.request_set_cursor.add(&cursor.request_set_cursor);
wlr_cursor.events.touch_up.add(&cursor.touch_up);
wlr_cursor.events.touch_down.add(&cursor.touch_down);
wlr_cursor.events.touch_motion.add(&cursor.touch_motion);
wlr_cursor.events.touch_up.add(&cursor.touch_up);
wlr_cursor.events.touch_cancel.add(&cursor.touch_cancel);
wlr_cursor.events.touch_frame.add(&cursor.touch_frame);
wlr_cursor.events.tablet_tool_axis.add(&cursor.tablet_tool_axis);
@ -492,19 +495,6 @@ fn handleSwipeEnd(
);
}
fn handleTouchUp(
listener: *wl.Listener(*wlr.Touch.event.Up),
event: *wlr.Touch.event.Up,
) void {
const cursor = @fieldParentPtr(Cursor, "touch_up", listener);
cursor.seat.handleActivity();
_ = cursor.touch_points.remove(event.touch_id);
cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
}
fn handleTouchDown(
listener: *wl.Listener(*wlr.Touch.event.Down),
event: *wlr.Touch.event.Down,
@ -519,6 +509,7 @@ fn handleTouchDown(
cursor.touch_points.putNoClobber(util.gpa, event.touch_id, .{ .lx = lx, .ly = ly }) catch {
log.err("out of memory", .{});
return;
};
if (server.root.at(lx, ly)) |result| {
@ -548,19 +539,67 @@ fn handleTouchMotion(
cursor.seat.handleActivity();
var lx: f64 = undefined;
var ly: f64 = undefined;
cursor.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &lx, &ly);
cursor.touch_points.put(util.gpa, event.touch_id, .{ .lx = lx, .ly = ly }) catch {
log.err("out of memory", .{});
};
if (cursor.touch_points.getPtr(event.touch_id)) |point| {
cursor.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &point.lx, &point.ly);
cursor.updateDragIcons();
if (server.root.at(lx, ly)) |result| {
if (server.root.at(point.lx, point.ly)) |result| {
cursor.seat.wlr_seat.touchNotifyMotion(event.time_msec, event.touch_id, result.sx, result.sy);
}
}
}
fn handleTouchUp(
listener: *wl.Listener(*wlr.Touch.event.Up),
event: *wlr.Touch.event.Up,
) void {
const cursor = @fieldParentPtr(Cursor, "touch_up", listener);
cursor.seat.handleActivity();
if (cursor.touch_points.remove(event.touch_id)) {
cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
}
}
fn handleTouchCancel(
listener: *wl.Listener(*wlr.Touch.event.Cancel),
_: *wlr.Touch.event.Cancel,
) void {
const cursor = @fieldParentPtr(Cursor, "touch_cancel", listener);
cursor.seat.handleActivity();
cursor.touch_points.clearRetainingCapacity();
// We can't call touchNotifyCancel() from inside the loop over touch points as it also loops
// over touch points and may destroy multiple touch points in a single call.
//
// What we should do here is `while (touch_points.first()) |point| cancel()` but since the
// surface may be null we can't rely on the fact tha all touch points will be destroyed
// and risk an infinite loop if the surface of any wlr_touch_point is null.
//
// This is all really silly and totally unnecessary since all touchNotifyCancel() does with
// the surface argument is obtain a seat client and touch_point.seat_client is never null.
// TODO(wlroots) clean this up after the wlroots MR fixing this is merged:
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4613
// The upper bound of 32 comes from an implementation detail of libinput which uses
// a 32-bit integer as a map to keep track of touch points.
var surfaces: std.BoundedArray(*wlr.Surface, 32) = .{};
{
var it = cursor.seat.wlr_seat.touch_state.touch_points.iterator(.forward);
while (it.next()) |touch_point| {
if (touch_point.surface) |surface| {
surfaces.append(surface) catch break;
}
}
}
for (surfaces.slice()) |surface| {
cursor.seat.wlr_seat.touchNotifyCancel(surface);
}
}
fn handleTouchFrame(listener: *wl.Listener(void)) void {