diff --git a/src/command.zig b/src/command.zig index 9c100f4..a325fe9 100644 --- a/src/command.zig +++ b/src/command.zig @@ -4,23 +4,33 @@ const c = @import("c.zig"); const Server = @import("server.zig").Server; const ViewStack = @import("view_stack.zig").ViewStack; +pub const Arg = union { + int: i32, + uint: u32, + float: f64, + none: void, +}; + +pub const Command = fn (server: *Server, arg: Arg) void; + /// Exit the compositor, terminating the wayland session. -pub fn exitCompositor(server: *Server) void { +pub fn exitCompositor(server: *Server, arg: Arg) void { c.wl_display_terminate(server.wl_display); } /// Shift focus to the next visible view, wrapping if needed. -pub fn focusNextView(server: *Server) void { +pub fn focusNextView(server: *Server, arg: Arg) void { server.root.focusNextView(); } /// Shift focus to the previous visible view, wrapping if needed. -pub fn focusPrevView(server: *Server) void { +pub fn focusPrevView(server: *Server, arg: Arg) void { server.root.focusPrevView(); } /// Modify the number of master views -pub fn modifyMasterCount(server: *Server, delta: i32) void { +pub fn modifyMasterCount(server: *Server, arg: Arg) void { + const delta = arg.int; server.root.master_count = @intCast(u32, std.math.max( 0, @intCast(i32, server.root.master_count) + delta, @@ -29,7 +39,8 @@ pub fn modifyMasterCount(server: *Server, delta: i32) void { } /// Modify the percent of the width of the screen that the master views occupy. -pub fn modifyMasterFactor(server: *Server, delta: f64) void { +pub fn modifyMasterFactor(server: *Server, arg: Arg) void { + const delta = arg.float; const new_master_factor = std.math.min( std.math.max(server.root.master_factor + delta, 0.05), 0.95, @@ -42,7 +53,7 @@ pub fn modifyMasterFactor(server: *Server, delta: f64) void { /// Bump the focused view to the top of the stack. /// TODO: if the top of the stack is focused, bump the next visible view. -pub fn zoom(server: *Server) void { +pub fn zoom(server: *Server, arg: Arg) void { if (server.root.focused_view) |current_focus| { const node = @fieldParentPtr(ViewStack.Node, "view", current_focus); if (node != server.root.views.first) { @@ -54,13 +65,15 @@ pub fn zoom(server: *Server) void { } /// Switch focus to the passed tags. -pub fn focusTags(server: *Server, tags: u32) void { +pub fn focusTags(server: *Server, arg: Arg) void { + const tags = arg.uint; server.root.pending_focused_tags = tags; server.root.arrange(); } /// Set the tags of the focused view. -pub fn setFocusedViewTags(server: *Server, tags: u32) void { +pub fn setFocusedViewTags(server: *Server, arg: Arg) void { + const tags = arg.uint; if (server.root.focused_view) |view| { if (view.current_tags != tags) { view.pending_tags = tags; @@ -71,7 +84,7 @@ pub fn setFocusedViewTags(server: *Server, tags: u32) void { /// Spawn a program. /// TODO: make this take a program as a paramter and spawn that -pub fn spawn(server: *Server) void { +pub fn spawn(server: *Server, arg: Arg) void { const argv = [_][]const u8{ "/bin/sh", "-c", "alacritty" }; const child = std.ChildProcess.init(&argv, std.heap.c_allocator) catch unreachable; std.ChildProcess.spawn(child) catch unreachable; diff --git a/src/config.zig b/src/config.zig new file mode 100644 index 0000000..ac67071 --- /dev/null +++ b/src/config.zig @@ -0,0 +1,62 @@ +const std = @import("std"); +const c = @import("c.zig"); +const command = @import("command.zig"); + +const Server = @import("server.zig"); + +pub const Config = struct { + const Self = @This(); + + /// Width of borders in pixels + border_width: u32 = 2, + + /// Amount of view padding in pixels + view_padding: u32 = 10, + + const Keybind = struct { + keysym: c.xkb_keysym_t, + modifiers: u32, + command: command.Command, + arg: command.Arg, + }; + + /// All user-defined keybindings + keybinds: std.ArrayList(Keybind), + + pub fn init(self: *Self, allocator: *std.mem.Allocator) !void { + self.border_width = 2; + self.view_padding = 10; + + self.keybinds = std.ArrayList(Keybind).init(allocator); + + const mod = c.WLR_MODIFIER_LOGO; + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_e, .modifiers = mod, .command = command.exitCompositor, .arg = .{ .none = {} } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_j, .modifiers = mod, .command = command.focusNextView, .arg = .{ .none = {} } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_k, .modifiers = mod, .command = command.focusPrevView, .arg = .{ .none = {} } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_h, .modifiers = mod, .command = command.modifyMasterFactor, .arg = .{ .float = 0.05 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_l, .modifiers = mod, .command = command.modifyMasterFactor, .arg = .{ .float = -0.05 } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_Return, .modifiers = mod, .command = command.zoom, .arg = .{ .none = {} } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_1, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 0 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_2, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 1 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_3, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 2 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_4, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 3 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_5, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 4 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_6, .modifiers = mod, .command = command.focusTags, .arg = .{ .uint = 1 << 5 } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_h, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.modifyMasterCount, .arg = .{ .int = 1 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_l, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.modifyMasterCount, .arg = .{ .int = -1 } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_Return, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.spawn, .arg = .{ .none = {} } }); + + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_1, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 0 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_2, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 1 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_3, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 2 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_4, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 3 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_5, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 4 } }); + try self.keybinds.append(Keybind{ .keysym = c.XKB_KEY_6, .modifiers = mod | c.WLR_MODIFIER_SHIFT, .command = command.setFocusedViewTags, .arg = .{ .uint = 1 << 5 } }); + } +}; diff --git a/src/keyboard.zig b/src/keyboard.zig index 8be9975..af2786f 100644 --- a/src/keyboard.zig +++ b/src/keyboard.zig @@ -112,7 +112,7 @@ pub const Keyboard = struct { if (keyboard.handleBuiltinKeybind(translated_keysyms.?[i])) { handled = true; break; - } else if (keyboard.seat.server.handleKeybinding(translated_keysyms.?[i], modifiers)) { + } else if (keyboard.seat.handleKeybinding(translated_keysyms.?[i], modifiers)) { handled = true; break; } @@ -123,7 +123,7 @@ pub const Keyboard = struct { if (keyboard.handleBuiltinKeybind(raw_keysyms.?[i])) { handled = true; break; - } else if (keyboard.seat.server.handleKeybinding(raw_keysyms.?[i], modifiers)) { + } else if (keyboard.seat.handleKeybinding(raw_keysyms.?[i], modifiers)) { handled = true; break; } diff --git a/src/output.zig b/src/output.zig index 44fa5c4..790c8d2 100644 --- a/src/output.zig +++ b/src/output.zig @@ -129,8 +129,8 @@ pub const Output = struct { // If we have a stashed buffer, we are in the middle of a transaction // and need to render that buffer until the transaction is complete. if (view.stashed_buffer) |buffer| { - const border_width = view.root.border_width; - const view_padding = view.root.view_padding; + const border_width = view.root.server.config.border_width; + const view_padding = view.root.server.config.view_padding; var box = c.wlr_box{ .x = view.current_box.x + @intCast(i32, border_width + view_padding), .y = view.current_box.y + @intCast(i32, border_width + view_padding), @@ -194,11 +194,13 @@ pub const Output = struct { return; } + const border_width = view.root.server.config.border_width; + const view_padding = view.root.server.config.view_padding; var box = c.wlr_box{ .x = @floatToInt(c_int, rdata.ox) + view.current_box.x + sx + - @intCast(c_int, view.root.border_width + view.root.view_padding), + @intCast(c_int, border_width + view_padding), .y = @floatToInt(c_int, rdata.oy) + view.current_box.y + sy + - @intCast(c_int, view.root.border_width + view.root.view_padding), + @intCast(c_int, border_width + view_padding), .width = surface.current.width, .height = surface.current.height, }; @@ -229,8 +231,8 @@ pub const Output = struct { [_]f32{ 0.57647059, 0.63137255, 0.63137255, 1.0 } // Solarized base1 else [_]f32{ 0.34509804, 0.43137255, 0.45882353, 1.0 }; // Solarized base01 - const border_width = self.root.border_width; - const view_padding = self.root.view_padding; + const border_width = self.root.server.config.border_width; + const view_padding = self.root.server.config.view_padding; // left border border.x = @floatToInt(c_int, ox) + view.current_box.x + @intCast(c_int, view_padding); diff --git a/src/root.zig b/src/root.zig index 3754b8b..9700827 100644 --- a/src/root.zig +++ b/src/root.zig @@ -34,12 +34,6 @@ pub const Root = struct { /// Percentage of the total screen that the master section takes up. master_factor: f64, - /// Width of borders in pixels - border_width: u32, - - /// Amount of view padding in pixels - view_padding: u32, - /// Number of pending configures sent in the current transaction. /// A value of 0 means there is no current transaction. pending_configures: u32, @@ -69,10 +63,6 @@ pub const Root = struct { self.master_factor = 0.6; - self.border_width = 4; - - self.view_padding = 10; - self.pending_configures = 0; self.transaction_timer = null; diff --git a/src/seat.zig b/src/seat.zig index 8767323..eacdf8d 100644 --- a/src/seat.zig +++ b/src/seat.zig @@ -41,6 +41,19 @@ pub const Seat = struct { self.cursor.destroy(); } + /// 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| { + if (modifiers == keybind.modifiers and keysym == keybind.keysym) { + // Execute the bound command + keybind.command(self.server, keybind.arg); + return true; + } + } + return false; + } + fn addKeyboard(self: *Self, device: *c.wlr_input_device) !void { c.wlr_seat_set_keyboard(self.wlr_seat, device); diff --git a/src/server.zig b/src/server.zig index d7c43f1..c729fd3 100644 --- a/src/server.zig +++ b/src/server.zig @@ -2,6 +2,7 @@ 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 Output = @import("output.zig").Output; const Root = @import("root.zig").Root; @@ -25,6 +26,8 @@ pub const Server = struct { root: Root, seat: Seat, + config: Config, + listen_new_output: c.wl_listener, listen_new_xdg_surface: c.wl_listener, @@ -73,6 +76,8 @@ pub const Server = struct { try self.seat.init(self); + try self.config.init(self.allocator); + // Register our listeners for new outputs and xdg_surfaces. self.listen_new_output.notify = handleNewOutput; c.wl_signal_add(&self.wlr_backend.events.new_output, &self.listen_new_output); @@ -112,45 +117,6 @@ pub const Server = struct { c.wl_display_run(self.wl_display); } - /// Handle all compositor keybindings - /// Note: this is a hacky initial implementation for testing and will be rewritten eventually - pub fn handleKeybinding(self: *Self, sym: c.xkb_keysym_t, modifiers: u32) bool { - if (modifiers & @intCast(u32, c.WLR_MODIFIER_LOGO) == 0) { - return false; - } - if (modifiers & @intCast(u32, c.WLR_MODIFIER_SHIFT) != 0) { - switch (sym) { - c.XKB_KEY_H => command.modifyMasterCount(self, 1), - c.XKB_KEY_L => command.modifyMasterCount(self, -1), - c.XKB_KEY_Return => command.spawn(self), - c.XKB_KEY_1 => command.setFocusedViewTags(self, 1 << 0), - c.XKB_KEY_2 => command.setFocusedViewTags(self, 1 << 1), - c.XKB_KEY_3 => command.setFocusedViewTags(self, 1 << 2), - c.XKB_KEY_4 => command.setFocusedViewTags(self, 1 << 3), - c.XKB_KEY_5 => command.setFocusedViewTags(self, 1 << 4), - c.XKB_KEY_6 => command.setFocusedViewTags(self, 1 << 5), - else => return false, - } - } else { - switch (sym) { - c.XKB_KEY_e => command.exitCompositor(self), - c.XKB_KEY_j => command.focusNextView(self), - c.XKB_KEY_k => command.focusPrevView(self), - c.XKB_KEY_h => command.modifyMasterFactor(self, 0.05), - c.XKB_KEY_l => command.modifyMasterFactor(self, -0.05), - c.XKB_KEY_Return => command.zoom(self), - c.XKB_KEY_1 => command.focusTags(self, 1 << 0), - c.XKB_KEY_2 => command.focusTags(self, 1 << 1), - c.XKB_KEY_3 => command.focusTags(self, 1 << 2), - c.XKB_KEY_4 => command.focusTags(self, 1 << 3), - c.XKB_KEY_5 => command.focusTags(self, 1 << 4), - c.XKB_KEY_6 => command.focusTags(self, 1 << 5), - else => return false, - } - } - return true; - } - fn handleNewOutput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void { const server = @fieldParentPtr(Server, "listen_new_output", listener.?); const wlr_output = @ptrCast(*c.wlr_output, @alignCast(@alignOf(*c.wlr_output), data)); diff --git a/src/view.zig b/src/view.zig index b1a741c..17a0621 100644 --- a/src/view.zig +++ b/src/view.zig @@ -91,10 +91,12 @@ pub const View = struct { pub fn configurePending(self: *Self) void { if (self.pending_box) |pending_box| { + const border_width = self.root.server.config.border_width; + const view_padding = self.root.server.config.view_padding; self.pending_serial = c.wlr_xdg_toplevel_set_size( self.wlr_xdg_surface, - pending_box.width - self.root.border_width * 2 - self.root.view_padding * 2, - pending_box.height - self.root.border_width * 2 - self.root.view_padding * 2, + pending_box.width - border_width * 2 - view_padding * 2, + pending_box.height - border_width * 2 - view_padding * 2, ); } else { // TODO: log warning