diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index f3704fe..b43678b 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -289,7 +289,7 @@ matches everything while _\*\*_ and the empty string are invalid. and existing views. - *csd*: Use client-side decorations for the view. Applies to new and existing views. - - *tag*: Set the initial tags of the view. Requires the tags as + - *tags*: Set the initial tags of the view. Requires the tags as an argument. Applies only to new views. - *output*: Set the initial output of the view. Requires the output as an argument. Applies only to new views. The output can be specified @@ -339,7 +339,7 @@ matches everything while _\*\*_ and the empty string are invalid. *rule-del* [*-app-id* _glob_|*-title* _glob_] _action_ Delete a rule created using *rule-add* with the given arguments. -*list-rules* *float*|*ssd*|*tag*|*position*|*dimensions*|*fullscreen* +*list-rules* *float*|*ssd*|*tags*|*position*|*dimensions*|*fullscreen* Print the specified rule list. The output is ordered from most specific to least specific, the same order in which views are checked against when searching for a match. Only the first matching rule in the list diff --git a/river/Config.zig b/river/Config.zig index a765d54..34336a7 100644 --- a/river/Config.zig +++ b/river/Config.zig @@ -87,13 +87,15 @@ mode_to_id: std.StringHashMap(u32), /// All user-defined keymap modes, indexed by mode id modes: std.ArrayListUnmanaged(Mode), -float_rules: RuleList(bool) = .{}, -ssd_rules: RuleList(bool) = .{}, -tag_rules: RuleList(u32) = .{}, -output_rules: RuleList([]const u8) = .{}, -position_rules: RuleList(Position) = .{}, -dimensions_rules: RuleList(Dimensions) = .{}, -fullscreen_rules: RuleList(bool) = .{}, +rules: struct { + float: RuleList(bool) = .{}, + ssd: RuleList(bool) = .{}, + tags: RuleList(u32) = .{}, + output: RuleList([]const u8) = .{}, + position: RuleList(Position) = .{}, + dimensions: RuleList(Dimensions) = .{}, + fullscreen: RuleList(bool) = .{}, +} = .{}, /// The selected focus_follows_cursor mode focus_follows_cursor: FocusFollowsCursorMode = .disabled, @@ -167,16 +169,16 @@ pub fn deinit(self: *Self) void { for (self.modes.items) |*mode| mode.deinit(); self.modes.deinit(util.gpa); - self.float_rules.deinit(); - self.ssd_rules.deinit(); - self.tag_rules.deinit(); - for (self.output_rules.rules.items) |rule| { + self.rules.float.deinit(); + self.rules.ssd.deinit(); + self.rules.tags.deinit(); + for (self.rules.output.rules.items) |rule| { util.gpa.free(rule.value); } - self.output_rules.deinit(); - self.position_rules.deinit(); - self.dimensions_rules.deinit(); - self.fullscreen_rules.deinit(); + self.rules.output.deinit(); + self.rules.position.deinit(); + self.rules.dimensions.deinit(); + self.rules.fullscreen.deinit(); util.gpa.free(self.default_layout_namespace); @@ -185,7 +187,7 @@ pub fn deinit(self: *Self) void { } pub fn outputRuleMatch(self: *Self, view: *View) !?*Output { - const output_name = self.output_rules.match(view) orelse return null; + const output_name = self.rules.output.match(view) orelse return null; var it = server.root.active_outputs.iterator(.forward); while (it.next()) |output| { const wlr_output = output.wlr_output; diff --git a/river/View.zig b/river/View.zig index bd4935d..e4ea30d 100644 --- a/river/View.zig +++ b/river/View.zig @@ -485,19 +485,19 @@ pub fn map(view: *Self) !void { view.foreign_toplevel_handle.map(); - if (server.config.float_rules.match(view)) |float| { + if (server.config.rules.float.match(view)) |float| { view.pending.float = float; } - if (server.config.fullscreen_rules.match(view)) |fullscreen| { + if (server.config.rules.fullscreen.match(view)) |fullscreen| { view.pending.fullscreen = fullscreen; } - if (server.config.ssd_rules.match(view)) |ssd| { + if (server.config.rules.ssd.match(view)) |ssd| { view.pending.ssd = ssd; } const focused_output = server.input_manager.defaultSeat().focused_output; if (try server.config.outputRuleMatch(view) orelse focused_output) |output| { - if (server.config.position_rules.match(view)) |position| { + if (server.config.rules.position.match(view)) |position| { view.pending.box.x = position.x; view.pending.box.y = position.y; } else { @@ -506,13 +506,13 @@ pub fn map(view: *Self) !void { view.pending.box.y = @divTrunc(@max(0, output.usable_box.height - view.pending.box.height), 2); } - if (server.config.dimensions_rules.match(view)) |dimensions| { + if (server.config.rules.dimensions.match(view)) |dimensions| { view.pending.box.width = dimensions.width; view.pending.box.height = dimensions.height; } view.pending.tags = blk: { - if (server.config.tag_rules.match(view)) |tags| break :blk tags; + if (server.config.rules.tags.match(view)) |tags| break :blk tags; const tags = output.pending.tags & server.config.spawn_tagmask; break :blk if (tags != 0) tags else output.pending.tags; }; diff --git a/river/XdgDecoration.zig b/river/XdgDecoration.zig index bfc133c..f43f5e9 100644 --- a/river/XdgDecoration.zig +++ b/river/XdgDecoration.zig @@ -42,7 +42,7 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void { wlr_decoration.events.destroy.add(&decoration.destroy); wlr_decoration.events.request_mode.add(&decoration.request_mode); - const ssd = server.config.ssd_rules.match(xdg_toplevel.view) orelse + const ssd = server.config.rules.ssd.match(xdg_toplevel.view) orelse (decoration.wlr_decoration.requested_mode != .client_side); // TODO(wlroots): make sure this is properly batched in a single configure @@ -81,7 +81,7 @@ fn handleRequestMode( const xdg_toplevel: *XdgToplevel = @ptrFromInt(decoration.wlr_decoration.surface.data); const view = xdg_toplevel.view; - const ssd = server.config.ssd_rules.match(xdg_toplevel.view) orelse + const ssd = server.config.rules.ssd.match(xdg_toplevel.view) orelse (decoration.wlr_decoration.requested_mode != .client_side); if (view.pending.ssd != ssd) { diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index b717aff..8fd3be1 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -285,7 +285,7 @@ fn handleSetDecorations(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.Xw const self = @fieldParentPtr(Self, "set_decorations", listener); const view = self.view; - const ssd = server.config.ssd_rules.match(view) orelse + const ssd = server.config.rules.ssd.match(view) orelse !self.xwayland_surface.decorations.no_border; if (view.pending.ssd != ssd) { diff --git a/river/command/rule.zig b/river/command/rule.zig index 0104fad..fc90cc3 100644 --- a/river/command/rule.zig +++ b/river/command/rule.zig @@ -32,7 +32,7 @@ const Action = enum { @"no-float", ssd, csd, - tag, + tags, output, position, dimensions, @@ -54,7 +54,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void const positional_arguments_count: u8 = switch (action) { .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1, - .tag, .output => 2, + .tags, .output => 2, .position, .dimensions => 3, }; if (result.args.len > positional_arguments_count) return Error.TooManyArguments; @@ -68,14 +68,14 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void switch (action) { .float, .@"no-float" => { - try server.config.float_rules.add(.{ + try server.config.rules.float.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = (action == .float), }); }, .ssd, .csd => { - try server.config.ssd_rules.add(.{ + try server.config.rules.ssd.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = (action == .ssd), @@ -83,18 +83,18 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void apply_ssd_rules(); server.root.applyPending(); }, - .tag => { - const tag = try fmt.parseInt(u32, result.args[1], 10); - try server.config.tag_rules.add(.{ + .tags => { + const tags = try fmt.parseInt(u32, result.args[1], 10); + try server.config.rules.tags.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, - .value = tag, + .value = tags, }); }, .output => { const output_name = try util.gpa.dupe(u8, result.args[1]); errdefer util.gpa.free(output_name); - try server.config.output_rules.add(.{ + try server.config.rules.output.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = output_name, @@ -103,7 +103,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void .position => { const x = try fmt.parseInt(u31, result.args[1], 10); const y = try fmt.parseInt(u31, result.args[2], 10); - try server.config.position_rules.add(.{ + try server.config.rules.position.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = .{ @@ -115,7 +115,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void .dimensions => { const width = try fmt.parseInt(u31, result.args[1], 10); const height = try fmt.parseInt(u31, result.args[2], 10); - try server.config.dimensions_rules.add(.{ + try server.config.rules.dimensions.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = .{ @@ -125,7 +125,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void }); }, .fullscreen, .@"no-fullscreen" => { - try server.config.fullscreen_rules.add(.{ + try server.config.rules.fullscreen.add(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, .value = (action == .fullscreen), @@ -153,29 +153,29 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void }; switch (action) { .float, .@"no-float" => { - _ = server.config.float_rules.del(rule); + _ = server.config.rules.float.del(rule); }, .ssd, .csd => { - _ = server.config.ssd_rules.del(rule); + _ = server.config.rules.ssd.del(rule); apply_ssd_rules(); server.root.applyPending(); }, - .tag => { - _ = server.config.tag_rules.del(rule); + .tags => { + _ = server.config.rules.tags.del(rule); }, .output => { - if (server.config.output_rules.del(rule)) |output_rule| { + if (server.config.rules.output.del(rule)) |output_rule| { util.gpa.free(output_rule); } }, .position => { - _ = server.config.position_rules.del(rule); + _ = server.config.rules.position.del(rule); }, .dimensions => { - _ = server.config.dimensions_rules.del(rule); + _ = server.config.rules.dimensions.del(rule); }, .fullscreen, .@"no-fullscreen" => { - _ = server.config.fullscreen_rules.del(rule); + _ = server.config.rules.fullscreen.del(rule); }, } } @@ -183,7 +183,7 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void fn apply_ssd_rules() void { var it = server.root.views.iterator(.forward); while (it.next()) |view| { - if (server.config.ssd_rules.match(view)) |ssd| { + if (server.config.rules.ssd.match(view)) |ssd| { view.pending.ssd = ssd; } } @@ -193,23 +193,17 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! if (args.len < 2) return error.NotEnoughArguments; if (args.len > 2) return error.TooManyArguments; - const list = std.meta.stringToEnum(enum { + const rule_list = std.meta.stringToEnum(enum { float, ssd, - tag, + tags, output, position, dimensions, fullscreen, }, args[1]) orelse return Error.UnknownOption; - const max_glob_len = switch (list) { - .float => server.config.float_rules.getMaxGlobLen(), - .ssd => server.config.ssd_rules.getMaxGlobLen(), - .tag => server.config.tag_rules.getMaxGlobLen(), - .output => server.config.output_rules.getMaxGlobLen(), - .position => server.config.position_rules.getMaxGlobLen(), - .dimensions => server.config.dimensions_rules.getMaxGlobLen(), - .fullscreen => server.config.fullscreen_rules.getMaxGlobLen(), + const max_glob_len = switch (rule_list) { + inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(), }; const app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id); const title_column_max = 2 + @max("title".len, max_glob_len.title); @@ -221,11 +215,13 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! try fmt.formatBuf("app-id", .{ .width = app_id_column_max, .alignment = .left }, writer); try writer.writeAll("action\n"); - switch (list) { - .float, .ssd => { + switch (rule_list) { + inline .float, .ssd, .output, .fullscreen => |list| { const rules = switch (list) { - .float => server.config.float_rules.rules.items, - .ssd => server.config.ssd_rules.rules.items, + .float => server.config.rules.float.rules.items, + .ssd => server.config.rules.ssd.rules.items, + .output => server.config.rules.output.rules.items, + .fullscreen => server.config.rules.fullscreen.rules.items, else => unreachable, }; for (rules) |rule| { @@ -234,50 +230,33 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! try writer.print("{s}\n", .{switch (list) { .float => if (rule.value) "float" else "no-float", .ssd => if (rule.value) "ssd" else "csd", + .output => rule.value, + .fullscreen => if (rule.value) "fullscreen" else "no-fullscreen", else => unreachable, }}); } }, - .tag => { - const rules = server.config.tag_rules.rules.items; - for (rules) |rule| { + .tags => { + for (server.config.rules.tags.rules.items) |rule| { try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try writer.print("{b}\n", .{rule.value}); } }, - .output => { - const rules = server.config.output_rules.rules.items; - for (rules) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); - try writer.print("{s}\n", .{rule.value}); - } - }, .position => { - const rules = server.config.position_rules.rules.items; - for (rules) |rule| { + for (server.config.rules.position.rules.items) |rule| { try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try writer.print("{d},{d}\n", .{ rule.value.x, rule.value.y }); } }, .dimensions => { - const rules = server.config.dimensions_rules.rules.items; - for (rules) |rule| { + for (server.config.rules.dimensions.rules.items) |rule| { try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try writer.print("{d}x{d}\n", .{ rule.value.width, rule.value.height }); } }, - .fullscreen => { - const rules = server.config.fullscreen_rules.rules.items; - for (rules) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); - try writer.print("{s}\n", .{if (rule.value) "fullscreen" else "no-fullscreen"}); - } - }, } out.* = try buffer.toOwnedSlice();