code wiki / (root) / nx_termios.nx

nx_termios.nx source

↩ module page · 162 lines · 5521 B

1// nx_termios.nx -- raw / cooked terminal mode via ioctl(TCGETS/TCSETS). 2// 3// Switches the controlling TTY between cooked (line-buffered, echo on) 4// and raw (byte-by-byte, no echo, no signals) modes. Required for 5// interactive TUI applications. 6// 7// Pairs with nx_tty.nx (output) and nx_input.nx (input). 8// 9// Per POSIX termios. Raw mode disables: 10// - ICANON (canonical line buffering) 11// - ECHO (echo input to terminal) 12// - ISIG (Ctrl+C/Z signal generation) 13// - IEXTEN (extended input processing) 14// - IXON (XON/XOFF flow control) 15// - ICRNL (CR -> NL input translation) 16// - OPOST (output post-processing) 17// ...and sets c_cc[VMIN]=1, c_cc[VTIME]=0 (return on each byte). 18// 19// genealogy_id: posix_termios_1003.1 + bsd_v6_tty_driver 20// + stevens_advanced_programming_1992 21// lineage_id: tty_mode_toggle_via_ioctl 22// axioms: NX_AX_LOGIC_EXCLUDED_MIDDLE (TTY is in raw OR cooked 23// mode at any instant; 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// ===== ioctl request constants (Linux generic) ========================= 36 37const NX_TIOC_GETS: i64 = 0x5401 // TCGETS 38const NX_TIOC_SETS: i64 = 0x5402 // TCSETS 39const NX_TIOC_GWINSZ: i64 = 0x5413 // TIOCGWINSZ 40 41// ===== termios flag bits (Linux generic) =============================== 42// 43// Subset used for raw-mode toggling. 44 45// c_iflag 46const NX_T_BRKINT: i64 = 0x0002 47const NX_T_ICRNL: i64 = 0x0100 48const NX_T_INPCK: i64 = 0x0010 49const NX_T_ISTRIP: i64 = 0x0020 50const NX_T_IXON: i64 = 0x0400 51 52// c_oflag 53const NX_T_OPOST: i64 = 0x0001 54 55// c_cflag (we OR these on for 8-bit chars + no parity) 56const NX_T_CS8: i64 = 0x0030 57 58// c_lflag 59const NX_T_ICANON: i64 = 0x0002 60const NX_T_ECHO: i64 = 0x0008 61const NX_T_ISIG: i64 = 0x0001 62const NX_T_IEXTEN: i64 = 0x8000 63 64// c_cc[] indices 65const NX_T_VMIN: i64 = 6 66const NX_T_VTIME: i64 = 5 67 68// ===== termios buffer access ============================================ 69// 70// We don't expose a struct; the buffer is opaque to callers. The 71// only operations are: get current state, modify flags, set new state. 72// Size: 60 bytes on Linux RV64 (4*4 + 1 + 19 + padding). 73 74const NX_TERMIOS_BYTES: i64 = 60 75 76// Read a 32-bit field at byte offset. 77func nx_termios_get32(buf: *u8, off: i64) -> i64 { 78 let b0: i64 = buf[off] 79 let b1: i64 = buf[off + 1] 80 let b2: i64 = buf[off + 2] 81 let b3: i64 = buf[off + 3] 82 return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) 83} 84 85// Write a 32-bit field at byte offset. 86func nx_termios_set32(buf: *u8, off: i64, v: i64) -> i64 { 87 buf[off] = v & 0xff 88 buf[off + 1] = (v >> 8) & 0xff 89 buf[off + 2] = (v >> 16) & 0xff 90 buf[off + 3] = (v >> 24) & 0xff 91 return 0 92} 93 94// ===== get / set wrappers ============================================== 95 96func nx_termios_get(buf: *u8) -> i64 { 97 return sys_ioctl(0, NX_TIOC_GETS, buf as i64) 98} 99 100func nx_termios_set(buf: *u8) -> i64 { 101 return sys_ioctl(0, NX_TIOC_SETS, buf as i64) 102} 103 104// ===== high-level raw / cooked toggles ================================= 105 106// Switch stdin into raw mode. Saves the current state into save_buf 107// so caller can restore it later. Returns 0 on success, negative 108// (-errno) on ioctl failure. 109 110func nx_termios_enter_raw(save_buf: *u8) -> i64 { 111 let r: i64 = nx_termios_get(save_buf) 112 if r != 0 { return r } 113 // Copy current state into a working buffer. 114 let work: *u8 = sys_mmap(NX_TERMIOS_BYTES) 115 var i: i64 = 0 116 while i < NX_TERMIOS_BYTES { 117 work[i] = save_buf[i] 118 i = i + 1 119 } 120 // Clear flags. 121 let iflag: i64 = nx_termios_get32(work, 0) 122 let cleared_i: i64 = iflag & (-1 - (NX_T_BRKINT | NX_T_ICRNL | NX_T_INPCK | NX_T_ISTRIP | NX_T_IXON)) 123 nx_termios_set32(work, 0, cleared_i) 124 125 let oflag: i64 = nx_termios_get32(work, 4) 126 nx_termios_set32(work, 4, oflag & (-1 - NX_T_OPOST)) 127 128 let cflag: i64 = nx_termios_get32(work, 8) 129 nx_termios_set32(work, 8, cflag | NX_T_CS8) 130 131 let lflag: i64 = nx_termios_get32(work, 12) 132 let cleared_l: i64 = lflag & (-1 - (NX_T_ICANON | NX_T_ECHO | NX_T_ISIG | NX_T_IEXTEN)) 133 nx_termios_set32(work, 12, cleared_l) 134 135 // c_cc[VMIN] = 1, c_cc[VTIME] = 0. c_cc starts at offset 17 136 // (after iflag/oflag/cflag/lflag = 16 bytes + c_line byte = 17). 137 work[17 + NX_T_VMIN] = 1 138 work[17 + NX_T_VTIME] = 0 139 140 return nx_termios_set(work) 141} 142 143func nx_termios_restore(save_buf: *u8) -> i64 { 144 return nx_termios_set(save_buf) 145} 146 147// ===== terminal size ==================================================== 148// 149// TIOCGWINSZ returns a struct winsize { unsigned short ws_row, ws_col, 150// ws_xpixel, ws_ypixel; }. 8 bytes total. We read it and return rows 151// + cols via out parameters. 152 153func nx_termios_winsize(out_rows: *i64, out_cols: *i64) -> i64 { 154 let buf: *u8 = sys_mmap(8) 155 let r: i64 = sys_ioctl(1, NX_TIOC_GWINSZ, buf as i64) 156 if r != 0 { return r } 157 let rows: i64 = (buf[0] as i64) | ((buf[1] as i64) << 8) 158 let cols: i64 = (buf[2] as i64) | ((buf[3] as i64) << 8) 159 out_rows[0] = rows 160 out_cols[0] = cols 161 return 0 162}