[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
1export const EVENT_KEY = 1;
2export const EVENT_MOUSE = 2;
3export const EVENT_RESIZE = 3;
4export const EVENT_CURSOR = 4;
5
6export const MOD_ALT = 1;
7export const MOD_CTRL = 2;
8export const MOD_SHIFT = 4;
9export const MOD_MOTION = 8;
10export const MOD_RELEASE = 16;
11
12export const KEY_F1 = 0xFFFF;
13export const KEY_F2 = 0xFFFE;
14export const KEY_F3 = 0xFFFD;
15export const KEY_F4 = 0xFFFC;
16export const KEY_F5 = 0xFFFB;
17export const KEY_F6 = 0xFFFA;
18export const KEY_F7 = 0xFFF9;
19export const KEY_F8 = 0xFFF8;
20export const KEY_F9 = 0xFFF7;
21export const KEY_F10 = 0xFFF6;
22export const KEY_F11 = 0xFFF5;
23export const KEY_F12 = 0xFFF4;
24export const KEY_ARROW_UP = 0xFFF3;
25export const KEY_ARROW_DOWN = 0xFFF2;
26export const KEY_ARROW_LEFT = 0xFFF1;
27export const KEY_ARROW_RIGHT = 0xFFF0;
28export const KEY_HOME = 0xFFEF;
29export const KEY_END = 0xFFEE;
30export const KEY_INSERT = 0xFFED;
31export const KEY_DELETE = 0xFFEC;
32export const KEY_PGUP = 0xFFEB;
33export const KEY_PGDN = 0xFFEA;
34export const KEY_BACKTAB = 0xFFE9;
35export const KEY_NUMPAD_0 = 0xFFE2;
36export const KEY_NUMPAD_1 = 0xFFE1;
37export const KEY_NUMPAD_2 = 0xFFE0;
38export const KEY_NUMPAD_3 = 0xFFDF;
39export const KEY_NUMPAD_4 = 0xFFDE;
40export const KEY_NUMPAD_5 = 0xFFDD;
41export const KEY_NUMPAD_6 = 0xFFDC;
42export const KEY_NUMPAD_7 = 0xFFDB;
43export const KEY_NUMPAD_8 = 0xFFDA;
44export const KEY_NUMPAD_9 = 0xFFD9;
45export const KEY_NUMPAD_DECIMAL = 0xFFD8;
46export const KEY_NUMPAD_DIVIDE = 0xFFD7;
47export const KEY_NUMPAD_MULTIPLY = 0xFFD6;
48export const KEY_NUMPAD_SUBTRACT = 0xFFD5;
49export const KEY_NUMPAD_ADD = 0xFFD4;
50export const KEY_NUMPAD_ENTER = 0xFFD3;
51export const KEY_NUMPAD_EQUAL = 0xFFD2;
52
53export const KEY_SHIFT_LEFT = 0xFFD1;
54export const KEY_SHIFT_RIGHT = 0xFFD0;
55export const KEY_CONTROL_LEFT = 0xFFCF;
56export const KEY_CONTROL_RIGHT = 0xFFCE;
57export const KEY_ALT_LEFT = 0xFFCD;
58export const KEY_ALT_RIGHT = 0xFFCC;
59export const KEY_SUPER_LEFT = 0xFFCB;
60export const KEY_SUPER_RIGHT = 0xFFCA;
61export const KEY_HYPER_LEFT = 0xFFC9;
62export const KEY_HYPER_RIGHT = 0xFFC8;
63export const KEY_META_LEFT = 0xFFC7;
64export const KEY_META_RIGHT = 0xFFC6;
65export const KEY_CAPS_LOCK = 0xFFC5;
66export const KEY_NUM_LOCK = 0xFFC4;
67export const KEY_SCROLL_LOCK = 0xFFC3;
68
69export const KEY_MOUSE_LEFT = 0xFFE8;
70export const KEY_MOUSE_RIGHT = 0xFFE7;
71export const KEY_MOUSE_MIDDLE = 0xFFE6;
72export const KEY_MOUSE_RELEASE = 0xFFE5;
73export const KEY_MOUSE_WHEEL_UP = 0xFFE4;
74export const KEY_MOUSE_WHEEL_DOWN = 0xFFE3;
75export const KEY_ESC = 0x1B;
76export const KEY_ENTER = 0x0D;
77export const KEY_TAB = 0x09;
78export const KEY_BACKSPACE = 0x7F;
79export const KEY_SPACE = 0x20;
80
81import {
82 array,
83 int32,
84 offsets,
85 struct,
86 uint16,
87 uint32,
88 uint8,
89} from "./typedef.ts";
90
91const MAX_TEXT_CODEPOINTS = 8;
92
93const InputEventLayout = struct({
94 type: uint8(),
95 mod: uint8(),
96 key: uint16(),
97 ch: uint32(),
98 x: int32(),
99 y: int32(),
100 w: int32(),
101 h: int32(),
102 action: uint8(),
103 shifted: uint32(),
104 base: uint32(),
105 text: array(uint32(), MAX_TEXT_CODEPOINTS),
106 text_len: uint8(),
107});
108
109const {
110 type: OFFSET_TYPE,
111 mod: OFFSET_MOD,
112 key: OFFSET_KEY,
113 ch: OFFSET_CH,
114 x: OFFSET_X,
115 y: OFFSET_Y,
116 w: OFFSET_W,
117 h: OFFSET_H,
118 action: OFFSET_ACTION,
119 text_len: OFFSET_TEXT_LEN,
120 shifted: OFFSET_SHIFTED,
121 base: OFFSET_BASE,
122 text: OFFSET_TEXT,
123} = offsets(InputEventLayout);
124
125export interface NativeInputEvent {
126 type: number;
127 mod: number;
128 key: number;
129 ch: number;
130 x: number;
131 y: number;
132 w: number;
133 h: number;
134 action: number;
135 shifted: number;
136 base: number;
137 text: number[];
138}
139
140export function readEvent(view: DataView, ptr: number): NativeInputEvent {
141 let len = view.getUint8(ptr + OFFSET_TEXT_LEN);
142 let text: number[] = [];
143 for (let i = 0; i < len && i < MAX_TEXT_CODEPOINTS; i++) {
144 text.push(view.getUint32(ptr + OFFSET_TEXT + i * 4, true));
145 }
146 return {
147 type: view.getUint8(ptr + OFFSET_TYPE),
148 mod: view.getUint8(ptr + OFFSET_MOD),
149 key: view.getUint16(ptr + OFFSET_KEY, true),
150 ch: view.getUint32(ptr + OFFSET_CH, true),
151 x: view.getInt32(ptr + OFFSET_X, true),
152 y: view.getInt32(ptr + OFFSET_Y, true),
153 w: view.getInt32(ptr + OFFSET_W, true),
154 h: view.getInt32(ptr + OFFSET_H, true),
155 action: view.getUint8(ptr + OFFSET_ACTION),
156 shifted: view.getUint32(ptr + OFFSET_SHIFTED, true),
157 base: view.getUint32(ptr + OFFSET_BASE, true),
158 text,
159 };
160}
161
162export interface InputNative {
163 memory: WebAssembly.Memory;
164 state: number;
165 buffer: number;
166 scan(st: number, buf: number, len: number, now: number): number;
167 count(st: number): number;
168 event(st: number, index: number): number;
169 delay(st: number): number;
170}
171
172import { compiled } from "./wasm.ts";
173
174export async function createInputNative(
175 escLatency: number,
176): Promise<InputNative> {
177 let memory = new WebAssembly.Memory({ initial: 4 });
178
179 let instance = await WebAssembly.instantiate(compiled, {
180 env: { memory },
181 clay: {
182 measureTextFunction() {},
183 queryScrollOffsetFunction(ret: number) {
184 let v = new DataView(memory.buffer);
185 v.setFloat32(ret, 0, true);
186 v.setFloat32(ret + 4, 0, true);
187 },
188 },
189 });
190
191 let exports = instance.exports as unknown as {
192 __heap_base: WebAssembly.Global;
193 input_size(): number;
194 input_init(mem: number, escLatency: number): number;
195 input_scan(st: number, buf: number, len: number, now: number): number;
196 input_count(st: number): number;
197 input_event(st: number, index: number): number;
198 input_delay(st: number): number;
199 };
200
201 let heap = exports.__heap_base.value as number;
202 let size = exports.input_size();
203 let state = exports.input_init(heap, escLatency);
204 let buffer = (heap + size + 7) & ~7;
205
206 return {
207 memory,
208 state,
209 buffer,
210 scan: exports.input_scan,
211 count: exports.input_count,
212 event: exports.input_event,
213 delay: exports.input_delay,
214 };
215}
216
217// Compiled terminfo entries are limited to 4096 bytes (legacy) or 32768
218// bytes (extended ncurses format). We use the extended limit as our upper
219// bound. See https://man7.org/linux/man-pages/man5/term.5.html
220export const MAX_TERMINFO = 32768;
221
222// Must match SCAN_BUFFER_SIZE in input.c — the maximum bytes input_scan()
223// can accept in a single call.
224export const SCAN_BUFFER_SIZE = 4096;