diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-21 22:37:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 297e14cfc36e4613a8c1cb3b995597d0a9c2873b (patch) | |
| tree | ad2ff7bd7a5869b2e45fe6098825a8ac35598703 /src/macos.zig | |
| parent | 9070942b29bd10dddcdecdb0e88ba0fb40608467 (diff) | |
| parent | 44ce573e15c773cf5bb0d42941a143b63e44d900 (diff) | |
| download | pardes-297e14cfc36e4613a8c1cb3b995597d0a9c2873b.tar.gz pardes-297e14cfc36e4613a8c1cb3b995597d0a9c2873b.zip | |
Merge the macOS first-class-host work
Diffstat (limited to 'src/macos.zig')
| -rw-r--r-- | src/macos.zig | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/src/macos.zig b/src/macos.zig index 79036c3e..d263cfc6 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -106,6 +106,7 @@ const color_default: u32 = 0x01000000; const color_indexed: u32 = 0x02000000; const cell_flag_default: u8 = 1; const cell_flag_tagline: u8 = 2; +const cell_flag_hover: u8 = 4; const scene_flag_crt: u32 = 1 << 0; const scene_flag_ripple: u32 = 1 << 1; const scene_flag_glitch: u32 = 1 << 2; @@ -730,6 +731,16 @@ fn initCore(runtime: ?*const Runtime, cols_arg: u16, rows_arg: u16) !void { const core = try pardes.Pardes.init(allocs.pardes, opts); errdefer core.deinit(); + // The workspace tag row is the macOS menu bar's business here (see + // docs/macos.md): the build option says whether this shell keeps the + // acme topbar or hands its commands to native chrome. The e2e harness + // opts back in with PARDES_WORKSPACE_TAG=1 — its scripts speak to a + // grid that has the row. Applied before the first resize so the layout + // has never seen it the other way. + core.settings.workspace_tag = if (std.c.getenv("PARDES_WORKSPACE_TAG")) |v| + std.mem.eql(u8, std.mem.span(v), "1") + else + @import("pardes_config").workspace_tag; core.native_images = true; var prompt_rcs = host_io.Shell.prepare(); @@ -917,7 +928,7 @@ export fn pardes_gui_tagline_font_percent() u8 { } export fn pardes_tagline_band_offset(row: u16, canvas_h: f32, cell_h: u32, tagline_h: u32) u32 { - return pardes.taglineBandOffset(row, canvas_h, cell_h, tagline_h); + return pardes.taglineBandOffset(row, canvas_h, cell_h, tagline_h, if (state) |*st| st.core.settings.workspace_tag else true); } export fn pardes_topbar_pane_border_px(cell_h: u32, tagline_h: u32) u32 { @@ -942,6 +953,11 @@ export fn pardes_topbar_pane_border_rgb() u32 { return @as(u32, rgb[0]) << 16 | @as(u32, rgb[1]) << 8 | rgb[2]; } +export fn pardes_tag_active_bg() u32 { + const st = state orelse return color_default; + const rgb = st.core.chromeTheme().tag_active_bg; + return @as(u32, rgb[0]) << 16 | @as(u32, rgb[1]) << 8 | rgb[2]; +} export fn pardes_tagline_bg() u32 { const st = state orelse return color_default; const rgb = st.core.chromeTheme().tag_bg; @@ -1756,12 +1772,18 @@ fn clearFrame(st: *State) void { st.panel_diff_len = 0; } -fn blankTagCells(cells: []Cell, cols: u16, rows: u16, layers: []const pardes.TagLayer) void { +fn blankTagCells(cells: []Cell, cols: u16, rows: u16, source: []const pardes.Cell, layers: []const pardes.TagLayer) void { for (layers) |layer| { if (layer.cols == 0 or layer.viewport.y >= rows or layer.viewport.x >= cols) continue; const start = @as(usize, layer.viewport.y) * cols + layer.viewport.x; const count = @min(layer.viewport.w, cols - layer.viewport.x); - for (cells[start..][0..count]) |*cell| cell.* = encodeCell(.{}); + for (cells[start..][0..count], source[start..][0..count]) |*cell, was| { + // The tag text is the layer's to draw, but a look-hover on a tag + // word is a canonical fact: carry the hover bit through so the + // glass overlay still finds the affordance rect. + cell.* = encodeCell(.{}); + cell.flags |= @as(u8, @intFromBool(was.hover)) * cell_flag_hover; + } } } @@ -1800,7 +1822,7 @@ fn presentFrame(ctx: ?*anyopaque, surface: *const pardes.Surface) void { defer tz_cells.end(); for (surface.cells, st.cells[0..count]) |cell, *out| out.* = encodeCell(cell); } - blankTagCells(st.cells[0..count], surface.cols, surface.rows, &surface.tag_layers); + blankTagCells(st.cells[0..count], surface.cols, surface.rows, surface.cells, &surface.tag_layers); collectPanelDiff(st, surface, count); collectImages(st, surface); collectPanelTracks(st, surface); @@ -1831,7 +1853,7 @@ fn collectPanelDiff(st: *State, surface: *const pardes.Surface, count: usize) vo } for (surface.previous_cells, st.previous_cells[0..count]) |cell, *out| out.* = encodeCell(cell); - blankTagCells(st.previous_cells[0..count], surface.cols, surface.rows, surface.previous_tag_layers); + blankTagCells(st.previous_cells[0..count], surface.cols, surface.rows, surface.previous_cells, surface.previous_tag_layers); for (surface.cell_diffs, st.changed_cells[0..count]) |diff, *out| out.* = encodeChanged(diff); st.panel_diff_len = count; @@ -2395,8 +2417,8 @@ fn encodeCell(cell: pardes.Cell) Cell { .fg = encodeColor(cell.style.fg), .bg = encodeColor(cell.style.bg), .attrs = encodeAttrs(cell.style), + .flags = encodeCellFlags(cell.default, cell.style.font_role, cell.hover), .len = if (cell.default) 1 else cell.len, - .flags = encodeCellFlags(cell.default, cell.style.font_role), }; if (cell.default) out.text[0] = ' ' @@ -2418,9 +2440,10 @@ fn encodeAttrs(style: pardes.CellStyle) u16 { return attrs; } -fn encodeCellFlags(default: bool, role: pardes.FontRole) u8 { +fn encodeCellFlags(default: bool, role: pardes.FontRole, hover: bool) u8 { return @as(u8, @intFromBool(default)) * cell_flag_default | - @as(u8, @intFromBool(role == .tagline)) * cell_flag_tagline; + @as(u8, @intFromBool(role == .tagline)) * cell_flag_tagline | + @as(u8, @intFromBool(hover)) * cell_flag_hover; } fn noteScrollVelocity(st: *State, delta_rows: f32, delta_cols: f32) void { @@ -2566,7 +2589,8 @@ test "pardes.h declares every export the way it is defined" { try expectSameAbi(@TypeOf(c.pardes_animating), @TypeOf(pardes_animating)); try expectSameAbi(@TypeOf(c.pardes_gui_tagline_font_percent), @TypeOf(pardes_gui_tagline_font_percent)); try expectSameAbi(@TypeOf(c.pardes_tagline_band_offset), @TypeOf(pardes_tagline_band_offset)); - try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_px), @TypeOf(pardes_topbar_pane_border_px)); + try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_rgb), @TypeOf(pardes_topbar_pane_border_rgb)); + try expectSameAbi(@TypeOf(c.pardes_tag_active_bg), @TypeOf(pardes_tag_active_bg)); try expectSameAbi(@TypeOf(c.pardes_tagline_origin_col), @TypeOf(pardes_tagline_origin_col)); try expectSameAbi(@TypeOf(c.pardes_grid_col_at), @TypeOf(pardes_grid_col_at)); try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_rgb), @TypeOf(pardes_topbar_pane_border_rgb)); @@ -2658,6 +2682,7 @@ test "pardes.h matches the Zig boundary" { try expectEqual(@as(u32, c.PARDES_COLOR_INDEXED), color_indexed); try expectEqual(@as(u8, c.PARDES_CELL_DEFAULT), cell_flag_default); try expectEqual(@as(u8, c.PARDES_CELL_TAGLINE), cell_flag_tagline); + try expectEqual(@as(u8, c.PARDES_CELL_HOVER), cell_flag_hover); try expectEqual(@as(u16, c.PARDES_TOPBAR_H), pardes.TOPBAR_H); try expectEqual(@as(u32, c.PARDES_SCENE_CRT), scene_flag_crt); try expectEqual(@as(u32, c.PARDES_SCENE_RIPPLE), scene_flag_ripple); @@ -2723,9 +2748,10 @@ test "pardes.h matches the Zig boundary" { encodeAttrs(.{ .ul = .curly }), ); - try expectEqual(@as(u8, 0), encodeCellFlags(false, .body)); - try expectEqual(cell_flag_tagline, encodeCellFlags(false, .tagline)); - try expectEqual(cell_flag_default | cell_flag_tagline, encodeCellFlags(true, .tagline)); + try expectEqual(@as(u8, 0), encodeCellFlags(false, .body, false)); + try expectEqual(cell_flag_hover, encodeCellFlags(false, .body, true)); + try expectEqual(cell_flag_tagline, encodeCellFlags(false, .tagline, false)); + try expectEqual(cell_flag_default | cell_flag_tagline, encodeCellFlags(true, .tagline, false)); try expectEqual(pardes.config.gui_tagline_font_percent, pardes_gui_tagline_font_percent()); } @@ -2947,10 +2973,16 @@ test "mac tag layer ABI preserves logical capacity and physical grip" { try std.testing.expectEqual(@as(u32, 11), pardes_tag_layer_value(0, 8)); try std.testing.expectEqual(@as(u8, 'Z'), pardes_tag_layer_cells(0).?[11].text[0]); var canonical: [24]Cell = @splat(encodeCell(.{ .text = .{'G'} ++ .{0} ** 6, .len = 1, .default = false })); - blankTagCells(&canonical, 8, 3, &core.surface.tag_layers); + // A look-hover under the layer is a canonical fact and survives the blank; + // the glyph it marks does not, because the layer redraws that text itself. + var source: [24]pardes.Cell = @splat(.{}); + source[11].hover = true; + blankTagCells(&canonical, 8, 3, &source, &core.surface.tag_layers); try std.testing.expectEqual(@as(u8, 'G'), canonical[9].text[0]); try std.testing.expectEqual(@as(u8, 'G'), canonical[18].text[0]); try std.testing.expectEqual(encodeCell(.{}).len, canonical[10].len); + try std.testing.expectEqual(@as(u8, 0), canonical[10].flags & cell_flag_hover); + try std.testing.expectEqual(cell_flag_hover, canonical[11].flags & cell_flag_hover); } test "mac window blur builtin reaches the ABI without changing opacity" { |
