summaryrefslogtreecommitdiff
path: root/src/macos/pardes.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/macos/pardes.h')
-rw-r--r--src/macos/pardes.h204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/macos/pardes.h b/src/macos/pardes.h
new file mode 100644
index 00000000..e0d6fa32
--- /dev/null
+++ b/src/macos/pardes.h
@@ -0,0 +1,204 @@
+// libpardes — the C ABI the macOS app links against.
+//
+// Hand-written, and kept honest by a test: build.zig translate-C's this file
+// into the unit-test build, and src/macos.zig asserts every constant and
+// struct layout below against the Zig side (ghostty's trick — see docs/macos.md).
+// Change nothing here without running `zig build unit-test -Dplatform=macos`.
+//
+// Division of labour, which is the whole design in two lines:
+// Zig owns the core, the ptys, every effect, and the worker threads.
+// Swift owns NSApplication, the window, input translation, and drawing.
+//
+// There is exactly one core per process, so there are no handles: the state is
+// a file-scoped singleton, same as src/web.zig. Every function below must be
+// called from the main thread. The one exception is the `wakeup` callback,
+// which fires from a pty reader thread.
+
+#ifndef PARDES_H
+#define PARDES_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------- frame
+
+// One rendered cell. Mirrors pardes.Cell (src/pardes.zig) flattened for the
+// wire; the identical encoding is spelled a second time for the browser in
+// src/web.zig as WebCell.
+typedef struct {
+ // UTF-8 grapheme, `len` bytes. The core caps graphemes at seven bytes; the
+ // eighth is always zero so a debugger prints something sensible.
+ uint8_t text[8];
+ // 0x01000000 terminal default
+ // 0x02000000 | index 256-color palette entry
+ // 0x00RRGGBB direct color
+ uint32_t fg;
+ uint32_t bg;
+ // bit 0 bold, 1 dim, 2 italic, 3 blink, 4 reverse, 5 invisible,
+ // 6 strikethrough; bits 8.. hold PARDES_UL_*.
+ uint16_t attrs;
+ uint8_t len;
+ // bit 0: the core never painted this cell — draw it as the default cell
+ // (background only), which is what makes a partial redraw cheap.
+ uint8_t flags;
+} pardes_cell_s;
+
+#define PARDES_COLOR_DEFAULT 0x01000000u
+#define PARDES_COLOR_INDEXED 0x02000000u
+#define PARDES_COLOR_TAG_MASK 0xff000000u
+#define PARDES_COLOR_RGB_MASK 0x00ffffffu
+
+#define PARDES_ATTR_BOLD 0x0001u
+#define PARDES_ATTR_DIM 0x0002u
+#define PARDES_ATTR_ITALIC 0x0004u
+#define PARDES_ATTR_BLINK 0x0008u
+#define PARDES_ATTR_REVERSE 0x0010u
+#define PARDES_ATTR_INVISIBLE 0x0020u
+#define PARDES_ATTR_STRIKETHROUGH 0x0040u
+#define PARDES_ATTR_UL_SHIFT 8
+// Literals rather than (1u << n): Swift's macro importer is dependable on a
+// plain integer and less so on an expression, and nothing here can compile the
+// Swift side to find out. The ABI guard asserts each value against the encoder.
+
+#define PARDES_UL_OFF 0
+#define PARDES_UL_SINGLE 1
+#define PARDES_UL_DOUBLE 2
+#define PARDES_UL_CURLY 3
+#define PARDES_UL_DOTTED 4
+#define PARDES_UL_DASHED 5
+
+#define PARDES_CELL_DEFAULT 0x01u
+
+// ---------------------------------------------------------------- input
+
+// Modifier bitmask shared by key and mouse events.
+#define PARDES_MOD_CTRL 0x0001u
+#define PARDES_MOD_ALT 0x0002u
+#define PARDES_MOD_SHIFT 0x0004u
+
+// Keys that carry no text. The four editing keys are their ASCII controls;
+// everything else lives in a private-use plane so it can never collide with a
+// real codepoint arriving as text.
+#define PARDES_KEY_ENTER 0x0Du
+#define PARDES_KEY_ESCAPE 0x1Bu
+#define PARDES_KEY_TAB 0x09u
+#define PARDES_KEY_BACKSPACE 0x7Fu
+#define PARDES_KEY_UP 0xF0001u
+#define PARDES_KEY_DOWN 0xF0002u
+#define PARDES_KEY_LEFT 0xF0003u
+#define PARDES_KEY_RIGHT 0xF0004u
+#define PARDES_KEY_HOME 0xF0005u
+#define PARDES_KEY_END 0xF0006u
+#define PARDES_KEY_PAGE_UP 0xF0007u
+#define PARDES_KEY_PAGE_DOWN 0xF0008u
+#define PARDES_KEY_DELETE 0xF0009u
+
+// acme's three buttons carry the whole vocabulary: 1 selects, 2 executes,
+// 3 looks. Wheel buttons are ordinary buttons, not a separate axis.
+typedef enum {
+ PARDES_MOUSE_LEFT = 0,
+ PARDES_MOUSE_MIDDLE = 1,
+ PARDES_MOUSE_RIGHT = 2,
+ PARDES_MOUSE_WHEEL_UP = 3,
+ PARDES_MOUSE_WHEEL_DOWN = 4,
+ PARDES_MOUSE_WHEEL_LEFT = 5,
+ PARDES_MOUSE_WHEEL_RIGHT = 6,
+ PARDES_MOUSE_NONE = 7,
+} pardes_mouse_button_e;
+
+typedef enum {
+ PARDES_MOUSE_PRESS = 0,
+ PARDES_MOUSE_RELEASE = 1,
+ PARDES_MOUSE_MOTION = 2,
+ PARDES_MOUSE_DRAG = 3,
+} pardes_mouse_kind_e;
+
+// ---------------------------------------------------------------- runtime
+
+// What the host lends the core. Two callbacks, because everything else the
+// core wants done it already does itself: it owns the ptys, and it opens URLs
+// through /usr/bin/open. Copied by value during pardes_init, so the struct
+// need only outlive that call.
+typedef struct {
+ // Passed back to every callback below. Conventionally the AppDelegate.
+ void *userdata;
+
+ // "Your state moved; please pump me." Called from a pty reader thread, so
+ // the host must hop to the main thread before calling pardes_tick — see
+ // AppDelegate.wakeup, which does exactly one DispatchQueue.main.async.
+ void (*wakeup)(void *userdata);
+
+ // The yank register changed. `text` is borrowed for the duration of the
+ // call only. Main thread, inside pardes_tick.
+ void (*set_clipboard)(void *userdata, const char *text, size_t len);
+} pardes_runtime_s;
+
+// ---------------------------------------------------------------- lifecycle
+
+// Returns 0 on success, or a nonzero opaque error code. Pass the real window
+// grid, not a placeholder: init delivers it as the first resize EVENT (the
+// core defers each shell's greeting until it has seen one), and the first
+// forkpty takes its winsize straight off the core — boot at 80x24 and the
+// shell draws its first prompt to a width the window never had.
+int pardes_init(const pardes_runtime_s *runtime, uint16_t cols, uint16_t rows);
+void pardes_deinit(void);
+
+// Drain pty output into the core and perform the effects it queued. Call after
+// every input function and on every wakeup. Returns true if anything changed
+// and the host should mark its view dirty.
+bool pardes_tick(void);
+
+// The core asked to exit (the Exit builtin, or the last pane closing).
+bool pardes_should_quit(void);
+
+// A theme transition is mid-flight and wants ~60 Hz ticks until it settles.
+bool pardes_animating(void);
+
+// ---------------------------------------------------------------- events in
+
+// `cp` is a codepoint or one of PARDES_KEY_*; `text`/`len` are the host's
+// already-composed characters for printable keys and empty for functional
+// ones. `text` is borrowed for the call.
+void pardes_key(uint32_t cp, const char *text, size_t len, uint32_t mods);
+void pardes_paste(const char *text, size_t len);
+void pardes_mouse(pardes_mouse_button_e button, pardes_mouse_kind_e kind,
+ uint16_t col, uint16_t row, uint32_t mods);
+
+// Trackpad/precision wheel distance in rows, sign following the grid (positive
+// scrolls down). The core has no fractional scroll — it moves a row at a time —
+// so libpardes accumulates here and emits whole-row wheel presses, keeping the
+// remainder. Both other shells do this same accumulation host-side (stepScroll
+// in gui.zig, the drain loop in web/app.mjs); it lives in Zig here so the Swift
+// side stays a translator. A discrete wheel notch should go through
+// pardes_mouse instead.
+void pardes_scroll(float delta_rows, uint16_t col, uint16_t row);
+
+// `cell_w`/`cell_h` are one cell in physical pixels, which only the native PDF
+// placement path reads. Pass the backing-store size, not points.
+void pardes_resize(uint16_t cols, uint16_t rows, uint16_t cell_w,
+ uint16_t cell_h);
+
+// ---------------------------------------------------------------- frame out
+
+// Render one frame. Returns the cell count (cols * rows), or 0 on failure.
+// The buffer returned by pardes_frame_cells is valid until the next call.
+uint32_t pardes_frame(void);
+const pardes_cell_s *pardes_frame_cells(void);
+uint16_t pardes_frame_cols(void);
+uint16_t pardes_frame_rows(void);
+
+// -1 when the cursor is hidden. `bar` asks for a thin insert-mode caret.
+int32_t pardes_cursor_x(void);
+int32_t pardes_cursor_y(void);
+bool pardes_cursor_bar(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PARDES_H