diff --git a/river/Config.zig b/river/Config.zig index 5a14529..67b9771 100644 --- a/river/Config.zig +++ b/river/Config.zig @@ -25,7 +25,7 @@ const util = @import("util.zig"); const Server = @import("Server.zig"); const Mode = @import("Mode.zig"); -const RuleList = @import("RuleList.zig"); +const RuleList = @import("rule_list.zig").RuleList; pub const AttachMode = enum { top, @@ -73,8 +73,8 @@ mode_to_id: std.StringHashMap(u32), /// All user-defined keymap modes, indexed by mode id modes: std.ArrayListUnmanaged(Mode), -float_rules: RuleList = .{}, -ssd_rules: RuleList = .{}, +float_rules: RuleList(bool) = .{}, +ssd_rules: RuleList(bool) = .{}, /// The selected focus_follows_cursor mode focus_follows_cursor: FocusFollowsCursorMode = .disabled, diff --git a/river/RuleList.zig b/river/RuleList.zig deleted file mode 100644 index db765e7..0000000 --- a/river/RuleList.zig +++ /dev/null @@ -1,106 +0,0 @@ -// This file is part of river, a dynamic tiling wayland compositor. -// -// Copyright 2023 The River Developers -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -const RuleList = @This(); - -const std = @import("std"); -const mem = std.mem; - -const globber = @import("globber"); -const util = @import("util.zig"); - -const View = @import("View.zig"); - -const Rule = struct { - app_id_glob: []const u8, - title_glob: []const u8, - value: bool, -}; - -/// Ordered from most specific to most general. -/// Ordered first by app-id generality then by title generality. -rules: std.ArrayListUnmanaged(Rule) = .{}, - -pub fn deinit(list: *RuleList) void { - for (list.rules.items) |rule| { - util.gpa.free(rule.app_id_glob); - util.gpa.free(rule.title_glob); - } - list.rules.deinit(util.gpa); -} - -pub fn add(list: *RuleList, rule: Rule) error{OutOfMemory}!void { - const index = for (list.rules.items) |*existing, i| { - if (mem.eql(u8, rule.app_id_glob, existing.app_id_glob) and - mem.eql(u8, rule.title_glob, existing.title_glob)) - { - existing.value = rule.value; - return; - } - - switch (globber.order(rule.app_id_glob, existing.app_id_glob)) { - .lt => break i, - .eq => { - if (globber.order(rule.title_glob, existing.title_glob) == .lt) { - break i; - } - }, - .gt => {}, - } - } else list.rules.items.len; - - const owned_app_id_glob = try util.gpa.dupe(u8, rule.app_id_glob); - errdefer util.gpa.free(owned_app_id_glob); - - const owned_title_glob = try util.gpa.dupe(u8, rule.title_glob); - errdefer util.gpa.free(owned_title_glob); - - try list.rules.insert(util.gpa, index, .{ - .app_id_glob = owned_app_id_glob, - .title_glob = owned_title_glob, - .value = rule.value, - }); -} - -pub fn del(list: *RuleList, rule: Rule) void { - for (list.rules.items) |existing, i| { - if (mem.eql(u8, rule.app_id_glob, existing.app_id_glob) and - mem.eql(u8, rule.title_glob, existing.title_glob)) - { - util.gpa.free(existing.app_id_glob); - util.gpa.free(existing.title_glob); - _ = list.rules.orderedRemove(i); - return; - } - } -} - -/// Returns the value of the most specific rule matching the view. -/// Returns null if no rule matches. -pub fn match(list: *RuleList, view: *View) ?bool { - const app_id = mem.sliceTo(view.getAppId(), 0) orelse ""; - const title = mem.sliceTo(view.getTitle(), 0) orelse ""; - - for (list.rules.items) |rule| { - if (globber.match(app_id, rule.app_id_glob) and - globber.match(title, rule.title_glob)) - { - return rule.value; - } - } - - return null; -} diff --git a/river/command/rule.zig b/river/command/rule.zig index c9665af..807b55e 100644 --- a/river/command/rule.zig +++ b/river/command/rule.zig @@ -24,7 +24,6 @@ const server = &@import("../main.zig").server; const util = @import("../util.zig"); const Error = @import("../command.zig").Error; -const RuleList = @import("../RuleList.zig"); const Seat = @import("../Seat.zig"); const View = @import("../View.zig"); @@ -95,14 +94,12 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void server.config.float_rules.del(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, - .value = (action == .float), }); }, .ssd, .csd => { server.config.ssd_rules.del(.{ .app_id_glob = app_id_glob, .title_glob = title_glob, - .value = (action == .ssd), }); apply_ssd_rules(); server.root.applyPending(); diff --git a/river/rule_list.zig b/river/rule_list.zig new file mode 100644 index 0000000..325dcc3 --- /dev/null +++ b/river/rule_list.zig @@ -0,0 +1,124 @@ +// This file is part of river, a dynamic tiling wayland compositor. +// +// Copyright 2023 The River Developers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +const std = @import("std"); +const mem = std.mem; + +const globber = @import("globber"); +const util = @import("util.zig"); + +const View = @import("View.zig"); + +pub fn RuleList(comptime T: type) type { + return struct { + const Self = @This(); + + const Rule = struct { + app_id_glob: []const u8, + title_glob: []const u8, + value: T, + }; + + /// Ordered from most specific to most general. + /// Ordered first by app-id generality then by title generality. + rules: std.ArrayListUnmanaged(Rule) = .{}, + + pub fn deinit(list: *Self) void { + for (list.rules.items) |rule| { + util.gpa.free(rule.app_id_glob); + util.gpa.free(rule.title_glob); + } + list.rules.deinit(util.gpa); + } + + pub fn add(list: *Self, rule: Rule) error{OutOfMemory}!void { + const index = for (list.rules.items) |*existing, i| { + if (mem.eql(u8, rule.app_id_glob, existing.app_id_glob) and + mem.eql(u8, rule.title_glob, existing.title_glob)) + { + existing.value = rule.value; + return; + } + + switch (globber.order(rule.app_id_glob, existing.app_id_glob)) { + .lt => break i, + .eq => { + if (globber.order(rule.title_glob, existing.title_glob) == .lt) { + break i; + } + }, + .gt => {}, + } + } else list.rules.items.len; + + const owned_app_id_glob = try util.gpa.dupe(u8, rule.app_id_glob); + errdefer util.gpa.free(owned_app_id_glob); + + const owned_title_glob = try util.gpa.dupe(u8, rule.title_glob); + errdefer util.gpa.free(owned_title_glob); + + try list.rules.insert(util.gpa, index, .{ + .app_id_glob = owned_app_id_glob, + .title_glob = owned_title_glob, + .value = rule.value, + }); + } + + pub fn del(list: *Self, rule: struct { app_id_glob: []const u8, title_glob: []const u8 }) void { + for (list.rules.items) |existing, i| { + if (mem.eql(u8, rule.app_id_glob, existing.app_id_glob) and + mem.eql(u8, rule.title_glob, existing.title_glob)) + { + util.gpa.free(existing.app_id_glob); + util.gpa.free(existing.title_glob); + _ = list.rules.orderedRemove(i); + return; + } + } + } + + /// Returns the value of the most specific rule matching the view. + /// Returns null if no rule matches. + pub fn match(list: *Self, view: *View) ?T { + const app_id = mem.sliceTo(view.getAppId(), 0) orelse ""; + const title = mem.sliceTo(view.getTitle(), 0) orelse ""; + + for (list.rules.items) |rule| { + if (globber.match(app_id, rule.app_id_glob) and + globber.match(title, rule.title_glob)) + { + return rule.value; + } + } + + return null; + } + + /// Returns the length of the longest globs. + pub fn getMaxGlobLen(self: *const Self) MaxGlobLen { + var app_id_len: usize = 0; + var title_len: usize = 0; + for (self.rules.items) |rule| { + app_id_len = @max(app_id_len, rule.app_id_glob.len); + title_len = @max(title_len, rule.title_glob.len); + } + return .{ + .app_id = app_id_len, + .title = title_len, + }; + } + }; +}