rules: rename "tag" action to "tags"

This rule action accepts and assigns a set of 32 tags represented as a
32 bit integer just like all of river's other commands handling tags.

Using the singular here is potentially misleading and is also
inconsistent with set-view-tags, etc. which all use the plural.

Sorry about the breaking change for those who use master branch, ideally
I would have caught this before merging but at least I noticed before a
release.

This commit also does a bit of internal refactoring/cleanup of the rules
system.
This commit is contained in:
Isaac Freund 2023-11-08 11:28:06 +01:00
parent 2b463c9e4d
commit c4fe1e1a3f
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
6 changed files with 66 additions and 85 deletions

View File

@ -289,7 +289,7 @@ matches everything while _\*\*_ and the empty string are invalid.
and existing views. and existing views.
- *csd*: Use client-side decorations for the view. Applies to new - *csd*: Use client-side decorations for the view. Applies to new
and existing views. 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. an argument. Applies only to new views.
- *output*: Set the initial output of the view. Requires the output - *output*: Set the initial output of the view. Requires the output
as an argument. Applies only to new views. The output can be specified 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_ *rule-del* [*-app-id* _glob_|*-title* _glob_] _action_
Delete a rule created using *rule-add* with the given arguments. 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 Print the specified rule list. The output is ordered from most specific
to least specific, the same order in which views are checked against 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 when searching for a match. Only the first matching rule in the list

View File

@ -87,13 +87,15 @@ mode_to_id: std.StringHashMap(u32),
/// All user-defined keymap modes, indexed by mode id /// All user-defined keymap modes, indexed by mode id
modes: std.ArrayListUnmanaged(Mode), modes: std.ArrayListUnmanaged(Mode),
float_rules: RuleList(bool) = .{}, rules: struct {
ssd_rules: RuleList(bool) = .{}, float: RuleList(bool) = .{},
tag_rules: RuleList(u32) = .{}, ssd: RuleList(bool) = .{},
output_rules: RuleList([]const u8) = .{}, tags: RuleList(u32) = .{},
position_rules: RuleList(Position) = .{}, output: RuleList([]const u8) = .{},
dimensions_rules: RuleList(Dimensions) = .{}, position: RuleList(Position) = .{},
fullscreen_rules: RuleList(bool) = .{}, dimensions: RuleList(Dimensions) = .{},
fullscreen: RuleList(bool) = .{},
} = .{},
/// The selected focus_follows_cursor mode /// The selected focus_follows_cursor mode
focus_follows_cursor: FocusFollowsCursorMode = .disabled, focus_follows_cursor: FocusFollowsCursorMode = .disabled,
@ -167,16 +169,16 @@ pub fn deinit(self: *Self) void {
for (self.modes.items) |*mode| mode.deinit(); for (self.modes.items) |*mode| mode.deinit();
self.modes.deinit(util.gpa); self.modes.deinit(util.gpa);
self.float_rules.deinit(); self.rules.float.deinit();
self.ssd_rules.deinit(); self.rules.ssd.deinit();
self.tag_rules.deinit(); self.rules.tags.deinit();
for (self.output_rules.rules.items) |rule| { for (self.rules.output.rules.items) |rule| {
util.gpa.free(rule.value); util.gpa.free(rule.value);
} }
self.output_rules.deinit(); self.rules.output.deinit();
self.position_rules.deinit(); self.rules.position.deinit();
self.dimensions_rules.deinit(); self.rules.dimensions.deinit();
self.fullscreen_rules.deinit(); self.rules.fullscreen.deinit();
util.gpa.free(self.default_layout_namespace); 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 { 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); var it = server.root.active_outputs.iterator(.forward);
while (it.next()) |output| { while (it.next()) |output| {
const wlr_output = output.wlr_output; const wlr_output = output.wlr_output;

View File

@ -485,19 +485,19 @@ pub fn map(view: *Self) !void {
view.foreign_toplevel_handle.map(); 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; view.pending.float = float;
} }
if (server.config.fullscreen_rules.match(view)) |fullscreen| { if (server.config.rules.fullscreen.match(view)) |fullscreen| {
view.pending.fullscreen = 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; view.pending.ssd = ssd;
} }
const focused_output = server.input_manager.defaultSeat().focused_output; const focused_output = server.input_manager.defaultSeat().focused_output;
if (try server.config.outputRuleMatch(view) orelse focused_output) |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.x = position.x;
view.pending.box.y = position.y; view.pending.box.y = position.y;
} else { } 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); 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.width = dimensions.width;
view.pending.box.height = dimensions.height; view.pending.box.height = dimensions.height;
} }
view.pending.tags = blk: { 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; const tags = output.pending.tags & server.config.spawn_tagmask;
break :blk if (tags != 0) tags else output.pending.tags; break :blk if (tags != 0) tags else output.pending.tags;
}; };

