cursor: implement implicit grabs

When a button is held down and the cursor leaves a surface, events now
continue to be sent to the client. This allows e.g. dragging a scroll
bar from outside the surface.
This commit is contained in:
Isaac Freund 2020-08-07 13:54:58 +02:00
parent 91052f7477
commit 0c4e3295b1
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
2 changed files with 55 additions and 29 deletions

View File

@ -42,18 +42,21 @@ const ResizeData = struct {
const Mode = union(enum) { const Mode = union(enum) {
passthrough: void, passthrough: void,
down: *View,
move: *View, move: *View,
resize: ResizeData, resize: ResizeData,
/// Enter move or resize mode /// Enter move or resize mode
fn enter(self: *Self, mode: @TagType(Mode), event: *c.wlr_event_pointer_button, view: *View) void { fn enter(self: *Self, mode: @TagType(Mode), event: *c.wlr_event_pointer_button, view: *View) void {
std.debug.assert(self.mode == .passthrough);
log.debug(.cursor, "enter {} mode", .{@tagName(mode)}); log.debug(.cursor, "enter {} mode", .{@tagName(mode)});
switch (mode) {
.passthrough => unreachable,
.down => self.mode = .{ .down = view },
.move, .resize => {
const cur_box = &view.current.box; const cur_box = &view.current.box;
self.mode = switch (mode) { self.mode = switch (mode) {
.passthrough => unreachable, .passthrough, .down => unreachable,
.move => .{ .move = view }, .move => .{ .move = view },
.resize => .{ .resize => .{
.resize = .{ .resize = .{
@ -80,14 +83,25 @@ const Mode = union(enum) {
if (mode == .move) "move" else "se-resize", if (mode == .move) "move" else "se-resize",
self.wlr_cursor, self.wlr_cursor,
); );
},
}
} }
/// Return from move/resize to passthrough /// Return from down/move/resize to passthrough
fn leave(self: *Self, event: *c.wlr_event_pointer_button) void { fn leave(self: *Self, event: *c.wlr_event_pointer_button) void {
std.debug.assert(self.mode != .passthrough); std.debug.assert(self.mode != .passthrough);
log.debug(.cursor, "leave {} mode", .{@tagName(self.mode)}); log.debug(.cursor, "leave {} mode", .{@tagName(self.mode)});
// If we were in down mode, we need pass along the release event
if (self.mode == .down)
_ = c.wlr_seat_pointer_notify_button(
self.seat.wlr_seat,
event.time_msec,
event.button,
event.state,
);
self.mode = .passthrough; self.mode = .passthrough;
passthrough(self, event.time_msec); passthrough(self, event.time_msec);
} }
@ -100,6 +114,15 @@ const Mode = union(enum) {
c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y); c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
passthrough(self, time); passthrough(self, time);
}, },
.down => |view| {
c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
c.wlr_seat_pointer_notify_motion(
self.seat.wlr_seat,
time,
self.wlr_cursor.x - @intToFloat(f64, view.current.box.x),
self.wlr_cursor.y - @intToFloat(f64, view.current.box.y),
);
},
.move => |view| { .move => |view| {
var output_width: c_int = undefined; var output_width: c_int = undefined;
var output_height: c_int = undefined; var output_height: c_int = undefined;
@ -364,6 +387,8 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
else => {}, else => {},
} }
return; return;
} else {
Mode.enter(self, .down, event, view);
} }
} }
} }

View File

@ -108,6 +108,7 @@ pub fn isCursorActionTarget(self: Self, view: *View) bool {
const seat = &node.data; const seat = &node.data;
switch (seat.cursor.mode) { switch (seat.cursor.mode) {
.passthrough => {}, .passthrough => {},
.down => |target_view| if (target_view == view) break true,
.move => |target_view| if (target_view == view) break true, .move => |target_view| if (target_view == view) break true,
.resize => |data| if (data.view == view) break true, .resize => |data| if (data.view == view) break true,
} }