summaryrefslogtreecommitdiff
path: root/src/macos.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/macos.zig')
-rw-r--r--src/macos.zig102
1 files changed, 99 insertions, 3 deletions
diff --git a/src/macos.zig b/src/macos.zig
index 3a1926b5..5406fcb4 100644
--- a/src/macos.zig
+++ b/src/macos.zig
@@ -637,6 +637,7 @@ const State = struct {
core: *pardes.Pardes,
started: bool = false,
runtime: Runtime,
+ tag_cells: [pardes.MAX_TAG_LAYERS][]Cell = @splat(&.{}),
layer_cells: [pardes.MAX_PANES][]Cell = @splat(&.{}),
cells: []Cell = &.{},
previous_cells: []Cell = &.{},
@@ -780,6 +781,7 @@ export fn pardes_deinit() void {
st.file_watches.deinit(st.gpa);
if (st.cells.len > 0) st.gpa.free(st.cells);
for (st.layer_cells) |cells| st.gpa.free(cells);
+ for (st.tag_cells) |cells| st.gpa.free(cells);
if (st.previous_cells.len > 0) st.gpa.free(st.previous_cells);
if (st.changed_cells.len > 0) st.gpa.free(st.changed_cells);
if (st.images.len > 0) st.gpa.free(st.images);
@@ -1482,7 +1484,7 @@ export fn pardes_paste(text_ptr: ?[*]const u8, len: usize) void {
}
export fn pardes_mouse(button_arg: c_int, kind_arg: c_int, col: u16, row: u16, mods: u32) void {
- mouseWithHit(button_arg, kind_arg, col, row, mods, null);
+ mouseWithHit(button_arg, kind_arg, col, row, mods, null, null);
}
export fn pardes_mouse_pixel(button_arg: c_int, kind_arg: c_int, col: u16, row: u16, mods: u32, x: f32, y: f32, bw: f32, bh: f32, tw: f32, th: f32) void {
@@ -1494,10 +1496,16 @@ export fn pardes_mouse_pixel(button_arg: c_int, kind_arg: c_int, col: u16, row:
hit = layer.hitAt(point.x * bw, point.y * bh, bw, bh, tw, th);
if (hit != null) break;
}
- mouseWithHit(button_arg, kind_arg, col, row, mods, hit);
+ var tag_hit: ?pardes.TagHit = null;
+ for (&st.core.surface.tag_layers) |*layer| {
+ const point = source orelse break;
+ tag_hit = layer.hitAt(point.x * bw, point.y * bh, bw, bh, tw, th);
+ if (tag_hit != null) break;
+ }
+ mouseWithHit(button_arg, kind_arg, col, row, mods, hit, tag_hit);
}
-fn mouseWithHit(button_arg: c_int, kind_arg: c_int, col: u16, row: u16, mods: u32, hit: ?pardes.Mouse.BodyHit) void {
+fn mouseWithHit(button_arg: c_int, kind_arg: c_int, col: u16, row: u16, mods: u32, hit: ?pardes.Mouse.BodyHit, tag_hit: ?pardes.TagHit) void {
const st = &(state orelse return);
const button: pardes.Mouse.Button = switch (button_arg) {
0 => .left,
@@ -1521,6 +1529,7 @@ fn mouseWithHit(button_arg: c_int, kind_arg: c_int, col: u16, row: u16, mods: u3
};
st.core.update(.{ .mouse = .{
.body_hit = hit,
+ .tag_hit = tag_hit,
.button = button,
.kind = kind,
.col = col,
@@ -1666,6 +1675,24 @@ fn clearFrame(st: *State) void {
st.panel_diff_len = 0;
}
+fn blankTagCells(cells: []Cell, cols: u16, rows: u16, 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(.{});
+ }
+}
+
+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
+ cursor.x - layer.viewport.x < layer.viewport.w) return true;
+ }
+ return false;
+}
+
fn presentFrame(ctx: ?*anyopaque, surface: *const pardes.Surface) void {
const st = hostState(ctx);
const tz = tracy.zone(@src(), "presentFrame");
@@ -1692,6 +1719,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);
collectPanelDiff(st, surface, count);
collectImages(st, surface);
collectPanelTracks(st, surface);
@@ -1722,6 +1750,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);
for (surface.cell_diffs, st.changed_cells[0..count]) |diff, *out|
out.* = encodeChanged(diff);
st.panel_diff_len = count;
@@ -1843,9 +1872,45 @@ export fn pardes_frame_rows() u16 {
export fn pardes_cursor_x() i32 {
const st = &(state orelse return -1);
+ if (tagCursorCovered(st.core)) return -1;
return if (st.core.surface.cursor) |c| c.x else -1;
}
+export fn pardes_tag_layer_limit() u32 {
+ return pardes.MAX_TAG_LAYERS;
+}
+
+export fn pardes_tag_layer_value(index: u32, field: u32) u32 {
+ const st = &(state orelse return 0);
+ if (index >= pardes.MAX_TAG_LAYERS) return 0;
+ const layer = &st.core.surface.tag_layers[index];
+ return switch (field) {
+ 0 => layer.cols,
+ 1 => @intFromEnum(layer.kind),
+ 2 => layer.id,
+ 3 => layer.serial,
+ 4 => layer.viewport.x,
+ 5 => layer.viewport.y,
+ 6 => layer.viewport.w,
+ 7 => layer.viewport.h,
+ 8 => if (layer.cursor) |c| c.col 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],
+ else => 0,
+ };
+}
+
+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;
+ 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);
+ return cells.ptr;
+}
+
export fn pardes_body_layer_value(index: u32, field: u32) u32 {
const st = &(state orelse return 0);
if (index >= pardes.MAX_PANES) return 0;
@@ -1893,6 +1958,7 @@ export fn pardes_pointer_shape() u32 {
export fn pardes_cursor_y() i32 {
const st = &(state orelse return -1);
+ if (tagCursorCovered(st.core)) return -1;
return if (st.core.surface.cursor) |c| c.y else -1;
}
@@ -2348,6 +2414,9 @@ test "pardes.h declares every export the way it is defined" {
try expectSameAbi(@TypeOf(c.pardes_pointer_shape), @TypeOf(pardes_pointer_shape));
try expectSameAbi(@TypeOf(c.pardes_mouse_pixel), @TypeOf(pardes_mouse_pixel));
try expectSameAbi(@TypeOf(c.pardes_row_metrics), @TypeOf(pardes_row_metrics));
+ try expectSameAbi(@TypeOf(c.pardes_tag_layer_limit), @TypeOf(pardes_tag_layer_limit));
+ try expectSameAbi(@TypeOf(c.pardes_tag_layer_value), @TypeOf(pardes_tag_layer_value));
+ try expectSameAbi(@TypeOf(c.pardes_tag_layer_cells), @TypeOf(pardes_tag_layer_cells));
try expectSameAbi(@TypeOf(c.pardes_body_layer_value), @TypeOf(pardes_body_layer_value));
try expectSameAbi(@TypeOf(c.pardes_body_layer_cells), @TypeOf(pardes_body_layer_cells));
try expectSameAbi(@TypeOf(c.pardes_take_haptic), @TypeOf(pardes_take_haptic));
@@ -2647,3 +2716,30 @@ test "mac row metrics ABI preserves PDF cell dimensions and the unrendered grid"
try std.testing.expectEqual(@as(u16, 24), core.screen_h);
try std.testing.expectEqualDeep(pardes.RowMetrics{ .body_w = 22, .body_h = 39, .tagline_w = 17, .tagline_h = 30 }, core.row_metrics);
}
+
+test "mac tag layer ABI preserves logical capacity and physical grip" {
+ const gpa = std.testing.allocator;
+ const core = try pardes.Pardes.init(gpa, .{ .tty_only = true, .cols = 8, .rows = 3 });
+ defer core.deinit();
+ state = .{ .gpa = gpa, .threaded = undefined, .io = std.testing.io, .core = core, .runtime = .{}, .config_arena = .init(gpa), .prompt_rcs = .{} };
+ defer {
+ for (state.?.tag_cells) |cells| gpa.free(cells);
+ state.?.config_arena.deinit();
+ state = null;
+ }
+ var logical: [12]pardes.Cell = @splat(.{});
+ 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 } };
+ 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));
+ 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);
+ 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);
+}