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

commitce5b8f601d37515aa9ceb194d19a2faf2779a8e1
parent79ffcd2a42
authorIsaac Freund <[email protected]>
date2022-05-31 18:09
river-status: add mode event to seat status

This allows clients such as a status bar to display the currently active
mode.

 build.zig                             |  2 +-
 protocol/river-status-unstable-v1.xml | 12 ++++++++++--
 river/Config.zig                      | 20 ++++++++------------
 river/InputManager.zig                |  7 ++++---
 river/Mode.zig                        |  2 ++
 river/Seat.zig                        | 14 ++++++++++++--
 river/SeatStatus.zig                  |  4 +++-
 river/StatusManager.zig               |  2 +-
 river/command/declare_mode.zig        | 11 +++++++----
 river/command/enter_mode.zig          |  2 +-
 10 files changed, 49 insertions(+), 27 deletions(-)

diff --git a/build.zig b/build.zig
index 4d0017c..fb3a21e 100644
--- a/build.zig
+++ b/build.zig
@@ -112,7 +112,7 @@ pub fn build(b: *zbs.Builder) !void {
     scanner.generate("zwp_pointer_constraints_v1", 1);
 
     scanner.generate("zriver_control_v1", 1);
-    scanner.generate("zriver_status_manager_v1", 2);
+    scanner.generate("zriver_status_manager_v1", 3);
     scanner.generate("river_layout_manager_v3", 1);
 
     scanner.generate("zwlr_layer_shell_v1", 4);
diff --git a/protocol/river-status-unstable-v1.xml b/protocol/river-status-unstable-v1.xml
index 13affaa..6a74256 100644
--- a/protocol/river-status-unstable-v1.xml
+++ b/protocol/river-status-unstable-v1.xml
@@ -16,7 +16,7 @@
     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   </copyright>
 
-  <interface name="zriver_status_manager_v1" version="2">
+  <interface name="zriver_status_manager_v1" version="3">
     <description summary="manage river status objects">
       A global factory for objects that receive status information specific
       to river. It could be used to implement, for example, a status bar.
@@ -85,7 +85,7 @@
     </event>
   </interface>
 
-  <interface name="zriver_seat_status_v1" version="1">
+  <interface name="zriver_seat_status_v1" version="3">
     <description summary="track seat focus">
       This interface allows clients to receive information about the current
       focus of a seat. Note that (un)focused_output events will only be sent
@@ -121,5 +121,13 @@
       </description>
       <arg name="title" type="string" summary="title of the focused view"/>
     </event>
+
+    <event name="mode" since="3">
+      <description summary="the active mode changed">
+        Sent once on binding the interface and again whenever a new mode
+        is entered (e.g. with riverctl enter-mode foobar).
+      </description>
+      <arg name="name" type="string" summary="name of the mode"/>
+    </event>
   </interface>
 </protocol>
diff --git a/river/Config.zig b/river/Config.zig
index 2da717c..9d0fa4c 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -57,7 +57,8 @@ border_color_unfocused: [4]f32 = [_]f32{ 0.34509804, 0.43137255, 0.45882353, 1.0
 border_color_urgent: [4]f32 = [_]f32{ 0.86274510, 0.19607843, 0.18431373, 1.0 }, // Solarized red
 
 /// Map of keymap mode name to mode id
-mode_to_id: std.StringHashMap(usize),
+/// Does not own the string keys. They are owned by the corresponding Mode struct.
+mode_to_id: std.StringHashMap(u32),
 
 /// All user-defined keymap modes, indexed by mode id
 modes: std.ArrayListUnmanaged(Mode),
@@ -98,7 +99,7 @@ cursor_hide_when_typing: HideCursorWhenTypingMode = .disabled,
 
 pub fn init() !Self {
     var self = Self{
-        .mode_to_id = std.StringHashMap(usize).init(util.gpa),
+        .mode_to_id = std.StringHashMap(u32).init(util.gpa),
         .modes = try std.ArrayListUnmanaged(Mode).initCapacity(util.gpa, 2),
     };
     errdefer self.deinit();
@@ -106,27 +107,22 @@ pub fn init() !Self {
     // Start with two empty modes, "normal" and "locked"
     {
         // Normal mode, id 0
-        const owned_slice = try util.gpa.dupe(u8, "normal");
+        const owned_slice = try util.gpa.dupeZ(u8, "normal");
         try self.mode_to_id.putNoClobber(owned_slice, 0);
-        self.modes.appendAssumeCapacity(.{});
+        self.modes.appendAssumeCapacity(.{ .name = owned_slice });
     }
     {
         // Locked mode, id 1
-        const owned_slice = try util.gpa.dupe(u8, "locked");
+        const owned_slice = try util.gpa.dupeZ(u8, "locked");
         try self.mode_to_id.putNoClobber(owned_slice, 1);
-        self.modes.appendAssumeCapacity(.{});
+        self.modes.appendAssumeCapacity(.{ .name = owned_slice });
     }
 
     return self;
 }
 
 pub fn deinit(self: *Self) void {
-    {
-        var it = self.mode_to_id.keyIterator();
-        while (it.next()) |key| util.gpa.free(key.*);
-        self.mode_to_id.deinit();
-    }
-
+    self.mode_to_id.deinit();
     for (self.modes.items) |*mode| mode.deinit();
     self.modes.deinit(util.gpa);
 
diff --git a/river/InputManager.zig b/river/InputManager.zig
index cc159f3..3bcef06 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -188,7 +188,7 @@ fn handleInhibitActivate(
 
         // Enter locked mode
         seat_node.data.prev_mode_id = seat_node.data.mode_id;
-        seat_node.data.mode_id = 1;
+        seat_node.data.enterMode(1);
     }
 
     self.exclusive_client = self.input_inhibit_manager.active_client;
@@ -215,8 +215,9 @@ fn handleInhibitDeactivate(
     // 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;
+        const seat = &seat_node.data;
+        seat.enterMode(seat.prev_mode_id);
+        seat.focus(null);
     }
 
     server.root.startTransaction();
diff --git a/river/Mode.zig b/river/Mode.zig
index 5793c50..cd0c1ab 100644
--- a/river/Mode.zig
+++ b/river/Mode.zig
@@ -23,11 +23,13 @@ const Mapping = @import("Mapping.zig");
 const PointerMapping = @import("PointerMapping.zig");
 const SwitchMapping = @import("SwitchMapping.zig");
 
+name: [:0]const u8,
 mappings: std.ArrayListUnmanaged(Mapping) = .{},
 pointer_mappings: std.ArrayListUnmanaged(PointerMapping) = .{},
 switch_mappings: std.ArrayListUnmanaged(SwitchMapping) = .{},
 
 pub fn deinit(self: *Self) void {
+    util.gpa.free(self.name);
     for (self.mappings.items) |m| m.deinit();
     self.mappings.deinit(util.gpa);
     self.pointer_mappings.deinit(util.gpa);
diff --git a/river/Seat.zig b/river/Seat.zig
index 7fd16d3..9582313 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -63,10 +63,10 @@ keyboards: std.TailQueue(Keyboard) = .{},
 switches: std.TailQueue(Switch) = .{},
 
 /// ID of the current keymap mode
-mode_id: usize = 0,
+mode_id: u32 = 0,
 
 /// ID of previous keymap mode, used when returning from "locked" mode
-prev_mode_id: usize = 0,
+prev_mode_id: u32 = 0,
 
 /// Timer for repeating keyboard mappings
 mapping_repeat_timer: *wl.EventSource,
@@ -358,6 +358,16 @@ pub fn handleViewUnmap(self: *Self, view: *View) void {
     if (self.focused == .view and self.focused.view == view) self.focus(null);
 }
 
+pub fn enterMode(self: *Self, mode_id: u32) void {
+    self.mode_id = mode_id;
+
+    var it = self.status_trackers.first;
+    while (it) |node| : (it = node.next) {
+        const seat_status = node.data.seat_status;
+        seat_status.sendMode(server.config.modes.items[mode_id].name);
+    }
+}
+
 /// Is there a user-defined mapping for passed keycode, modifiers and keyboard state?
 pub fn hasMapping(
     self: *Self,
diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig
index a4adf99..d06c7dd 100644
--- a/river/SeatStatus.zig
+++ b/river/SeatStatus.zig
@@ -21,6 +21,7 @@ const wayland = @import("wayland");
 const wl = wayland.server.wl;
 const zriver = wayland.server.zriver;
 
+const server = &@import("main.zig").server;
 const util = @import("util.zig");
 
 const Seat = @import("Seat.zig");
@@ -35,7 +36,8 @@ pub fn init(self: *Self, seat: *Seat, seat_status: *zriver.SeatStatusV1) void {
 
     seat_status.setHandler(*Self, handleRequest, handleDestroy, self);
 
-    // Send focused output/view once on bind
+    // Send all info once on bind
+    seat_status.sendMode(server.config.modes.items[seat.mode_id].name);
     self.sendOutput(.focused);
     self.sendFocusedView();
 }
diff --git a/river/StatusManager.zig b/river/StatusManager.zig
index 1cf75eb..8239587 100644
--- a/river/StatusManager.zig
+++ b/river/StatusManager.zig
@@ -39,7 +39,7 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
 
 pub fn init(self: *Self) !void {
     self.* = .{
-        .global = try wl.Global.create(server.wl_server, zriver.StatusManagerV1, 2, ?*anyopaque, null, bind),
+        .global = try wl.Global.create(server.wl_server, zriver.StatusManagerV1, 3, ?*anyopaque, null, bind),
     };
 
     server.wl_server.addDestroyListener(&self.server_destroy);
diff --git a/river/command/declare_mode.zig b/river/command/declare_mode.zig
index 8b0d7a6..5b50b77 100644
--- a/river/command/declare_mode.zig
+++ b/river/command/declare_mode.zig
@@ -38,9 +38,12 @@ pub fn declareMode(
 
     if (config.mode_to_id.get(new_mode_name) != null) return;
 
+    try config.mode_to_id.ensureUnusedCapacity(1);
     try config.modes.ensureUnusedCapacity(util.gpa, 1);
-    const owned_name = try util.gpa.dupe(u8, new_mode_name);
-    errdefer util.gpa.free(owned_name);
-    try config.mode_to_id.putNoClobber(owned_name, config.modes.items.len);
-    config.modes.appendAssumeCapacity(.{});
+
+    const owned_name = try util.gpa.dupeZ(u8, new_mode_name);
+
+    const id = @intCast(u32, config.modes.items.len);
+    config.mode_to_id.putAssumeCapacityNoClobber(owned_name, id);
+    config.modes.appendAssumeCapacity(.{ .name = owned_name });
 }
diff --git a/river/command/enter_mode.zig b/river/command/enter_mode.zig
index f5ae8e1..c982e60 100644
--- a/river/command/enter_mode.zig
+++ b/river/command/enter_mode.zig
@@ -59,5 +59,5 @@ pub fn enterMode(
         return Error.Other;
     }
 
-    seat.mode_id = mode_id;
+    seat.enterMode(mode_id);
 }