From c4da4ef30acd8f7b9151b1b1e3bf148b1d631270 Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Tue, 17 Feb 2026 16:51:57 -0600 Subject: [PATCH] Make window rules frame perfect This moves window initialization earlier in the manage sequence. Previously, it was on the Window's first manage() call, but this is after the layout has already been calculated, which matters both because of tags and whether the window starts floating or not. Now, initialization is handled in a separate function that gets called in Output.calculatePrimaryStackLayout() instead. --- docs/TODO.md | 2 +- src/Output.zig | 3 +++ src/Window.zig | 45 ++++++++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index e9ef37c..9b87dd5 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -2,7 +2,6 @@ These are in rough order of my priority, though no promises I do them in this order. -- [ ] Support window rules (float/tags/SSD by app-id/title) - [ ] Support overriding config location - [ ] Support configuring primary vs secondary stack side - [ ] Support switch handling (e.g. lid close) @@ -31,3 +30,4 @@ These are in rough order of my priority, though no promises I do them in this or - [x] Add options to the tag overlay - [x] Add options to the bar - [x] Make a Rect struct to combine x, y, width, and height +- [x] Support window rules (float/tags by app-id/title) diff --git a/src/Output.zig b/src/Output.zig index ec65420..fdfff0a 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -617,6 +617,9 @@ fn calculatePrimaryStackLayout(output: *Output) void { var active_count: u31 = 0; var it = output.windows.iterator(.forward); while (it.next()) |window| { + // Initialize new windows before checking tags/float so that + // window rules are reflected in the first frame's layout. + window.initialize(); if (output.tags & window.tags != 0x0000) { // Floating windows should be shown but not included in this layout generation const will_float = window.pending_manage.floating orelse window.floating; diff --git a/src/Window.zig b/src/Window.zig index 7ff9be0..5c38bfb 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -164,31 +164,38 @@ fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event, } } -pub fn manage(window: *Window) void { - const river_window_v1 = window.river_window_v1; - if (!window.initialized) { - // Only happens once per Window +/// Apply one-time initialization for newly created windows. +/// Called before calculatePrimaryStackLayout() so that tag and float +/// rules are reflected in the first frame's layout. +pub fn initialize(window: *Window) void { + if (window.initialized) { @branchHint(.unlikely); - window.initialized = true; - - // TODO: We might want to think about paying attention to the decoration_hint event - // If we do, this would need to move, I think? - river_window_v1.useSsd(); - - river_window_v1.setCapabilities(.{ .fullscreen = true, .maximize = true }); - river_window_v1.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); - - const res = window.applyRules(); - if (res.tags) |tags| window.tags = tags; - if (res.float) |should_float| - window.pending_manage.floating = should_float - else - window.pending_manage.floating = false; + return; } + window.initialized = true; + const river_window_v1 = window.river_window_v1; + + // TODO: We might want to think about paying attention to the decoration_hint event + // If we do, this would need to move, I think? + river_window_v1.useSsd(); + + river_window_v1.setCapabilities(.{ .fullscreen = true, .maximize = true }); + river_window_v1.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); + + const res = window.applyRules(); + if (res.tags) |tags| window.tags = tags; + if (res.float) |should_float| + window.pending_manage.floating = should_float + else + window.pending_manage.floating = false; +} + +pub fn manage(window: *Window) void { // Updating state since the last manage event defer window.pending_manage = .{}; const pending_manage = window.pending_manage; + const river_window_v1 = window.river_window_v1; // Floating status if (pending_manage.floating) |floating| blk: { // This needs to be before proposing the new dimensions since we want to save the current ones!