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

commita3ee38ba6c3a6f276fe1e719ca704e677e8361d5
parent84eed7d3fb
authorIsaac Freund <[email protected]>
date2020-04-12 22:19
Introduce InputManager for eventual multi-seat

 src/cursor.zig        |  7 +++--
 src/input_manager.zig | 43 +++++++++++++++++++++++++++++++
 src/keyboard.zig      |  2 +-
 src/seat.zig          | 71 ++++++++++++++++++++++-----------------------------
 src/server.zig        | 12 +++------
 src/view.zig          |  3 ++-
 6 files changed, 84 insertions(+), 54 deletions(-)

diff --git a/src/cursor.zig b/src/cursor.zig
index e867a17..af41c77 100644
--- a/src/cursor.zig
+++ b/src/cursor.zig
@@ -2,7 +2,6 @@ const std = @import("std");
 const c = @import("c.zig");
 
 const Seat = @import("seat.zig").Seat;
-const Server = @import("server.zig").Server;
 const View = @import("view.zig").View;
 
 const CursorMode = enum {
@@ -52,7 +51,7 @@ pub const Cursor = struct {
         self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, 24) orelse
             return error.CantCreateWlrXCursorManager;
 
-        c.wlr_cursor_attach_output_layout(self.wlr_cursor, seat.server.root.wlr_output_layout);
+        c.wlr_cursor_attach_output_layout(self.wlr_cursor, seat.input_manager.server.root.wlr_output_layout);
         _ = c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1);
 
         self.mode = CursorMode.Passthrough;
