Iskren Chernev: 1 Implement per-tag-cfg 1 files changed, 73 insertions(+), 47 deletions(-)
March 30, 2024 at 8:56 PM, "Iskren Chernev" <me@iskren.info> wrote:
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~novakane/public-inbox/patches/50615/mbox | git am -3Learn more about email & git
It is sometimes useful to remember the configuration for every tag, instead of having a single configuration for all tags. Add a command line option to pick per-tags-config, Then keep the available configuration in a HashMap and pick the right one for user_command and layout. --- src/main.zig | 120 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/src/main.zig b/src/main.zig index f0298c7..f13c49d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -40,6 +40,7 @@ const usage = \\ -h Print this help message and exit. \\ -version Print the version number and exit. \\ -no-smart-gaps Disable smart gaps + \\ -per-tag-cfg Remember configuration per tag
I prefer just `per-tag`
\\ \\ The following commands may also be sent to rivercarro at runtime: \\ @@ -86,6 +87,7 @@ const Config = struct { main_count: u31 = 1, main_ratio: f64 = 0.6, width_ratio: f64 = 1.0, + per_tag_config: bool = false, };
same, just `per_tag`
const Context = struct { @@ -101,10 +103,25 @@ const Output = struct { wl_output: *wl.Output, name: u32, - cfg: Config, + cfgs: std.AutoHashMap(u32, Config),
Could you use std.AutoHashMapUnmanaged instead
+ user_command_tags: u32 = 0, layout: *river.LayoutV3 = undefined, + fn get_cfg(output: *Output, tags: u32) *Config { + var default_cfg = output.cfgs.getPtr(0) orelse unreachable; + if (!cfg.per_tag_config) { + return default_cfg; + } + // default to global config + var entry = output.cfgs.getOrPutValue(tags, default_cfg.*) catch { + // 0 always has a value + log.err("out of memory, reverting to default cfg", .{}); + return default_cfg; + }; + return entry.value_ptr; + } + fn get_layout(output: *Output) !void { output.layout = try ctx.layout_manager.?.getLayout(output.wl_output, "rivercarro"); output.layout.setListener(*Output, layout_listener, output); @@ -116,6 +133,7 @@ const Output = struct { .user_command => |ev| { var it = mem.tokenize(u8, mem.span(ev.command), " "); + const active_cfg = output.get_cfg(output.user_command_tags); const raw_cmd = it.next() orelse { log.err("not enough arguments", .{}); return; @@ -139,12 +157,12 @@ const Output = struct { return; }; switch (raw_arg[0]) { - '+' => output.cfg.inner_gaps +|= @intCast(arg), + '+' => active_cfg.inner_gaps +|= @intCast(arg), '-' => { - const res = output.cfg.inner_gaps +| arg; - if (res >= 0) output.cfg.inner_gaps = @intCast(res); + const res = active_cfg.inner_gaps +| arg; + if (res >= 0) active_cfg.*.inner_gaps = @intCast(res); },
`active_cfg.inner_gaps` as said in the previous mail `.*` is not needed, you can remove it everywhere
- else => output.cfg.inner_gaps = @intCast(arg), + else => active_cfg.*.inner_gaps = @intCast(arg), } }, .@"outer-gaps" => { @@ -153,12 +171,12 @@ const Output = struct { return; }; switch (raw_arg[0]) { - '+' => output.cfg.outer_gaps +|= @intCast(arg), + '+' => active_cfg.*.outer_gaps +|= @intCast(arg), '-' => { - const res = output.cfg.outer_gaps +| arg; - if (res >= 0) output.cfg.outer_gaps = @intCast(res); + const res = active_cfg.outer_gaps +| arg; + if (res >= 0) active_cfg.*.outer_gaps = @intCast(res); }, - else => output.cfg.outer_gaps = @intCast(arg), + else => active_cfg.*.outer_gaps = @intCast(arg), } }, .gaps => { @@ -168,23 +186,23 @@ const Output = struct { }; switch (raw_arg[0]) { '+' => { - output.cfg.inner_gaps +|= @intCast(arg); - output.cfg.outer_gaps +|= @intCast(arg); + active_cfg.*.inner_gaps +|= @intCast(arg); + active_cfg.*.outer_gaps +|= @intCast(arg); }, '-' => { - const o = output.cfg.outer_gaps +| arg; - const i = output.cfg.inner_gaps +| arg; - if (i >= 0) output.cfg.inner_gaps = @intCast(i); - if (o >= 0) output.cfg.outer_gaps = @intCast(o); + const o = active_cfg.outer_gaps +| arg; + const i = active_cfg.inner_gaps +| arg; + if (i >= 0) active_cfg.*.inner_gaps = @intCast(i); + if (o >= 0) active_cfg.*.outer_gaps = @intCast(o); }, else => { - output.cfg.inner_gaps = @intCast(arg); - output.cfg.outer_gaps = @intCast(arg); + active_cfg.*.inner_gaps = @intCast(arg); + active_cfg.*.outer_gaps = @intCast(arg); }, } }, .@"main-location" => { - output.cfg.main_location = std.meta.stringToEnum(Location, raw_arg) orelse { + active_cfg.*.main_location = std.meta.stringToEnum(Location, raw_arg) orelse { log.err("unknown location: {s}", .{raw_arg}); return; }; @@ -203,11 +221,11 @@ const Output = struct { picked = current; if (pick_next) break; } - if (current == output.cfg.main_location) { + if (current == active_cfg.main_location) { pick_next = true; } } - output.cfg.main_location = picked.?; + active_cfg.*.main_location = picked.?; }, .@"main-count" => { const arg = fmt.parseInt(i32, raw_arg, 10) catch |err| { @@ -215,13 +233,13 @@ const Output = struct { return; }; switch (raw_arg[0]) { - '+' => output.cfg.main_count +|= @intCast(arg), + '+' => active_cfg.*.main_count +|= @intCast(arg), '-' => { - const res = output.cfg.main_count +| arg; - if (res >= 1) output.cfg.main_count = @intCast(res); + const res = active_cfg.main_count +| arg; + if (res >= 1) active_cfg.*.main_count = @intCast(res); }, else => { - if (arg >= 1) output.cfg.main_count = @intCast(arg); + if (arg >= 1) active_cfg.*.main_count = @intCast(arg); }, } }, @@ -232,9 +250,9 @@ const Output = struct { }; switch (raw_arg[0]) { '+', '-' => { - output.cfg.main_ratio = math.clamp(output.cfg.main_ratio + arg, 0.1, 0.9); + active_cfg.*.main_ratio = math.clamp(active_cfg.main_ratio + arg, 0.1, 0.9); }, - else => output.cfg.main_ratio = math.clamp(arg, 0.1, 0.9), + else => active_cfg.*.main_ratio = math.clamp(arg, 0.1, 0.9), } }, .@"width-ratio" => { @@ -244,44 +262,47 @@ const Output = struct { }; switch (raw_arg[0]) { '+', '-' => { - output.cfg.width_ratio = math.clamp(output.cfg.width_ratio + arg, 0.1, 1.0); + active_cfg.*.width_ratio = math.clamp(active_cfg.width_ratio + arg, 0.1, 1.0); }, - else => output.cfg.width_ratio = math.clamp(arg, 0.1, 1.0), + else => active_cfg.*.width_ratio = math.clamp(arg, 0.1, 1.0), } }, } }, - .user_command_tags => {}, + .user_command_tags => |ev| { + output.user_command_tags = ev.tags; + }, .layout_demand => |ev| { assert(ev.view_count > 0); - const main_count = @min(output.cfg.main_count, @as(u31, @truncate(ev.view_count))); + const active_cfg = output.get_cfg(ev.tags); + const main_count = @min(active_cfg.main_count, @as(u31, @truncate(ev.view_count))); const sec_count = @as(u31, @truncate(ev.view_count)) -| main_count; - const only_one_view = ev.view_count == 1 or output.cfg.main_location == .monocle; + const only_one_view = ev.view_count == 1 or active_cfg.main_location == .monocle; // Don't add gaps if there is only one view. if (only_one_view and cfg.smart_gaps) { cfg.outer_gaps = 0; cfg.inner_gaps = 0; } else { - cfg.outer_gaps = output.cfg.outer_gaps; - cfg.inner_gaps = output.cfg.inner_gaps; + cfg.outer_gaps = active_cfg.outer_gaps; + cfg.inner_gaps = active_cfg.inner_gaps; } - const usable_w = switch (output.cfg.main_location) { + const usable_w = switch (active_cfg.main_location) { .left, .right, .monocle => @as( u31, - @intFromFloat(@as(f64, @floatFromInt(ev.usable_width)) * output.cfg.width_ratio), + @intFromFloat(@as(f64, @floatFromInt(ev.usable_width)) * active_cfg.width_ratio), ) -| (2 *| cfg.outer_gaps), .top, .bottom => @as(u31, @truncate(ev.usable_height)) -| (2 *| cfg.outer_gaps), }; - const usable_h = switch (output.cfg.main_location) { + const usable_h = switch (active_cfg.main_location) { .left, .right, .monocle => @as(u31, @truncate(ev.usable_height)) -| (2 *| cfg.outer_gaps), .top, .bottom => @as( u31, - @intFromFloat(@as(f64, @floatFromInt(ev.usable_width)) * output.cfg.width_ratio), + @intFromFloat(@as(f64, @floatFromInt(ev.usable_width)) * active_cfg.width_ratio), ) -| (2 *| cfg.outer_gaps), }; @@ -295,7 +316,7 @@ const Output = struct { var sec_h: u31 = undefined; var sec_h_rem: u31 = undefined; - if (output.cfg.main_location == .monocle) { + if (active_cfg.main_location == .monocle) { main_w = usable_w; main_h = usable_h; @@ -303,7 +324,7 @@ const Output = struct { sec_h = usable_h; } else { if (sec_count > 0) { - main_w = @as(u31, @intFromFloat(output.cfg.main_ratio * @as(f64, @floatFromInt(usable_w)))); + main_w = @as(u31, @intFromFloat(active_cfg.main_ratio * @as(f64, @floatFromInt(usable_w)))); main_h = usable_h / main_count; main_h_rem = usable_h % main_count; @@ -324,7 +345,7 @@ const Output = struct { var width: u31 = undefined; var height: u31 = undefined; - if (output.cfg.main_location == .monocle) { + if (active_cfg.main_location == .monocle) { x = 0; y = 0; width = main_w; @@ -346,7 +367,7 @@ const Output = struct { } } - switch (output.cfg.main_location) { + switch (active_cfg.main_location) { .left => layout.pushViewDimensions( x +| cfg.outer_gaps, y +| cfg.outer_gaps, @@ -385,7 +406,7 @@ const Output = struct { } } - switch (output.cfg.main_location) { + switch (active_cfg.main_location) { .left => layout.commit("left", ev.serial), .right => layout.commit("right", ev.serial), .top => layout.commit("top", ev.serial), @@ -408,6 +429,7 @@ pub fn main() !void { .{ .name = "main-count", .kind = .arg }, .{ .name = "main-ratio", .kind = .arg }, .{ .name = "width-ratio", .kind = .arg }, + .{ .name = "per-tag-cfg", .kind = .boolean }, }).parse(os.argv[1..]) catch { try std.io.getStdErr().writeAll(usage); os.exit(1); @@ -457,6 +479,9 @@ pub fn main() !void { fatal_usage("Invalid value '{s}' provided to -width-ratio", .{raw}); } } + if (res.flags.@"per-tag-cfg") { + cfg.per_tag_config = true; + } const display = wl.Display.connect(null) catch { fatal("unable to connect to wayland compositor", .{}); @@ -513,11 +538,11 @@ fn registry_event(context: *Context, registry: *wl.Registry, event: wl.Registry. const node = try gpa.create(std.SinglyLinkedList(Output).Node); errdefer gpa.destroy(node); - node.data = .{ - .wl_output = wl_output, - .name = ev.name, - .cfg = cfg, - }; + node.data = .{ .wl_output = wl_output, .name = ev.name, .cfgs = blk: { + var cfgs = std.AutoHashMap(u32, Config).init(gpa); + try cfgs.put(0, cfg); + break :blk cfgs; + } }; if (ctx.initialized) try node.data.get_layout(); context.outputs.prepend(node); @@ -529,6 +554,7 @@ fn registry_event(context: *Context, registry: *wl.Registry, event: wl.Registry. if (node.data.name == ev.name) { node.data.wl_output.release(); node.data.layout.destroy(); + node.data.cfgs.deinit(); context.outputs.remove(node); gpa.destroy(node); break; -- 2.44.0
A few nitpicks but other than that it seems to works well, I'll test it a bit more in the next few days, and see if I can think of improvements for the implementation, but that seems fine for now.
builds.sr.ht <builds@sr.ht>rivercarro/patches/.build.yml: SUCCESS in 1m3s [Implement per-tag-cfg][0] from [Iskren Chernev][1] [0]: https://lists.sr.ht/~novakane/public-inbox/patches/50615 [1]: mailto:me@iskren.info ✓ #1182928 SUCCESS rivercarro/patches/.build.yml https://builds.sr.ht/~novakane/job/1182928
Iskren Chernev <me@iskren.info>Sorry for posting the patch again, I though the old one got lost. This one adds deinit, and cleans up the init (in a labeled block). Ideally there should be an Output init/deinit which captures the logic, I can do that as well, but it's a bit offtopic, so decided against it.