diff options
Diffstat (limited to 'src/macos.zig')
| -rw-r--r-- | src/macos.zig | 99 |
1 files changed, 42 insertions, 57 deletions
diff --git a/src/macos.zig b/src/macos.zig index 70469618..b472e6cc 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -12,6 +12,7 @@ const colors = @import("colors.zig"); const look = @import("look.zig"); const message = pardes.Messages.Message; const layout = @import("layout.zig"); +const animation = @import("animation.zig"); const file_watch = @import("file_watch.zig"); const image = if (pardes.pdf_enabled) @import("image.zig") else struct {}; const host_io = @import("host_io.zig"); @@ -72,8 +73,8 @@ pub const Scene = extern struct { frame: u32 = 0, }; -pub const PanelBox = layout.Box; -pub const PanelTrack = layout.Track; +pub const PanelBox = animation.Box; +pub const PanelTrack = animation.Track; pub const Image = extern struct { serial: u32, @@ -111,13 +112,11 @@ 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; const scene_frame_hz: u32 = 60; const scene_wrap_ns: u64 = 4096 * std.time.ns_per_s; -const max_tick_catch_up_ns: u64 = 4 * pardes.layout.Animation.frame_ns; +const max_tick_catch_up_ns: u64 = 4 * pardes.animation.frame_ns; const theme_watch_pane: u8 = @intCast(pardes.MAX_PANES); const watch_slot_count = pardes.MAX_PANES + 1; @@ -873,16 +872,16 @@ export fn pardes_should_quit() bool { return st.core.quit; } -fn encodeSceneEffects(effects: layout.SceneEffect) u32 { +// The Metal shell keeps its own CRT kernel: the GUI's bundled post pass, as +// a flag. +fn encodeSceneEffects(post: *const pardes.config.Runtime.Post) u32 { var flags: u32 = 0; - if (effects.crt) flags |= scene_flag_crt; - if (effects.ripple) flags |= scene_flag_ripple; - if (effects.glitch) flags |= scene_flag_glitch; + if (post.has(.crt)) flags |= scene_flag_crt; return flags; } fn currentSceneFlags(st: *const State) u32 { - return encodeSceneEffects(st.core.settings.scene_effects); + return encodeSceneEffects(&st.core.settings.post); } fn advanceSceneClock(st: *State, elapsed_ns: u64) void { @@ -893,18 +892,18 @@ const TickSpend = struct { elapsed_ns: u64, steps: u32, bank_ns: u64 }; fn spendTickTime(previous_ns: u64, now_ns: u64, bank_ns: u64) TickSpend { const measured = if (previous_ns == 0 or now_ns <= previous_ns) - pardes.layout.Animation.frame_ns + pardes.animation.frame_ns else now_ns - previous_ns; const elapsed = @min(measured, max_tick_catch_up_ns); var bank = bank_ns +| elapsed; var steps: u32 = 0; - while (bank >= pardes.layout.Animation.frame_ns) : (steps += 1) bank -= pardes.layout.Animation.frame_ns; + while (bank >= pardes.animation.frame_ns) : (steps += 1) bank -= pardes.animation.frame_ns; return .{ .elapsed_ns = elapsed, .steps = steps, .bank_ns = bank }; } test "the animation clock spends real time, not callbacks" { - const frame = pardes.layout.Animation.frame_ns; + const frame = pardes.animation.frame_ns; const expectEqual = std.testing.expectEqual; const first = spendTickTime(0, 999 * std.time.ns_per_s, 0); @@ -1904,25 +1903,22 @@ fn clearFrame(st: *State) void { st.panel_diff_len = 0; } -fn blankTagCells(cells: []Cell, cols: u16, rows: u16, source: []const pardes.Cell, layers: []const pardes.TagLayer) void { +fn blankTagCells(cells: []Cell, cols: u16, rows: u16, source: []const pardes.Cell, layers: []const pardes.Layer) void { for (layers) |layer| { - if (layer.cols == 0 or layer.viewport.y >= rows or layer.viewport.x >= cols) continue; + if (layer.rows == 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], 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; - } + // The tag text is the layer's to draw. (The look-hover's glass + // rect went with the core's hover bit, stage 8's follow-up.) + _ = source; + for (cells[start..][0..count]) |*cell| cell.* = encodeCell(.{}); } } fn tagCursorCovered(core: *const pardes.Pardes) bool { const cursor = core.surface.cursor orelse return false; for (core.surface.tag_layers) |layer| { - if (layer.cols != 0 and cursor.y == layer.viewport.y and cursor.x >= layer.viewport.x and + if (layer.rows != 0 and cursor.y == layer.viewport.y and cursor.x >= layer.viewport.x and cursor.x - layer.viewport.x < layer.viewport.w) return true; } return false; @@ -2164,7 +2160,7 @@ export fn pardes_tag_layer_value(index: u32, field: u32) u32 { 5 => layer.viewport.y, 6 => layer.viewport.w, 7 => layer.viewport.h, - 8 => if (layer.cursor) |c| c.col else 65535, + 8 => if (layer.cursor) |c| c.x else 65535, 9 => if (layer.cursor) |c| @intFromBool(c.bar) else 0, 10 => @as(u32, layer.bg[0]) << 16 | @as(u32, layer.bg[1]) << 8 | layer.bg[2], 11 => @intFromBool(layer.kind == .pane and st.core.settings.tag_bottom), @@ -2182,7 +2178,7 @@ export fn pardes_tag_layer_cells(index: u32) ?[*]const Cell { const st = &(state orelse return null); if (index >= pardes.MAX_TAG_LAYERS) return null; const layer = &st.core.surface.tag_layers[index]; - if (layer.cols == 0) return null; + if (layer.rows == 0) return null; const cells = &st.tag_cells[index]; if (cells.len != layer.cells.len) cells.* = st.gpa.realloc(cells.*, layer.cells.len) catch return null; for (layer.cells, cells.*) |source, *out| out.* = encodeCell(source); @@ -2619,7 +2615,7 @@ 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), + .flags = encodeCellFlags(cell.default, cell.style.font_role, false), .len = if (cell.default) 1 else cell.len, }; if (cell.default) @@ -2888,23 +2884,21 @@ test "pardes.h matches the Zig boundary" { 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); - try expectEqual(@as(u32, c.PARDES_SCENE_GLITCH), scene_flag_glitch); - try expectEqual(@as(u8, c.PARDES_PANEL_OPENING), @intFromEnum(layout.Phase.opening)); - try expectEqual(@as(u8, c.PARDES_PANEL_MOVING), @intFromEnum(layout.Phase.moving)); - try expectEqual(@as(u8, c.PARDES_PANEL_CLOSING), @intFromEnum(layout.Phase.closing)); - try expectEqual(@as(u8, c.PARDES_PANEL_OFF), @intFromEnum(layout.Transition.off)); - try expectEqual(@as(u8, c.PARDES_PANEL_SLIDE), @intFromEnum(layout.Transition.slide)); - try expectEqual(@as(u8, c.PARDES_PANEL_ZOOM), @intFromEnum(layout.Transition.zoom)); - try expectEqual(@as(u8, c.PARDES_PANEL_DISSOLVE), @intFromEnum(layout.Transition.dissolve)); - try expectEqual(@as(u8, c.PARDES_PANEL_ASCII), @intFromEnum(layout.Transition.ascii)); - try expectEqual(@as(u8, c.PARDES_PANEL_VERTICAL), @intFromEnum(layout.Transition.vertical)); - try expectEqual(@as(u8, c.PARDES_PANEL_EDGES), @intFromEnum(layout.Transition.edges)); - try expectEqual(@as(u8, c.PARDES_PANEL_FALL), @intFromEnum(layout.Transition.fall)); - try expectEqual(@as(u8, c.PARDES_PANEL_WAVE), @intFromEnum(layout.Transition.wave)); - try expectEqual(@as(u8, c.PARDES_PANEL_CURTAIN), @intFromEnum(layout.Transition.curtain)); - try expectEqual(@as(u8, c.PARDES_PANEL_SCRAMBLE), @intFromEnum(layout.Transition.scramble)); - try expectEqual(@as(u8, c.PARDES_PANEL_TYPEWRITER), @intFromEnum(layout.Transition.typewriter)); + try expectEqual(@as(u8, c.PARDES_PANEL_OPENING), @intFromEnum(animation.Phase.opening)); + try expectEqual(@as(u8, c.PARDES_PANEL_MOVING), @intFromEnum(animation.Phase.moving)); + try expectEqual(@as(u8, c.PARDES_PANEL_CLOSING), @intFromEnum(animation.Phase.closing)); + try expectEqual(@as(u8, c.PARDES_PANEL_OFF), @intFromEnum(animation.Transition.off)); + try expectEqual(@as(u8, c.PARDES_PANEL_SLIDE), @intFromEnum(animation.Transition.slide)); + try expectEqual(@as(u8, c.PARDES_PANEL_ZOOM), @intFromEnum(animation.Transition.zoom)); + try expectEqual(@as(u8, c.PARDES_PANEL_DISSOLVE), @intFromEnum(animation.Transition.dissolve)); + try expectEqual(@as(u8, c.PARDES_PANEL_ASCII), @intFromEnum(animation.Transition.ascii)); + try expectEqual(@as(u8, c.PARDES_PANEL_VERTICAL), @intFromEnum(animation.Transition.vertical)); + try expectEqual(@as(u8, c.PARDES_PANEL_EDGES), @intFromEnum(animation.Transition.edges)); + try expectEqual(@as(u8, c.PARDES_PANEL_FALL), @intFromEnum(animation.Transition.fall)); + try expectEqual(@as(u8, c.PARDES_PANEL_WAVE), @intFromEnum(animation.Transition.wave)); + try expectEqual(@as(u8, c.PARDES_PANEL_CURTAIN), @intFromEnum(animation.Transition.curtain)); + try expectEqual(@as(u8, c.PARDES_PANEL_SCRAMBLE), @intFromEnum(animation.Transition.scramble)); + try expectEqual(@as(u8, c.PARDES_PANEL_TYPEWRITER), @intFromEnum(animation.Transition.typewriter)); try expectEqual(@as(u21, c.PARDES_KEY_ENTER), pardes.Key.enter); try expectEqual(@as(u21, c.PARDES_KEY_ESCAPE), pardes.Key.escape); @@ -2960,14 +2954,10 @@ test "pardes.h matches the Zig boundary" { test "scene effect flags and display clock are compact and independent" { const expectEqual = std.testing.expectEqual; - try expectEqual(@as(u32, 0), encodeSceneEffects(.{})); - try expectEqual(scene_flag_crt, encodeSceneEffects(.{ .crt = true })); - try expectEqual(scene_flag_ripple, encodeSceneEffects(.{ .ripple = true })); - try expectEqual(scene_flag_glitch, encodeSceneEffects(.{ .glitch = true })); - try expectEqual( - scene_flag_crt | scene_flag_ripple | scene_flag_glitch, - encodeSceneEffects(.{ .crt = true, .ripple = true, .glitch = true }), - ); + var post: pardes.config.Runtime.Post = .{}; + try expectEqual(@as(u32, 0), encodeSceneEffects(&post)); + _ = post.set(.crt, "", true); + try expectEqual(scene_flag_crt, encodeSceneEffects(&post)); var st: State = undefined; st.scene_ns = scene_wrap_ns - (std.time.ns_per_ms * 5); @@ -3155,7 +3145,7 @@ test "mac tag layer ABI preserves logical capacity and physical grip" { logical[11].text[0] = 'Z'; logical[11].len = 1; logical[11].default = false; - core.surface.tag_layers[0] = .{ .kind = .pane, .id = 1, .serial = 42, .viewport = .{ .x = 2, .y = 1, .w = 6, .h = 1 }, .cols = 12, .cells = &logical, .bg = .{ 0x12, 0x34, 0x56 }, .cursor = .{ .col = 11, .bar = true } }; + core.surface.tag_layers[0] = .{ .kind = .pane, .id = 1, .serial = 42, .viewport = .{ .x = 2, .y = 1, .w = 6, .h = 1 }, .cols = 12, .rows = 1, .cells = &logical, .bg = .{ 0x12, 0x34, 0x56 }, .cursor = .{ .x = 11, .y = 0, .bar = true } }; defer core.surface.tag_layers[0] = .{}; try std.testing.expectEqual(@as(u32, 12), pardes_tag_layer_value(0, 0)); try std.testing.expectEqual(@as(u32, 0x123456), pardes_tag_layer_value(0, 10)); @@ -3176,16 +3166,11 @@ 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 })); - // 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" { |
