a6f3c66c
refactor: the removed-tile flag is not called gone
a73x 2026-09-01 09:40
Commit message
src/tui/wall_pump.zig
| Old | New | ||
|---|---|---|---|
| @@ -43,7 +43,7 @@ pub fn copySelection( | |||
| 43 | /// this tile has not been forgotten. `paint_mu` is held for the whole paint. | 43 | /// this tile has not been forgotten. `paint_mu` is held for the whole paint. |
| 44 | pub fn tilePaintBegin(ctx: ?*anyopaque) bool { | 44 | pub fn tilePaintBegin(ctx: ?*anyopaque) bool { |
| 45 | const t: *Tile = @ptrCast(@alignCast(ctx.?)); | 45 | const t: *Tile = @ptrCast(@alignCast(ctx.?)); |
| 46 | if (t.gone.load(.acquire)) return false; | 46 | if (t.removed.load(.acquire)) return false; |
| 47 | t.shared.paint_mu.lock(); | 47 | t.shared.paint_mu.lock(); |
| 48 | // Under the lock, not before: a pump that loses the race paints over the | 48 | // Under the lock, not before: a pump that loses the race paints over the |
| 49 | // popup, and `picker_stamp` suppresses the repaint that would repair it. | 49 | // popup, and `picker_stamp` suppresses the repaint that would repair it. |
| @@ -216,9 +216,9 @@ pub fn askOn(target: client.Target, shared: *const Shared) client.Target { | |||
| 216 | fn dial(alloc: std.mem.Allocator, t: *Tile, target_in: client.Target) ?client.Transport { | 216 | fn dial(alloc: std.mem.Allocator, t: *Tile, target_in: client.Target) ?client.Transport { |
| 217 | var target = askOn(target_in, t.shared); | 217 | var target = askOn(target_in, t.shared); |
| 218 | var backoff_ms: u64 = 0; | 218 | var backoff_ms: u64 = 0; |
| 219 | // `gone` as well as `running`: a tile forgotten while retrying a dead | 219 | // `removed` as well as `running`: a tile forgotten while retrying a dead |
| 220 | // host must not keep a thread and a backoff alive. | 220 | // host must not keep a thread and a backoff alive. |
| 221 | while (t.shared.running.load(.acquire) and !t.gone.load(.acquire)) { | 221 | while (t.shared.running.load(.acquire) and !t.removed.load(.acquire)) { |
| 222 | // The reason is not kept — the picker row polls the same host and | 222 | // The reason is not kept — the picker row polls the same host and |
| 223 | // carries it. The PID is: only it separates a refusal from a dead box. | 223 | // carries it. The PID is: only it separates a refusal from a dead box. |
| 224 | var d: handoff.Dial = .{}; | 224 | var d: handoff.Dial = .{}; |
| @@ -241,7 +241,7 @@ fn dial(alloc: std.mem.Allocator, t: *Tile, target_in: client.Target) ?client.Tr | |||
| 241 | // Sliced sleep so quit is never behind a full backoff. | 241 | // Sliced sleep so quit is never behind a full backoff. |
| 242 | var slept: u64 = 0; | 242 | var slept: u64 = 0; |
| 243 | while (slept < backoff_ms and t.shared.running.load(.acquire) and | 243 | while (slept < backoff_ms and t.shared.running.load(.acquire) and |
| 244 | !t.gone.load(.acquire)) : (slept += 50) | 244 | !t.removed.load(.acquire)) : (slept += 50) |
| 245 | { | 245 | { |
| 246 | std.Thread.sleep(50 * std.time.ns_per_ms); | 246 | std.Thread.sleep(50 * std.time.ns_per_ms); |
| 247 | } | 247 | } |
| @@ -501,9 +501,9 @@ pub fn pumpTile(t: *Tile) void { | |||
| 501 | // What this tile's paint is worth: while it matches the wall's | 501 | // What this tile's paint is worth: while it matches the wall's |
| 502 | // generation the terminal still holds what this thread drew. | 502 | // generation the terminal still holds what this thread drew. |
| 503 | var painted_gen = t.shared.repaint_gen.load(.acquire); | 503 | var painted_gen = t.shared.repaint_gen.load(.acquire); |
| 504 | // `gone` ends this thread exactly as `running` does: the defers free the | 504 | // `removed` ends this thread exactly as `running` does: the defers free the |
| 505 | // daemon slot and nothing else. The session goes on running. | 505 | // daemon slot and nothing else. The session goes on running. |
| 506 | outer: while (t.shared.running.load(.acquire) and !t.gone.load(.acquire)) { | 506 | outer: while (t.shared.running.load(.acquire) and !t.removed.load(.acquire)) { |
| 507 | // The link, the doorbell, then one fd per live agent channel — one | 507 | // The link, the doorbell, then one fd per live agent channel — one |
| 508 | // poll, because the transport has exactly one owning thread. | 508 | // poll, because the transport has exactly one owning thread. |
| 509 | var fdbuf: [3 + agent_locals.len]std.posix.pollfd = undefined; | 509 | var fdbuf: [3 + agent_locals.len]std.posix.pollfd = undefined; |
src/tui/wall_test_wall.zig
| Old | New | ||
|---|---|---|---|
| @@ -1043,7 +1043,7 @@ test "setFocus: the marker moves between two tiles with no pump to move it" { | |||
| 1043 | .state = .refused, | 1043 | .state = .refused, |
| 1044 | .alive = std.atomic.Value(bool).init(false), | 1044 | .alive = std.atomic.Value(bool).init(false), |
| 1045 | }; | 1045 | }; |
| 1046 | // Forgotten, not merely dead: `x` clears `present` and sets `gone` | 1046 | // Forgotten, not merely dead: `x` clears `present` and sets `removed` |
| 1047 | // together, and a bar painted into a rect the layout no longer owns | 1047 | // together, and a bar painted into a rect the layout no longer owns |
| 1048 | // writes over a neighbour. | 1048 | // writes over a neighbour. |
| 1049 | tiles[2] = Tile{ | 1049 | tiles[2] = Tile{ |
| @@ -1055,7 +1055,7 @@ test "setFocus: the marker moves between two tiles with no pump to move it" { | |||
| 1055 | .wake_w = -1, | 1055 | .wake_w = -1, |
| 1056 | .state = .exited, | 1056 | .state = .exited, |
| 1057 | .alive = std.atomic.Value(bool).init(false), | 1057 | .alive = std.atomic.Value(bool).init(false), |
| 1058 | .gone = std.atomic.Value(bool).init(true), | 1058 | .removed = std.atomic.Value(bool).init(true), |
| 1059 | }; | 1059 | }; |
| 1060 | shared.sel = 0; | 1060 | shared.sel = 0; |
| 1061 | 1061 | ||
| @@ -1122,9 +1122,9 @@ test "a vanished tile is stepped over" { | |||
| 1122 | // through stepPresent, and a click's rectHit and the Ctrl-\ l and 1-9 | 1122 | // through stepPresent, and a click's rectHit and the Ctrl-\ l and 1-9 |
| 1123 | // chords check the same present array a vanish clears. Tiles are never | 1123 | // chords check the same present array a vanish clears. Tiles are never |
| 1124 | // compacted — pumps hold pointers into the array — so a vanished tile is | 1124 | // compacted — pumps hold pointers into the array — so a vanished tile is |
| 1125 | // a hole focus steps over, not a gap it fills. gone and present clear | 1125 | // a hole focus steps over, not a gap it fills. `removed` and present clear |
| 1126 | // together in `vanishTile`, so stepping on present is stepping past | 1126 | // together in `vanishTile`, so stepping on present is stepping past |
| 1127 | // gone. | 1127 | // `removed`. |
| 1128 | const mid = [_]bool{ true, false, true }; | 1128 | const mid = [_]bool{ true, false, true }; |
| 1129 | try std.testing.expectEqual(@as(?usize, 2), wv.stepPresent(&mid, 0, true)); | 1129 | try std.testing.expectEqual(@as(?usize, 2), wv.stepPresent(&mid, 0, true)); |
| 1130 | try std.testing.expectEqual(@as(?usize, 0), wv.stepPresent(&mid, 2, true)); | 1130 | try std.testing.expectEqual(@as(?usize, 0), wv.stepPresent(&mid, 2, true)); |
src/tui/wallview.zig
| Old | New | ||
|---|---|---|---|
| @@ -257,8 +257,8 @@ pub const Tile = struct { | |||
| 257 | pump_done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), | 257 | pump_done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), |
| 258 | /// Forgotten by `Ctrl-\ x`: off the wall, and off the wire as soon as the | 258 | /// Forgotten by `Ctrl-\ x`: off the wall, and off the wire as soon as the |
| 259 | /// pump notices. "Remove is detach" — the session is untouched. Distinct | 259 | /// pump notices. "Remove is detach" — the session is untouched. Distinct |
| 260 | /// from `alive`: a gone tile the user removed, a dead one still narrates. | 260 | /// from `alive`: a tile the user removed, a dead one still narrates. |
| 261 | gone: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), | 261 | removed: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), |
| 262 | /// The doorbell. The keyboard writes one byte here to wake this tile's | 262 | /// The doorbell. The keyboard writes one byte here to wake this tile's |
| 263 | /// pump; the bytes themselves carry nothing, the mailbox does. Both | 263 | /// pump; the bytes themselves carry nothing, the mailbox does. Both |
| 264 | /// ends are non-blocking, which is what makes a bell that is already | 264 | /// ends are non-blocking, which is what makes a bell that is already |
| @@ -436,7 +436,7 @@ pub fn endKey(t: *Tile, now: i64) EndKey { | |||
| 436 | // Nothing is asked of the daemon: the session keeps running and the | 436 | // Nothing is asked of the daemon: the session keeps running and the |
| 437 | // host's next list births the tile back. A pending pane has no pump. | 437 | // host's next list births the tile back. A pending pane has no pump. |
| 438 | if (!t.alive.load(.acquire)) return .drop; | 438 | if (!t.alive.load(.acquire)) return .drop; |
| 439 | // A pump on its FIRST dial is parked in `dial`, which polls `gone` and | 439 | // A pump on its FIRST dial is parked in `dial`, which polls `removed` and |
| 440 | // never `ask`: the key would be swallowed until the box came back, then | 440 | // never `ask`: the key would be swallowed until the box came back, then |
| 441 | // end the session. Birthing onto an `unreachable` row is a designed path. | 441 | // end the session. Birthing onto an `unreachable` row is a designed path. |
| 442 | if (!t.ever_up.load(.acquire) and awaitingSession(t) != null) return .drop; | 442 | if (!t.ever_up.load(.acquire) and awaitingSession(t) != null) return .drop; |
| @@ -554,11 +554,11 @@ pub fn paintLabelLocked(t: *Tile) void { | |||
| 554 | 554 | ||
| 555 | // The bars of the tiles whose pump has ended. Caller holds `paint_mu`. | 555 | // The bars of the tiles whose pump has ended. Caller holds `paint_mu`. |
| 556 | // A dead pump answers no doorbell, so the keyboard is the only thread that | 556 | // A dead pump answers no doorbell, so the keyboard is the only thread that |
| 557 | // can move its focus marker. `gone` rather than `present`, because a focus | 557 | // can move its focus marker. `removed` rather than `present`, because a focus |
| 558 | // move has no `present` slice and a vanished tile's rect is a neighbour's. | 558 | // move has no `present` slice and a vanished tile's rect is a neighbour's. |
| 559 | pub fn paintDeadBarsLocked(tiles: []Tile) void { | 559 | pub fn paintDeadBarsLocked(tiles: []Tile) void { |
| 560 | for (tiles) |*t| { | 560 | for (tiles) |*t| { |
| 561 | if (!t.alive.load(.acquire) and !t.gone.load(.acquire)) paintLabelLocked(t); | 561 | if (!t.alive.load(.acquire) and !t.removed.load(.acquire)) paintLabelLocked(t); |
| 562 | } | 562 | } |
| 563 | } | 563 | } |
| 564 | 564 | ||
| @@ -750,7 +750,7 @@ pub fn paintEmptyWallLocked(shared: *Shared) void { | |||
| 750 | /// focus hand-off is written once. `to` overrides where the focus goes. | 750 | /// focus hand-off is written once. `to` overrides where the focus goes. |
| 751 | pub fn vanishTile(tiles: []Tile, present: []bool, shared: *Shared, i: usize, to: ?usize) void { | 751 | pub fn vanishTile(tiles: []Tile, present: []bool, shared: *Shared, i: usize, to: ?usize) void { |
| 752 | present[i] = false; | 752 | present[i] = false; |
| 753 | tiles[i].gone.store(true, .release); | 753 | tiles[i].removed.store(true, .release); |
| 754 | ring(&tiles[i]); | 754 | ring(&tiles[i]); |
| 755 | shared.tree.remove(@intCast(i)); | 755 | shared.tree.remove(@intCast(i)); |
| 756 | // Through `setFocus`, never by writing `sel`: a focus that arrives | 756 | // Through `setFocus`, never by writing `sel`: a focus that arrives |