8975cd18
Add dirty-row refresh planning helpers
a73x 2026-04-08 18:25
diff --git a/src/main.zig b/src/main.zig index d0abf95..e0b4313 100644 --- a/src/main.zig +++ b/src/main.zig @@ -367,6 +367,46 @@ fn shouldRenderFrame(terminal_dirty: bool, window_dirty: bool, forced: bool) boo return terminal_dirty or window_dirty or forced; } const RowRefreshState = enum { full, partial, }; const RowRefreshContext = struct { cursor_changed: bool, old_cursor_row: ?usize, new_cursor_row: ?usize, }; const RowRefreshPlan = struct { full_rebuild: bool, cursor_rebuild: bool, rows_to_rebuild: std.StaticBitSet(256), }; fn planRowRefresh( state: RowRefreshState, dirty_rows: []const bool, ctx: RowRefreshContext, ) RowRefreshPlan { var rows_to_rebuild = std.StaticBitSet(256).initEmpty(); const full_rebuild = state == .full; if (!full_rebuild) { const limit = @min(dirty_rows.len, rows_to_rebuild.capacity()); var row_idx: usize = 0; while (row_idx < limit) : (row_idx += 1) { if (dirty_rows[row_idx]) rows_to_rebuild.set(row_idx); } } return .{ .full_rebuild = full_rebuild, .cursor_rebuild = ctx.cursor_changed, .rows_to_rebuild = rows_to_rebuild, }; } fn appendCellInstances( alloc: std.mem.Allocator, instances: *std.ArrayListUnmanaged(renderer.Instance), @@ -476,6 +516,42 @@ test "event loop redraws only when terminal or window state changed" { try std.testing.expect(!shouldRenderFrame(false, false, false)); } test "planRowRefresh requests full rebuild for full dirty state" { const plan = planRowRefresh(.full, &.{ false, true, false }, .{ .cursor_changed = false, .old_cursor_row = null, .new_cursor_row = null, }); try std.testing.expect(plan.full_rebuild); try std.testing.expectEqual(@as(usize, 0), plan.rows_to_rebuild.count()); } test "planRowRefresh selects only dirty rows for partial state" { const plan = planRowRefresh(.partial, &.{ false, true, false, true }, .{ .cursor_changed = false, .old_cursor_row = null, .new_cursor_row = null, }); try std.testing.expect(!plan.full_rebuild); try std.testing.expect(plan.rows_to_rebuild.isSet(1)); try std.testing.expect(plan.rows_to_rebuild.isSet(3)); try std.testing.expect(!plan.rows_to_rebuild.isSet(0)); } test "planRowRefresh handles cursor-only updates without unrelated rows" { const plan = planRowRefresh(.partial, &.{ false, false, false }, .{ .cursor_changed = true, .old_cursor_row = 1, .new_cursor_row = 2, }); try std.testing.expect(!plan.full_rebuild); try std.testing.expect(plan.cursor_rebuild); try std.testing.expectEqual(@as(usize, 0), plan.rows_to_rebuild.count()); } fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { var conn = try wayland_client.Connection.init(); defer conn.deinit();