git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitfc2dea9858b5f574289d8a5e60b060a63ef10b65
parentc0d4185463
authorLeon Henrik Plickat <[email protected]>
date2020-10-19 12:39
Introduce mode "locked"

This mode is automatically entered when the screen is locked.

 doc/riverctl.1.scd           |  4 ++--
 river/Config.zig             | 19 +++++++++++++++----
 river/InputManager.zig       |  9 +++++++--
 river/Seat.zig               |  3 +++
 river/command/enter_mode.zig | 22 +++++++++++++++++++++-
 5 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 8ede532..9a73a88 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -135,8 +135,8 @@ that tag 1 through 9 are visible.
 	When set to _strict_ this is not the case. The focus will be updated on every cursor movement.
 
 *map* [-release] _mode_ _modifiers_ _key_ _command_
-	_mode_ is either “normal” (the default mode) or a mode created with
-	*declare-mode*.
+	_mode_ is either "normal" (the default mode), "locked" (the mode entered when
+	an input inhibitor such as a lock screen is active) or a mode created with *declare-mode*.
 	If _-release_ is specified the mapping is executed on key release rather than key press.
 	_modifiers_ is a list of one or more of the following
 	modifiers separated with a plus sign:
diff --git a/river/Config.zig b/river/Config.zig
index 04b1893..f1ed500 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -89,11 +89,22 @@ pub fn init() !Self {
         .csd_filter = std.ArrayList([]const u8).init(util.gpa),
     };
 
-    // Start with a single, empty mode called normal
     errdefer self.deinit();
-    const owned_slice = try std.mem.dupe(util.gpa, u8, "normal");
-    try self.mode_to_id.putNoClobber(owned_slice, 0);
-    try self.modes.append(Mode.init());
+
+    // Start with two empty modes, "normal" and "locked"
+    try self.modes.ensureCapacity(2);
+    {
+        // Normal mode, id 0
+        const owned_slice = try std.mem.dupe(util.gpa, u8, "normal");
+        try self.mode_to_id.putNoClobber(owned_slice, 0);
+        self.modes.appendAssumeCapacity(Mode.init());
+    }
+    {
+        // Locked mode, id 1
+        const owned_slice = try std.mem.dupe(util.gpa, u8, "locked");
+        try self.mode_to_id.putNoClobber(owned_slice, 1);
+        self.modes.appendAssumeCapacity(Mode.init());
+    }
 
     return self;
 }
diff --git a/river/InputManager.zig b/river/InputManager.zig
index 55de3b2..d618c52 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -110,10 +110,14 @@ fn handleInhibitActivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
 
     log.debug(.input_manager, "input inhibitor activated", .{});
 
-    // Clear focus of all seats
     var seat_it = self.seats.first;
     while (seat_it) |seat_node| : (seat_it = seat_node.next) {
+        // Clear focus of all seats
         seat_node.data.setFocusRaw(.{ .none = {} });
+
+        // Enter locked mode
+        seat_node.data.prev_mode_id = seat_node.data.mode_id;
+        seat_node.data.mode_id = 1;
     }
 
     self.exclusive_client = self.wlr_input_inhibit_manager.active_client;
@@ -134,10 +138,11 @@ fn handleInhibitDeactivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.
     }
 
     // After ensuring that any possible layer surface focus grab has occured,
-    // have each Seat handle focus.
+    // have each Seat handle focus and enter their previous mode.
     var seat_it = self.seats.first;
     while (seat_it) |seat_node| : (seat_it = seat_node.next) {
         seat_node.data.focus(null);
+        seat_node.data.mode_id = seat_node.data.prev_mode_id;
     }
 
     self.server.root.startTransaction();
diff --git a/river/Seat.zig b/river/Seat.zig
index 638954d..c35ab79 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -53,6 +53,9 @@ keyboards: std.TailQueue(Keyboard) = .{},
 /// ID of the current keymap mode
 mode_id: usize = 0,
 
+/// ID of previous keymap mode, used when returning from "locked" mode
+prev_mode_id: usize = 0,
+
 /// Currently focused output, may be the noop output if no
 focused_output: *Output,
 
diff --git a/river/command/enter_mode.zig b/river/command/enter_mode.zig
index c60dcf9..ada18e8 100644
--- a/river/command/enter_mode.zig
+++ b/river/command/enter_mode.zig
@@ -30,9 +30,18 @@ pub fn enterMode(
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
 
+    if (seat.mode_id == 1) {
+        out.* = try std.fmt.allocPrint(
+            allocator,
+            "manually exiting mode 'locked' is not allowed",
+            .{},
+        );
+        return Error.Other;
+    }
+
     const config = seat.input_manager.server.config;
     const target_mode = args[1];
-    seat.mode_id = config.mode_to_id.get(target_mode) orelse {
+    const mode_id = config.mode_to_id.get(target_mode) orelse {
         out.* = try std.fmt.allocPrint(
             allocator,
             "cannot enter non-existant mode '{}'",
@@ -40,4 +49,15 @@ pub fn enterMode(
         );
         return Error.Other;
     };
+
+    if (mode_id == 1) {
+        out.* = try std.fmt.allocPrint(
+            allocator,
+            "manually entering mode 'locked' is not allowed",
+            .{},
+        );
+        return Error.Other;
+    }
+
+    seat.mode_id = mode_id;
 }