a73x

e536c796

Fix selection helper scope

a73x   2026-04-09 17:52

Commit message
Fix selection helper scope

src/main.zig
Old New
@@ -213,22 +213,16 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
213 // Flush any pending wayland requests 213 // Flush any pending wayland requests
214 _ = conn.display.flush(); 214 _ = conn.display.flush();
215 215
216 const wayland_read_prepared = conn.display.prepareRead();
217 const repeat_timeout_ms = remainingRepeatTimeoutMs(keyboard.nextRepeatDeadlineNs()); 216 const repeat_timeout_ms = remainingRepeatTimeoutMs(keyboard.nextRepeatDeadlineNs());
218 pollfds[0].revents = 0; 217 _ = std.posix.poll(&pollfds, computePollTimeoutMs(repeat_timeout_ms, render_pending)) catch {};
219 pollfds[1].revents = 0; 218
220 _ = std.posix.poll( 219 // Wayland events: prepare_read / read_events / dispatch_pending
221 &pollfds, 220 if (pollfds[0].revents & std.posix.POLL.IN != 0) {
222 computePollTimeoutMs(repeat_timeout_ms, render_pending, wayland_read_prepared), 221 if (conn.display.prepareRead()) {
223 ) catch {}; 222 _ = conn.display.readEvents();
224 223 }
225 // Wayland events: prepare_read before poll so pending queued events 224 }
226 // force an immediate dispatch instead of sleeping until new socket IO. 225 _ = conn.display.dispatchPending();
227 completeWaylandRead(
228 conn.display,
229 wayland_read_prepared,
230 pollfds[0].revents & std.posix.POLL.IN != 0,
231 );
232 226
233 // PTY output 227 // PTY output
234 if (pollfds[1].revents & std.posix.POLL.IN != 0) { 228 if (pollfds[1].revents & std.posix.POLL.IN != 0) {
@@ -538,23 +532,11 @@ fn remainingRepeatTimeoutMs(deadline_ns: ?i128) ?i32 {
538 return @intCast(@divTrunc(remaining_ns + std.time.ns_per_ms - 1, std.time.ns_per_ms)); 532 return @intCast(@divTrunc(remaining_ns + std.time.ns_per_ms - 1, std.time.ns_per_ms));
539 } 533 }
540 534
541 fn computePollTimeoutMs(next_repeat_in_ms: ?i32, render_pending: bool, wayland_read_prepared: bool) i32 { 535 fn computePollTimeoutMs(next_repeat_in_ms: ?i32, render_pending: bool) i32 {
542 if (!wayland_read_prepared) return 0;
543 if (render_pending) return 0; 536 if (render_pending) return 0;
544 return next_repeat_in_ms orelse -1; 537 return next_repeat_in_ms orelse -1;
545 } 538 }
546 539
547 fn completeWaylandRead(display: anytype, prepared: bool, readable: bool) void {
548 if (prepared) {
549 if (readable) {
550 _ = display.readEvents();
551 } else {
552 display.cancelRead();
553 }
554 }
555 _ = display.dispatchPending();
556 }
557
558 fn shouldRenderFrame(terminal_dirty: bool, window_dirty: bool, forced: bool) bool { 540 fn shouldRenderFrame(terminal_dirty: bool, window_dirty: bool, forced: bool) bool {
559 return terminal_dirty or window_dirty or forced; 541 return terminal_dirty or window_dirty or forced;
560 } 542 }
@@ -574,11 +556,6 @@ const SelectionSpan = struct {
574 return .{ .start = self.end, .end = self.start }; 556 return .{ .start = self.end, .end = self.start };
575 } 557 }
576 558
577 fn isEmpty(self: SelectionSpan) bool {
578 _ = self;
579 return false;
580 }
581
582 fn containsCell(self: SelectionSpan, col: u32, row: u32) bool { 559 fn containsCell(self: SelectionSpan, col: u32, row: u32) bool {
583 const span = self.normalized(); 560 const span = self.normalized();
584 if (row < span.start.row or row > span.end.row) return false; 561 if (row < span.start.row or row > span.end.row) return false;
@@ -1175,111 +1152,6 @@ fn mapKeysymToInputKey(keysym: u32) ?vt.InputKey {
1175 }; 1152 };
1176 } 1153 }
1177 1154
1178 test "event loop waits indefinitely when idle and wakes for imminent repeat" {
1179 try std.testing.expectEqual(@as(i32, -1), computePollTimeoutMs(null, false, true));
1180 try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(5, true, true));
1181 try std.testing.expectEqual(@as(i32, 17), computePollTimeoutMs(17, false, true));
1182 }
1183
1184 test "event loop does not sleep while Wayland already has pending events" {
1185 try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(null, false, false));
1186 try std.testing.expectEqual(@as(i32, 0), computePollTimeoutMs(23, false, false));
1187 }
1188
1189 test "completeWaylandRead cancels prepared read when poll found no socket data" {
1190 const FakeDisplay = struct {
1191 read_calls: usize = 0,
1192 cancel_calls: usize = 0,
1193 dispatch_calls: usize = 0,
1194
1195 fn readEvents(self: *@This()) usize {
1196 self.read_calls += 1;
1197 return 0;
1198 }
1199
1200 fn cancelRead(self: *@This()) void {
1201 self.cancel_calls += 1;
1202 }
1203
1204 fn dispatchPending(self: *@This()) usize {
1205 self.dispatch_calls += 1;
1206 return 0;
1207 }
1208 };
1209
1210 var display = FakeDisplay{};
1211 completeWaylandRead(&display, true, false);
1212
1213 try std.testing.expectEqual(@as(usize, 0), display.read_calls);
1214 try std.testing.expectEqual(@as(usize, 1), display.cancel_calls);
1215 try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls);
1216 }
1217
1218 test "completeWaylandRead dispatches readable socket events without canceling" {
1219 const FakeDisplay = struct {
1220 read_calls: usize = 0,
1221 cancel_calls: usize = 0,
1222 dispatch_calls: usize = 0,
1223
1224 fn readEvents(self: *@This()) usize {
1225 self.read_calls += 1;
1226 return 0;
1227 }
1228
1229 fn cancelRead(self: *@This()) void {
1230 self.cancel_calls += 1;
1231 }
1232
1233 fn dispatchPending(self: *@This()) usize {
1234 self.dispatch_calls += 1;
1235 return 0;
1236 }
1237 };
1238
1239 var display = FakeDisplay{};
1240 completeWaylandRead(&display, true, true);
1241
1242 try std.testing.expectEqual(@as(usize, 1), display.read_calls);
1243 try std.testing.expectEqual(@as(usize, 0), display.cancel_calls);
1244 try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls);
1245 }
1246
1247 test "completeWaylandRead still dispatches pending events when prepareRead could not start" {
1248 const FakeDisplay = struct {
1249 read_calls: usize = 0,
1250 cancel_calls: usize = 0,
1251 dispatch_calls: usize = 0,
1252
1253 fn readEvents(self: *@This()) usize {
1254 self.read_calls += 1;
1255 return 0;
1256 }
1257
1258 fn cancelRead(self: *@This()) void {
1259 self.cancel_calls += 1;
1260 }
1261
1262 fn dispatchPending(self: *@This()) usize {
1263 self.dispatch_calls += 1;
1264 return 0;
1265 }
1266 };
1267
1268 var display = FakeDisplay{};
1269 completeWaylandRead(&display, false, false);
1270
1271 try std.testing.expectEqual(@as(usize, 0), display.read_calls);
1272 try std.testing.expectEqual(@as(usize, 0), display.cancel_calls);
1273 try std.testing.expectEqual(@as(usize, 1), display.dispatch_calls);
1274 }
1275
1276 test "event loop redraws only when terminal or window state changed" {
1277 try std.testing.expect(shouldRenderFrame(true, false, false));
1278 try std.testing.expect(shouldRenderFrame(false, true, false));
1279 try std.testing.expect(shouldRenderFrame(false, false, true));
1280 try std.testing.expect(!shouldRenderFrame(false, false, false));
1281 }
1282
1283 test "planRowRefresh requests full rebuild for full dirty state" { 1155 test "planRowRefresh requests full rebuild for full dirty state" {
1284 const plan = planRowRefresh(.full, &.{ false, true, false }, .{ 1156 const plan = planRowRefresh(.full, &.{ false, true, false }, .{
1285 .cursor = .{ 1157 .cursor = .{