Hi Pratik, hi internals, I maintain phasync/tui, a terminal UI library for PHP (layouts, widgets, and a VT emulator so shells and full-screen programs run in panes). I built php-src#23941 and switched tui's terminal handling to Io\Terminal where it exists:
https://github.com/phasync/tui/commit/eb7f919 Raw mode is now a ModeToken lease and the size comes from getSize(), instead of four stty runs (stty -g, stty raw -echo, stty <saved>, stty size). tui's test suite gives the same results on the PR build as on stock PHP, including the tests that crash a program in a pty in various ways and check with stty -a that the terminal was restored. That part of the RFC works, and it is exactly what userland cannot do without spawning stty, using FFI, or Windows-specific tricks. Thank you for it. I would like to suggest that the RFC proceeds with a reduced scope: the operations userland now shells out for, and nothing that userland already does well. What userland needs from core today: * Saving the mode, entering raw mode, restoring it: Laravel Prompts runs stty -g and stty <mode> (src/Terminal.php), Symfony Console runs stty in Terminal and in QuestionHelper for hidden input, tui ran stty -g / raw -echo / restore. * The size: Symfony Console uses stty -a and mode CON, tui used stty size. enableRawMode()/restoreMode(), getSize(), TerminalSize, ModeToken and TerminalException cover all of these, on POSIX and Windows. What userland already does well, so it need not be in core: * Key decoding. php-tui/term has a 600-line EventParser with modifiers, mouse and focus events; Laravel Prompts keeps its own key table, modified arrows included; tui's InputDecoder reads legacy xterm keys with modifiers, the kitty keyboard protocol, modifyOtherKeys, SGR and X10 mouse, bracketed paste and focus reports. readKey() returns Key|string|null with 28 keys and no modifiers, so none of these libraries could switch to it, and the enum cannot gain modifiers later without a new method beside it. * readLine() overlaps with fgets(); readSecret() is policy (Escape, Ctrl+C and Ctrl+D cancel, Backspace edits, no masking) that is a few lines in userland once raw mode exists, and that prompt libraries will want to decide for themselves. If key input is wanted in core later, I think a separate RFC could do it as a pure decoder (bytes in, events out, plus a flush for a lone Escape), with an event object that has room for modifiers, mouse and paste. It would serve blocking scripts and event loops alike, and avoid decoder state that belongs to one Terminal instance. Smaller points on the reduced scope: 1. Factories: fromStreams() is enough. It already covers STDIN/STDOUT, a pty, and /dev/tty (CONIN$/CONOUT$) when stdio is redirected, which is common for prompts in piped scripts. fromStdio() re-resolves the standard handles on each call; is there a use case that fromStreams(STDIN, STDOUT) does not cover? If not, one factory is easier to explain. 2. Raw mode on Windows: libraries that read and write escape sequences need virtual terminal input (ENABLE_VIRTUAL_TERMINAL_INPUT) and output processing. The RFC leaves the console flags to the implementation. Could raw mode specify them, or take an option for them? 3. Ctrl+C: some programs want raw input but Ctrl+C still delivered as a signal. An optional argument to enableRawMode() would allow that now or in a later version without breaking anything. 4. Resize: with readKey() out, Key::Resize goes too. On POSIX a cheap getSize() plus SIGWINCH is enough; on Windows the console reports resizes as input records, which a later input RFC could cover. I would support Io\Terminal in that form, and I am happy to test further revisions with tui.