View File

@ -42,7 +42,7 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void {
wlr_decoration.events.destroy.add(&decoration.destroy); wlr_decoration.events.destroy.add(&decoration.destroy);
wlr_decoration.events.request_mode.add(&decoration.request_mode); 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); (decoration.wlr_decoration.requested_mode != .client_side);
// TODO(wlroots): make sure this is properly batched in a single configure // 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 xdg_toplevel: *XdgToplevel = @ptrFromInt(decoration.wlr_decoration.surface.data);
const view = xdg_toplevel.view; 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); (decoration.wlr_decoration.requested_mode != .client_side);
if (view.pending.ssd != ssd) { if (view.pending.ssd != ssd) {

View File

@ -285,7 +285,7 @@ fn handleSetDecorations(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.Xw
const self = @fieldParentPtr(Self, "set_decorations", listener); const self = @fieldParentPtr(Self, "set_decorations", listener);
const view = self.view; 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; !self.xwayland_surface.decorations.no_border;
if (view.pending.ssd != ssd) { if (view.pending.ssd != ssd) {

View File

@ -32,7 +32,7 @@ const Action = enum {
@"no-float", @"no-float",
ssd, ssd,
csd, csd,
tag, tags,
output, output,
position, position,
dimensions, 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) { const positional_arguments_count: u8 = switch (action) {
.float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1, .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1,
.tag, .output => 2, .tags, .output => 2,
.position, .dimensions => 3, .position, .dimensions => 3,
}; };
if (result.args.len > positional_arguments_count) return Error.TooManyArguments; 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) { switch (action) {
.float, .@"no-float" => { .float, .@"no-float" => {
try server.config.float_rules.add(.{ try server.config.rules.float.add(.{
.app_id_glob = app_id_glob, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = (action == .float), .value = (action == .float),
}); });
}, },
.ssd, .csd => { .ssd, .csd => {
try server.config.ssd_rules.add(.{ try server.config.rules.ssd.add(.{
.app_id_glob = app_id_glob, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = (action == .ssd), .value = (action == .ssd),
@ -83,18 +83,18 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
apply_ssd_rules(); apply_ssd_rules();
server.root.applyPending(); server.root.applyPending();
}, },
.tag => { .tags => {
const tag = try fmt.parseInt(u32, result.args[1], 10); const tags = try fmt.parseInt(u32, result.args[1], 10);
try server.config.tag_rules.add(.{ try server.config.rules.tags.add(.{
.app_id_glob = app_id_glob, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = tag, .value = tags,
}); });
}, },
.output => { .output => {
const output_name = try util.gpa.dupe(u8, result.args[1]); const output_name = try util.gpa.dupe(u8, result.args[1]);
errdefer util.gpa.free(output_name); errdefer util.gpa.free(output_name);
try server.config.output_rules.add(.{ try server.config.rules.output.add(.{
.app_id_glob = app_id_glob, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = output_name, .value = output_name,
@ -103,7 +103,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
.position => { .position => {
const x = try fmt.parseInt(u31, result.args[1], 10); const x = try fmt.parseInt(u31, result.args[1], 10);
const y = try fmt.parseInt(u31, result.args[2], 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, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = .{ .value = .{
@ -115,7 +115,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
.dimensions => { .dimensions => {
const width = try fmt.parseInt(u31, result.args[1], 10); const width = try fmt.parseInt(u31, result.args[1], 10);
const height = try fmt.parseInt(u31, result.args[2], 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, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = .{ .value = .{
@ -125,7 +125,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
}); });
}, },
.fullscreen, .@"no-fullscreen" => { .fullscreen, .@"no-fullscreen" => {
try server.config.fullscreen_rules.add(.{ try server.config.rules.fullscreen.add(.{
.app_id_glob = app_id_glob, .app_id_glob = app_id_glob,
.title_glob = title_glob, .title_glob = title_glob,
.value = (action == .fullscreen), .value = (action == .fullscreen),
@ -153,29 +153,29 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
}; };
switch (action) { switch (action) {
.float, .@"no-float" => { .float, .@"no-float" => {
_ = server.config.float_rules.del(rule); _ = server.config.rules.float.del(rule);
}, },
.ssd, .csd => { .ssd, .csd => {
_ = server.config.ssd_rules.del(rule); _ = server.config.rules.ssd.del(rule);
apply_ssd_rules(); apply_ssd_rules();
server.root.applyPending(); server.root.applyPending();
}, },
.tag => { .tags => {
_ = server.config.tag_rules.del(rule); _ = server.config.rules.tags.del(rule);
}, },
.output => { .output => {
if (server.config.output_rules.del(rule)) |output_rule| { if (server.config.rules.output.del(rule)) |output_rule| {
util.gpa.free(output_rule); util.gpa.free(output_rule);
} }
}, },
.position => { .position => {
_ = server.config.position_rules.del(rule); _ = server.config.rules.position.del(rule);
}, },
.dimensions => { .dimensions => {
_ = server.config.dimensions_rules.del(rule); _ = server.config.rules.dimensions.del(rule);
}, },
.fullscreen, .@"no-fullscreen" => { .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 { fn apply_ssd_rules() void {
var it = server.root.views.iterator(.forward); var it = server.root.views.iterator(.forward);
while (it.next()) |view| { while (it.next()) |view| {
if (server.config.ssd_rules.match(view)) |ssd| { if (server.config.rules.ssd.match(view)) |ssd| {
view.pending.ssd = 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.NotEnoughArguments;
if (args.len > 2) return error.TooManyArguments; if (args.len > 2) return error.TooManyArguments;
const list = std.meta.stringToEnum(enum { const rule_list = std.meta.stringToEnum(enum {
float, float,
ssd, ssd,
tag, tags,
output, output,
position, position,
dimensions, dimensions,
fullscreen, fullscreen,
}, args[1]) orelse return Error.UnknownOption; }, args[1]) orelse return Error.UnknownOption;
const max_glob_len = switch (list) { const max_glob_len = switch (rule_list) {
.float => server.config.float_rules.getMaxGlobLen(), inline else => |list| @field(server.config.rules, @tagName(list)).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 app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id); 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); 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 fmt.formatBuf("app-id", .{ .width = app_id_column_max, .alignment = .left }, writer);
try writer.writeAll("action\n"); try writer.writeAll("action\n");
switch (list) { switch (rule_list) {
.float, .ssd => { inline .float, .ssd, .output, .fullscreen => |list| {
const rules = switch (list) { const rules = switch (list) {
.float => server.config.float_rules.rules.items, .float => server.config.rules.float.rules.items,
.ssd => server.config.ssd_rules.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, else => unreachable,
}; };
for (rules) |rule| { 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) { try writer.print("{s}\n", .{switch (list) {
.float => if (rule.value) "float" else "no-float", .float => if (rule.value) "float" else "no-float",
.ssd => if (rule.value) "ssd" else "csd", .ssd => if (rule.value) "ssd" else "csd",
.output => rule.value,
.fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
else => unreachable, else => unreachable,
}}); }});
} }
}, },
.tag => { .tags => {
const rules = server.config.tag_rules.rules.items; for (server.config.rules.tags.rules.items) |rule| {
for (rules) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); 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 fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
try writer.print("{b}\n", .{rule.value}); 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 => { .position => {
const rules = server.config.position_rules.rules.items; for (server.config.rules.position.rules.items) |rule| {
for (rules) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); 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 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 }); try writer.print("{d},{d}\n", .{ rule.value.x, rule.value.y });
} }
}, },
.dimensions => { .dimensions => {
const rules = server.config.dimensions_rules.rules.items; for (server.config.rules.dimensions.rules.items) |rule| {
for (rules) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); 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 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 }); 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(); out.* = try buffer.toOwnedSlice();