@@ -166,7 +165,7 @@ pub const Cursor = struct {
         var sx: f64 = undefined;
         var sy: f64 = undefined;
         var opt_surface: ?*c.wlr_surface = null;
-        const view = self.seat.server.root.viewAt(
+        const view = self.seat.input_manager.server.root.viewAt(
             self.wlr_cursor.x,
             self.wlr_cursor.y,
             &opt_surface,
@@ -260,7 +259,7 @@ pub const Cursor = struct {
         var sy: f64 = undefined;
 
         var surface: ?*c.wlr_surface = null;
-        const view = cursor.seat.server.root.viewAt(
+        const view = cursor.seat.input_manager.server.root.viewAt(
             cursor.wlr_cursor.x,
             cursor.wlr_cursor.y,
             &surface,
diff --git a/src/input_manager.zig b/src/input_manager.zig
new file mode 100644
index 0000000..885680c
--- /dev/null
+++ b/src/input_manager.zig
@@ -0,0 +1,43 @@
+const std = @import("std");
+const c = @import("c.zig");
+
+const Seat = @import("seat.zig").Seat;
+const Server = @import("server.zig").Server;
+
+pub const InputManager = struct {
+    const Self = @This();
+
+    const default_seat = "seat0";
+
+    server: *Server,
+
+    seats: std.TailQueue(Seat),
+
+    listen_new_input: c.wl_listener,
+
+    pub fn init(self: *Self, server: *Server) !void {
+        self.server = server;
+
+        self.seats = std.TailQueue(Seat).init();
+
+        const seat_node = try server.allocator.create(std.TailQueue(Seat).Node);
+        try seat_node.data.init(self, default_seat);
+        self.seats.prepend(seat_node);
+
+        // Set up handler for all new input devices made available. This
+        // includes keyboards, pointers, touch, etc.
+        self.listen_new_input.notify = handleNewInput;
+        c.wl_signal_add(&self.server.wlr_backend.events.new_input, &self.listen_new_input);
+    }
+
+    /// This event is raised by the backend when a new input device becomes available.
+    fn handleNewInput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+        const input_manager = @fieldParentPtr(InputManager, "listen_new_input", listener.?);
+        const device = @ptrCast(*c.wlr_input_device, @alignCast(@alignOf(*c.wlr_input_device), data));
+
+        // TODO: suport multiple seats
+        if (input_manager.seats.first) |seat_node| {
+            seat_node.data.addDevice(device) catch unreachable;
+        }
+    }
+};
diff --git a/src/keyboard.zig b/src/keyboard.zig
index af2786f..7542a29 100644
--- a/src/keyboard.zig
+++ b/src/keyboard.zig
@@ -149,7 +149,7 @@ pub const Keyboard = struct {
     fn handleBuiltinKeybind(self: Self, keysym: c.xkb_keysym_t) bool {
         if (keysym >= c.XKB_KEY_XF86Switch_VT_1 and keysym <= c.XKB_KEY_XF86Switch_VT_12) {
             Log.Debug.log("Switch VT keysym received", .{});
-            const wlr_backend = self.seat.server.wlr_backend;
+            const wlr_backend = self.seat.input_manager.server.wlr_backend;
             if (c.river_wlr_backend_is_multi(wlr_backend)) {
                 if (c.river_wlr_backend_get_session(wlr_backend)) |session| {
                     const vt = keysym - c.XKB_KEY_XF86Switch_VT_1 + 1;
diff --git a/src/seat.zig b/src/seat.zig
index eacdf8d..674f0eb 100644
--- a/src/seat.zig
+++ b/src/seat.zig
@@ -2,39 +2,32 @@ const std = @import("std");
 const c = @import("c.zig");
 
 const Cursor = @import("cursor.zig").Cursor;
+const InputManager = @import("input_manager.zig").InputManager;
 const Keyboard = @import("keyboard.zig").Keyboard;
-const Server = @import("server.zig").Server;
 
-// TODO: InputManager and multi-seat support
 pub const Seat = struct {
     const Self = @This();
 
-    server: *Server,
+    input_manager: *InputManager,
     wlr_seat: *c.wlr_seat,
-    listen_new_input: c.wl_listener,
 
-    // Multiple mice are handled by the same Cursor
+    /// Multiple mice are handled by the same Cursor
     cursor: Cursor,
-    // Mulitple keyboards are handled separately
+
+    /// Mulitple keyboards are handled separately
     keyboards: std.TailQueue(Keyboard),
 
-    pub fn init(self: *Self, server: *Server) !void {
-        self.server = server;
+    pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
+        self.input_manager = input_manager;
 
-        // This seems to be the default seat name used by compositors
         // This will be automatically destroyed when the display is destroyed
-        self.wlr_seat = c.wlr_seat_create(server.wl_display, "seat0") orelse
+        self.wlr_seat = c.wlr_seat_create(input_manager.server.wl_display, name.ptr) orelse
             return error.CantCreateWlrSeat;
 
         try self.cursor.init(self);
         errdefer self.cursor.destroy();
 
         self.keyboards = std.TailQueue(Keyboard).init();
-
-        // Set up handler for all new input devices made available. This
-        // includes keyboards, pointers, touch, etc.
-        self.listen_new_input.notify = handleNewInput;
-        c.wl_signal_add(&self.server.wlr_backend.events.new_input, &self.listen_new_input);
     }
 
     pub fn destroy(self: Self) void {
@@ -44,20 +37,40 @@ pub const Seat = struct {
     /// Handle any user-defined keybinding for the passed keysym and modifiers
     /// Returns true if the key was handled
     pub fn handleKeybinding(self: Self, keysym: c.xkb_keysym_t, modifiers: u32) bool {
-        for (self.server.config.keybinds.items) |keybind| {
+        for (self.input_manager.server.config.keybinds.items) |keybind| {
             if (modifiers == keybind.modifiers and keysym == keybind.keysym) {
                 // Execute the bound command
-                keybind.command(self.server, keybind.arg);
+                keybind.command(self.input_manager.server, keybind.arg);
                 return true;
             }
         }
         return false;
     }
 
+    /// Add a newly created input device to the seat and update the reported
+    /// capabilities.
+    pub fn addDevice(self: *Self, device: *c.wlr_input_device) !void {
+        switch (device.type) {
+            .WLR_INPUT_DEVICE_KEYBOARD => self.addKeyboard(device) catch unreachable,
+            .WLR_INPUT_DEVICE_POINTER => self.addPointer(device),
+            else => {},
+        }
+
+        // We need to let the wlr_seat know what our capabilities are, which is
+        // communiciated to the client. We always have a cursor, even if
+        // there are no pointer devices, so we always include that capability.
+        var caps: u32 = @intCast(u32, c.WL_SEAT_CAPABILITY_POINTER);
+        // if list not empty
+        if (self.keyboards.len > 0) {
+            caps |= @intCast(u32, c.WL_SEAT_CAPABILITY_KEYBOARD);
+        }
+        c.wlr_seat_set_capabilities(self.wlr_seat, caps);
+    }
+
     fn addKeyboard(self: *Self, device: *c.wlr_input_device) !void {
         c.wlr_seat_set_keyboard(self.wlr_seat, device);
 
-        const node = try self.keyboards.allocateNode(self.server.allocator);
+        const node = try self.keyboards.allocateNode(self.input_manager.server.allocator);
         try node.data.init(self, device);
         self.keyboards.append(node);
     }
@@ -69,26 +82,4 @@ pub const Seat = struct {
         // acceleration, etc.
         c.wlr_cursor_attach_input_device(self.cursor.wlr_cursor, device);
     }
-
-    fn handleNewInput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
-        // This event is raised by the backend when a new input device becomes available.
-        const seat = @fieldParentPtr(Seat, "listen_new_input", listener.?);
-        const device = @ptrCast(*c.wlr_input_device, @alignCast(@alignOf(*c.wlr_input_device), data));
-
-        switch (device.type) {
-            .WLR_INPUT_DEVICE_KEYBOARD => seat.addKeyboard(device) catch unreachable,
-            .WLR_INPUT_DEVICE_POINTER => seat.addPointer(device),
-            else => {},
-        }
-
-        // We need to let the wlr_seat know what our capabilities are, which is
-        // communiciated to the client. In TinyWL we always have a cursor, even if
-        // there are no pointer devices, so we always include that capability.
-        var caps: u32 = @intCast(u32, c.WL_SEAT_CAPABILITY_POINTER);
-        // if list not empty
-        if (seat.keyboards.len > 0) {
-            caps |= @intCast(u32, c.WL_SEAT_CAPABILITY_KEYBOARD);
-        }
-        c.wlr_seat_set_capabilities(seat.wlr_seat, caps);
-    }
 };
diff --git a/src/server.zig b/src/server.zig
index 902eb54..4e42485 100644
--- a/src/server.zig
+++ b/src/server.zig
@@ -1,13 +1,12 @@
 const std = @import("std");
 const c = @import("c.zig");
-const command = @import("command.zig");
 
 const Config = @import("config.zig").Config;
 const DecorationManager = @import("decoration_manager.zig").DecorationManager;
+const InputManager = @import("input_manager.zig").InputManager;
 const Log = @import("log.zig").Log;
 const Output = @import("output.zig").Output;
 const Root = @import("root.zig").Root;
-const Seat = @import("seat.zig").Seat;
 const View = @import("view.zig").View;
 const ViewStack = @import("view_stack.zig").ViewStack;
 
@@ -25,9 +24,8 @@ pub const Server = struct {
     wlr_layer_shell: *c.wlr_layer_shell_v1,
 
     decoration_manager: DecorationManager,
+    input_manager: InputManager,
     root: Root,
-    seat: Seat,
-
     config: Config,
 
     listen_new_output: c.wl_listener,
@@ -71,11 +69,9 @@ pub const Server = struct {
             return error.CantCreateWlrLayerShell;
 
         try self.decoration_manager.init(self);
-
         try self.root.init(self);
-
-        try self.seat.init(self);
-
+        // Must be called after root is initialized
+        try self.input_manager.init(self);
         try self.config.init(self.allocator);
 
         // These all free themselves when the wl_display is destroyed
diff --git a/src/view.zig b/src/view.zig
index 9ff03ec..824f98a 100644
--- a/src/view.zig
+++ b/src/view.zig
@@ -182,7 +182,8 @@ pub const View = struct {
 
     fn focus(self: *Self, surface: *c.wlr_surface) void {
         const root = self.output.root;
-        const wlr_seat = root.server.seat.wlr_seat;
+        // TODO: remove this hack
+        const wlr_seat = root.server.input_manager.seats.first.?.data.wlr_seat;
         const prev_surface = wlr_seat.keyboard_state.focused_surface;
 
         if (prev_surface == surface) {