[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
0

Configure Feed

Select the types of activity you want to include in your feed.

tty / input-native.ts
7.2 kB 263 lines
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 172/** 173 * Attachment surface provided by a TermInfo handle: the shared memory, 174 * its bump allocator, and the capability struct / raw terminfo region 175 * pointers. See terminfo.ts internals(). 176 */ 177export interface InputAttach { 178 memory: WebAssembly.Memory; 179 exports: Record<string, CallableFunction>; 180 structPtr: number; 181 bytesPtr: number; 182 bytesLen: number; 183 alloc(size: number, align?: number): number; 184} 185 186import { compiled } from "./wasm.ts"; 187 188export async function createInputNative( 189 escLatency: number, 190 attach?: InputAttach, 191): Promise<InputNative> { 192 let memory = attach?.memory ?? new WebAssembly.Memory({ initial: 4 }); 193 194 let raw: unknown; 195 if (attach) { 196 // Reuse the handle's instance: instantiating the module again over 197 // the shared memory would rewrite its data segments and clobber 198 // static state already initialized there. 199 raw = attach.exports; 200 } else { 201 let instance = await WebAssembly.instantiate(compiled, { 202 env: { memory }, 203 clay: { 204 measureTextFunction() {}, 205 queryScrollOffsetFunction(ret: number) { 206 let v = new DataView(memory.buffer); 207 v.setFloat32(ret, 0, true); 208 v.setFloat32(ret + 4, 0, true); 209 }, 210 }, 211 }); 212 raw = instance.exports; 213 } 214 215 let exports = raw as { 216 __heap_base: WebAssembly.Global; 217 input_size(): number; 218 input_init( 219 mem: number, 220 escLatency: number, 221 terminfo: number, 222 terminfoLen: number, 223 ti: number, 224 ): number; 225 input_scan(st: number, buf: number, len: number, now: number): number; 226 input_count(st: number): number; 227 input_event(st: number, index: number): number; 228 input_delay(st: number): number; 229 }; 230 231 let size = exports.input_size(); 232 let state: number; 233 let buffer: number; 234 if (attach) { 235 let arena = attach.alloc(size); 236 buffer = attach.alloc(SCAN_BUFFER_SIZE); 237 state = exports.input_init( 238 arena, 239 escLatency, 240 attach.bytesLen > 0 ? attach.bytesPtr : 0, 241 attach.bytesLen, 242 attach.structPtr, 243 ); 244 } else { 245 let heap = exports.__heap_base.value as number; 246 state = exports.input_init(heap, escLatency, 0, 0, 0); 247 buffer = (heap + size + 7) & ~7; 248 } 249 250 return { 251 memory, 252 state, 253 buffer, 254 scan: exports.input_scan, 255 count: exports.input_count, 256 event: exports.input_event, 257 delay: exports.input_delay, 258 }; 259} 260 261// Must match SCAN_BUFFER_SIZE in input.c — the maximum bytes input_scan() 262// can accept in a single call. 263export const SCAN_BUFFER_SIZE = 4096;