From 0c4e3295b190770da1f346779c18777c9504721b Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 7 Aug 2020 13:54:58 +0200 Subject: [PATCH] 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. --- river/Cursor.zig | 83 +++++++++++++++++++++++++++--------------- river/InputManager.zig | 1 + 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/river/Cursor.zig b/river/Cursor.zig index 2913c7c..ec9a216 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -42,52 +42,66 @@ const ResizeData = struct { const Mode = union(enum) { passthrough: void, + down: *View, move: *View, resize: ResizeData, /// Enter move or resize mode 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)}); - const cur_box = &view.current.box; - self.mode = switch (mode) { + switch (mode) { .passthrough => unreachable, - .move => .{ .move = view }, - .resize => .{ - .resize = .{ - .view = view, - .x_offset = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x), - .y_offset = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y), - }, + .down => self.mode = .{ .down = view }, + .move, .resize => { + const cur_box = &view.current.box; + self.mode = switch (mode) { + .passthrough, .down => unreachable, + .move => .{ .move = view }, + .resize => .{ + .resize = .{ + .view = view, + .x_offset = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x), + .y_offset = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y), + }, + }, + }; + + // Automatically float all views being moved by the pointer + if (!view.current.float) { + view.pending.float = true; + // Start a transaction to apply the pending state of the grabbed + // view and rearrange the layout to fill the hole. + view.output.root.arrange(); + } + + // Clear cursor focus, so that the surface does not receive events + c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat); + + c.wlr_xcursor_manager_set_cursor_image( + self.wlr_xcursor_manager, + if (mode == .move) "move" else "se-resize", + self.wlr_cursor, + ); }, - }; - - // Automatically float all views being moved by the pointer - if (!view.current.float) { - view.pending.float = true; - // Start a transaction to apply the pending state of the grabbed - // view and rearrange the layout to fill the hole. - view.output.root.arrange(); } - - // Clear cursor focus, so that the surface does not receive events - c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat); - - c.wlr_xcursor_manager_set_cursor_image( - self.wlr_xcursor_manager, - if (mode == .move) "move" else "se-resize", - 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 { std.debug.assert(self.mode != .passthrough); 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; 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); 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| { var output_width: 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 => {}, } return; + } else { + Mode.enter(self, .down, event, view); } } } diff --git a/river/InputManager.zig b/river/InputManager.zig index ddcd017..ab6dda4 100644 --- a/river/InputManager.zig +++ b/river/InputManager.zig @@ -108,6 +108,7 @@ pub fn isCursorActionTarget(self: Self, view: *View) bool { const seat = &node.data; switch (seat.cursor.mode) { .passthrough => {}, + .down => |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, }