code wiki / (root) / nx_input.nx

nx_input.nx source

↩ module page · 133 lines · 5148 B

1// nx_input.nx -- terminal input primitives (line + byte read from stdin). 2// 3// The user-interaction layer's INPUT side. Reads bytes from stdin 4// (fd=0) via sys_read; decodes basic escape sequences when in raw 5// mode (see nx_termios.nx for raw-mode entry). 6// 7// Companion to nx_tty.nx (output side). Together they close the 8// substrate-to-user gap. 9// 10// Why this exists (per user 2026-05-13): 11// user said "if not build it" when the layer audit showed UI was 12// the missing tier. This is the foundation; widgets / event loop 13// can build on these primitives. 14// 15// Standard usage: 16// - nx_input_read_line(buf, max) -- cooked-mode line read 17// - nx_input_read_byte() -- single byte (works in any mode) 18// - nx_input_read_key() -- decode arrow keys etc. (raw mode) 19// 20// genealogy_id: ecma_48_1976_ansi_escape + readline_lineage 21// lineage_id: stdin_byte_stream + escape_sequence_decode 22// axioms: NX_AX_LOGIC_EXCLUDED_MIDDLE (each byte is or isn't a 23// key event prefix; no third state) 24 25// nx_safety_envelope: 26// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 27// sil_target: SIL1 28// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 29// verdict: NOT_YET_EVALUATED 30 31import "syscalls.nx" 32import "runtime.nx" 33import "nx_axioms.nx" 34 35// Special key codes returned by nx_input_read_key (above 256 so they 36// can't collide with byte values). 37const NX_KEY_NONE: i64 = 0 38const NX_KEY_BACKSPACE: i64 = 127 39const NX_KEY_ENTER: i64 = 10 40const NX_KEY_ESC: i64 = 27 41const NX_KEY_UP: i64 = 1001 42const NX_KEY_DOWN: i64 = 1002 43const NX_KEY_RIGHT: i64 = 1003 44const NX_KEY_LEFT: i64 = 1004 45const NX_KEY_HOME: i64 = 1005 46const NX_KEY_END: i64 = 1006 47const NX_KEY_PGUP: i64 = 1007 48const NX_KEY_PGDN: i64 = 1008 49const NX_KEY_DEL: i64 = 1009 50const NX_KEY_INS: i64 = 1010 51 52// Read one byte from stdin. Returns the byte value (0..255), or -1 53// on EOF / read error. 54func nx_input_read_byte() -> i64 { 55 let buf: *u8 = sys_mmap(1) 56 let n: i64 = sys_read(0, buf, 1) 57 if n <= 0 { return -1 } 58 return buf[0] 59} 60 61// Read one line of input into buf (max bytes capacity). Reads up 62// to but NOT including the trailing newline; null-terminates buf. 63// Returns the line length, or -1 on EOF. 64 65func nx_input_read_line(buf: *u8, max: i64) -> i64 { 66 var n: i64 = 0 67 var done: i64 = 0 68 while done == 0 { 69 if n >= max - 1 { done = 1 } 70 if done == 0 { 71 let c: i64 = nx_input_read_byte() 72 if c < 0 { done = 1 } 73 if done == 0 { 74 if c == 10 { done = 1 } // newline 75 if done == 0 { 76 if c == 13 { // CR 77 // peek? for now just terminate. 78 done = 1 79 } 80 if done == 0 { 81 buf[n] = c 82 n = n + 1 83 } 84 } 85 } 86 } 87 } 88 buf[n] = 0 89 if n == 0 { return -1 } // EOF on empty 90 return n 91} 92 93// Read one "key event" from stdin (assumes raw mode). Decodes ANSI 94// CSI escape sequences (\x1b[A = up arrow, etc.). Returns either 95// a single-byte value 0..255 OR an NX_KEY_* constant for special keys. 96// Returns -1 on EOF. 97// 98// Caller MUST be in raw mode (see nx_termios_enter_raw). In cooked 99// mode this still works for plain bytes but escape sequences won't 100// arrive interleaved with the keystrokes. 101 102func nx_input_read_key() -> i64 { 103 let c: i64 = nx_input_read_byte() 104 if c < 0 { return -1 } 105 if c != NX_KEY_ESC { return c } 106 // ESC pressed; peek next byte (no timeout -- caller must guarantee 107 // an actual escape sequence arrived). 108 let c2: i64 = nx_input_read_byte() 109 if c2 < 0 { return NX_KEY_ESC } 110 if c2 != 91 { return NX_KEY_ESC } // '[' for CSI 111 // CSI sequence: read one more byte (or sometimes two-digit ~). 112 let c3: i64 = nx_input_read_byte() 113 if c3 < 0 { return NX_KEY_ESC } 114 if c3 == 65 { return NX_KEY_UP } // 'A' 115 if c3 == 66 { return NX_KEY_DOWN } // 'B' 116 if c3 == 67 { return NX_KEY_RIGHT } // 'C' 117 if c3 == 68 { return NX_KEY_LEFT } // 'D' 118 if c3 == 72 { return NX_KEY_HOME } // 'H' 119 if c3 == 70 { return NX_KEY_END } // 'F' 120 // Multi-digit "ESC[3~" sequences (Del = 3, Ins = 2, PgUp = 5, PgDn = 6). 121 if c3 >= 48 { 122 if c3 <= 57 { 123 let c4: i64 = nx_input_read_byte() 124 if c4 == 126 { // '~' 125 if c3 == 50 { return NX_KEY_INS } 126 if c3 == 51 { return NX_KEY_DEL } 127 if c3 == 53 { return NX_KEY_PGUP } 128 if c3 == 54 { return NX_KEY_PGDN } 129 } 130 } 131 } 132 return NX_KEY_ESC // unrecognized 133}