diff options
Diffstat (limited to 'src/macos/pardes.h')
| -rw-r--r-- | src/macos/pardes.h | 204 |
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 |
