e536c796
Fix selection helper scope
a73x 2026-04-09 17:52
diff --git a/src/main.zig b/src/main.zig index 0badb6e..efe47ad 100644 --- a/src/main.zig +++ b/src/main.zig @@ -213,22 +213,16 @@ fn runTerminal(alloc: std.mem.Allocator) !void { // Flush any pending wayland requests _ = conn.display.flush(); const wayland_read_prepared = conn.display.prepareRead(); const repeat_timeout_ms = remainingRepeatTimeoutMs(keyboard.nextRepeatDeadlineNs()); pollfds[0].revents = 0; pollfds[1].revents = 0; _ = std.posix.poll( &pollfds, computePollTimeoutMs(repeat_timeout_ms, render_pending, wayland_read_prepared), ) catch {}; // Wayland events: prepare_read before poll so pending queued events // force an immediate dispatch instead of sleeping until new socket IO. completeWaylandRead( conn.display, wayland_read_prepared, pollfds[0].revents & std.posix.POLL.IN != 0, ); _ = std.posix.poll(&pollfds, computePollTimeoutMs(repeat_timeout_ms, render_pending)) catch {}; // Wayland events: prepare_read / read_events / dispatch_pending if (pollfds[0].revents & std.posix.POLL.IN != 0) { if (conn.display.prepareRead()) { _ = conn.display.readEvents(); } } _ = conn.display.dispatchPending(); // PTY output if (pollfds[1].revents & std.posix.POLL.IN != 0) { @@ -538,23 +532,11 @@ fn remainingRepeatTimeoutMs(deadline_ns: ?i128) ?i32 { return @intCast(@divTrunc(remaining_ns + std.time.ns_per_ms - 1, std.time.ns_per_ms)); } fn computePollTimeoutMs(next_repeat_in_ms: ?i32, render_pending: bool, wayland_read_prepared: bool) i32 { if (!wayland_read_prepared) return 0; fn computePollTimeoutMs(next_repeat_in_ms: ?i32, render_pending: bool) i32 { if (render_pending) return 0; return next_repeat_in_ms orelse -1; } fn completeWaylandRead(display: anytype, prepared: bool, readable: bool) void { if (prepared) { if (readable) { _ = display.readEvents(); } else { display.cancelRead(); } } _ = display.dispatchPending(); } fn shouldRenderFrame(terminal_dirty: bool, window_dirty: bool, forced: bool) bool { return terminal_dirty or window_dirty or forced; } @@ -574,11 +556,6 @@ const SelectionSpan = struct { return .{ .start = self.end, .end = self.start }; } fn isEmpty(self: SelectionSpan) bool { _ = self; return false; } fn containsCell(self: SelectionSpan, col: u32, row: u32) bool { const span = self.normalized(); if (row < span.start.row or row > span.end.row) return false; @@ -1175,111 +1152,6 @@ fn mapKeysymToInputKey(keysym: u32) ?vt.InputKey { }; } test "event loop waits indefinitely when idle and wakes for imminent repeat" { try std.testing.expectEqual(@as(i32, -1), computePollTimeoutMs(null, false, true)); try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(5, true, true)); try std.testing.expectEqual(@as(i32, 17), computePollTimeoutMs(17, false, true)); } test "event loop does not sleep while Wayland already has pending events" { try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(null, false, false)); try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(23, false, false)); } test "completeWaylandRead cancels prepared read when poll found no socket data" { const FakeDisplay = struct { read_calls: usize = 0, cancel_calls: usize = 0, dispatch_calls: usize = 0, fn readEvents(self: *@This()) usize { self.read_calls += 1; return 0; } fn cancelRead(self: *@This()) void { self.cancel_calls += 1; } fn dispatchPending(self: *@This()) usize { self.dispatch_calls += 1; return 0; } }; var display = FakeDisplay{}; completeWaylandRead(&display, true, false); try std.testing.expectEqual(@as(usize, 0), display.read_calls); try std.testing.expectEqual(@as(usize, 1), display.cancel_calls); try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls); } test "completeWaylandRead dispatches readable socket events without canceling" { const FakeDisplay = struct { read_calls: usize = 0, cancel_calls: usize = 0, dispatch_calls: usize = 0, fn readEvents(self: *@This()) usize { self.read_calls += 1; return 0; } fn cancelRead(self: *@This()) void { self.cancel_calls += 1; } fn dispatchPending(self: *@This()) usize { self.dispatch_calls += 1; return 0; } }; var display = FakeDisplay{}; completeWaylandRead(&display, true, true); try std.testing.expectEqual(@as(usize, 1), display.read_calls); try std.testing.expectEqual(@as(usize, 0), display.cancel_calls); try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls); } test "completeWaylandRead still dispatches pending events when prepareRead could not start" { const FakeDisplay = struct { read_calls: usize = 0, cancel_calls: usize = 0, dispatch_calls: usize = 0, fn readEvents(self: *@This()) usize { self.read_calls += 1; return 0; } fn cancelRead(self: *@This()) void { self.cancel_calls += 1; } fn dispatchPending(self: *@This()) usize { self.dispatch_calls += 1; return 0; } }; var display = FakeDisplay{}; completeWaylandRead(&display, false, false); try std.testing.expectEqual(@as(usize, 0), display.read_calls); try std.testing.expectEqual(@as(usize, 0), display.cancel_calls); try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls); } test "event loop redraws only when terminal or window state changed" { try std.testing.expect(shouldRenderFrame(true, false, false)); try std.testing.expect(shouldRenderFrame(false, true, false)); try std.testing.expect(shouldRenderFrame(false, false, true)); 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 = .{