From 7b92da8edb0decd756463d49e8c6c1ac5581cd9f Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 06:58:59 -0500 Subject: [PATCH 1/4] linux: work-362: claim From 5c3152c98b9247054edcca3394f05859015c61c7 Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 07:04:35 -0500 Subject: [PATCH 2/4] linux: work-362 (codex) Agent-Team: linux Worker: codex --- core/widgets/ScrollArea.zig | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/core/widgets/ScrollArea.zig b/core/widgets/ScrollArea.zig index f35a503fa..7c1958a07 100644 --- a/core/widgets/ScrollArea.zig +++ b/core/widgets/ScrollArea.zig @@ -289,6 +289,7 @@ var t_use_dvui: bool = false; /// and whether the renderer snaps there. var t_drawn_y: f32 = 0; var t_raw_y: f32 = 0; +var t_draw_scale: f32 = 1; var t_snapping: bool = false; /// Lay the area out as a column of `t_rows` rows, `t_top` points below the window's top, and /// record where each row is drawn. @@ -324,6 +325,7 @@ fn testFrame() !dvui.App.Result { } const child = dvui.spacer(@src(), .{ .min_size_content = .{ .w = 100, .h = 20000 } }); t_snapping = dvui.snapToPixels(); + t_draw_scale = child.rectScale().s; const y = child.rectScale().r.y; t_raw_y = y; t_drawn_y = if (t_snapping) @round(y) else y; @@ -401,9 +403,12 @@ test "coast displacement follows each frame gap" { defer t.deinit(); t_use_dvui = false; _ = try swipe(); + // swipe ends the release frame and begins the next one 16 ms later. + const release_time = dvui.currentWindow().frame_time_ns - 16 * std.time.ns_per_ms; + const release_speed: f64 = t_release_velocity * t_draw_scale; var previous_time = dvui.currentWindow().frame_time_ns; try frameAfter(25); - var previous_y = t_raw_y; + var previous_y = t_drawn_y; var previous_speed: f32 = std.math.inf(f32); var follows_gaps = true; // frameAfter draws at the current timestamp, then begins the next frame with this gap. @@ -412,12 +417,18 @@ test "coast displacement follows each frame gap" { const time = dvui.currentWindow().frame_time_ns; const gap_ms: f32 = @as(f32, @floatFromInt(time - previous_time)) / std.time.ns_per_ms; try frameAfter(next_gap); - const displacement = previous_y - t_raw_y; + const displacement = previous_y - t_drawn_y; + const from_s = @as(f64, @floatFromInt(previous_time - release_time)) / std.time.ns_per_s; + const to_s = @as(f64, @floatFromInt(time - release_time)) / std.time.ns_per_s; + const expected: f32 = @floatCast(release_speed / tuning.decay * + (@exp(-tuning.decay * from_s) - @exp(-tuning.decay * to_s))); + // Check what reaches the renderer against the release integral, including its decay. + try std.testing.expectApproxEqAbs(expected, displacement, 0.0001); const speed = displacement / gap_ms; - std.debug.print("coast: gap={d:.0} ms, displacement={d:.6} points\n", .{ gap_ms, displacement }); + std.debug.print("coast: gap={d:.0} ms, displacement={d:.6} pixels\n", .{ gap_ms, displacement }); follows_gaps = follows_gaps and displacement > 0 and speed < previous_speed; previous_time = time; - previous_y = t_raw_y; + previous_y = t_drawn_y; previous_speed = speed; } try std.testing.expect(follows_gaps); From 3395a8d408e78fefeed2daf3c3b37148fb3bfe9d Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 07:58:32 -0500 Subject: [PATCH 3/4] linux: work-362 (codex) Agent-Team: linux Worker: codex --- core/widgets/ScrollArea.zig | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/widgets/ScrollArea.zig b/core/widgets/ScrollArea.zig index 7c1958a07..1d0d2acde 100644 --- a/core/widgets/ScrollArea.zig +++ b/core/widgets/ScrollArea.zig @@ -284,6 +284,8 @@ fn scrollBy(self: *ScrollArea, by: dvui.Point) void { var t_viewport_y: f32 = 0; var t_release_velocity: f32 = 0; +// Written by swipe on the test thread before it draws the release frame. +var t_release_time: i128 = 0; var t_use_dvui: bool = false; /// Where a child of the area is drawn this frame (physical y, as the renderer would place it), /// and whether the renderer snaps there. @@ -359,6 +361,7 @@ fn swipe() !f32 { _ = try cw.addEventTouchMotion(.touch0, x, y - 1 / h, 0, -1 / h); try frameAfter(16); _ = try cw.addEventPointer(.{ .button = .touch0, .action = .release, .xynorm = .{ .x = x, .y = y } }); + t_release_time = cw.frame_time_ns; try frameAfter(16); return t_viewport_y; } @@ -403,14 +406,11 @@ test "coast displacement follows each frame gap" { defer t.deinit(); t_use_dvui = false; _ = try swipe(); - // swipe ends the release frame and begins the next one 16 ms later. - const release_time = dvui.currentWindow().frame_time_ns - 16 * std.time.ns_per_ms; + const release_time = t_release_time; const release_speed: f64 = t_release_velocity * t_draw_scale; var previous_time = dvui.currentWindow().frame_time_ns; try frameAfter(25); var previous_y = t_drawn_y; - var previous_speed: f32 = std.math.inf(f32); - var follows_gaps = true; // frameAfter draws at the current timestamp, then begins the next frame with this gap. // Alternating long and short gaps exposes a displacement applied one frame late. for ([_]u64{ 8, 25, 8, 25, 8, 25, 8, 25 }) |next_gap| { @@ -424,14 +424,10 @@ test "coast displacement follows each frame gap" { (@exp(-tuning.decay * from_s) - @exp(-tuning.decay * to_s))); // Check what reaches the renderer against the release integral, including its decay. try std.testing.expectApproxEqAbs(expected, displacement, 0.0001); - const speed = displacement / gap_ms; std.debug.print("coast: gap={d:.0} ms, displacement={d:.6} pixels\n", .{ gap_ms, displacement }); - follows_gaps = follows_gaps and displacement > 0 and speed < previous_speed; previous_time = time; previous_y = t_drawn_y; - previous_speed = speed; } - try std.testing.expect(follows_gaps); } test "a coast requests another frame even when its timestamp has not advanced" { From de8b50d4b01b247f7da4eb0c8bec148c13d68481 Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 08:50:40 -0500 Subject: [PATCH 4/4] linux: work-362 (codex) Agent-Team: linux Worker: codex --- core/widgets/ScrollArea.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/widgets/ScrollArea.zig b/core/widgets/ScrollArea.zig index 1d0d2acde..f4d5af278 100644 --- a/core/widgets/ScrollArea.zig +++ b/core/widgets/ScrollArea.zig @@ -284,13 +284,14 @@ fn scrollBy(self: *ScrollArea, by: dvui.Point) void { var t_viewport_y: f32 = 0; var t_release_velocity: f32 = 0; -// Written by swipe on the test thread before it draws the release frame. +/// Written by swipe on the test thread before it draws the release frame. var t_release_time: i128 = 0; var t_use_dvui: bool = false; /// Where a child of the area is drawn this frame (physical y, as the renderer would place it), /// and whether the renderer snaps there. var t_drawn_y: f32 = 0; var t_raw_y: f32 = 0; +/// Written by testFrame on the test thread from the child's physical draw scale. var t_draw_scale: f32 = 1; var t_snapping: bool = false; /// Lay the area out as a column of `t_rows` rows, `t_top` points below the window's top, and @@ -415,7 +416,6 @@ test "coast displacement follows each frame gap" { // Alternating long and short gaps exposes a displacement applied one frame late. for ([_]u64{ 8, 25, 8, 25, 8, 25, 8, 25 }) |next_gap| { const time = dvui.currentWindow().frame_time_ns; - const gap_ms: f32 = @as(f32, @floatFromInt(time - previous_time)) / std.time.ns_per_ms; try frameAfter(next_gap); const displacement = previous_y - t_drawn_y; const from_s = @as(f64, @floatFromInt(previous_time - release_time)) / std.time.ns_per_s; @@ -424,7 +424,6 @@ test "coast displacement follows each frame gap" { (@exp(-tuning.decay * from_s) - @exp(-tuning.decay * to_s))); // Check what reaches the renderer against the release integral, including its decay. try std.testing.expectApproxEqAbs(expected, displacement, 0.0001); - std.debug.print("coast: gap={d:.0} ms, displacement={d:.6} pixels\n", .{ gap_ms, displacement }); previous_time = time; previous_y = t_drawn_y; }