summaryrefslogtreecommitdiff
path: root/src/macos/pardes.h
blob: e0d6fa32945a3b2a6166492f5b3482896bdf1fd4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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