feat: libc - add SYS_GETCHAR_NB and a non-blocking stdin readiness probe
Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NQRTGoYgnZQsh7uCh6Jsxm
This commit is contained in:
@@ -228,6 +228,7 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_WIFI_RESULTS = 163; // (WifiNetwork*, maxCount) -> count, no radio work
|
||||
static constexpr uint64_t SYS_WIFI_CONNECT_ASYNC = 164; // (ssid, password) -> 0 accepted, <0 on error
|
||||
static constexpr uint64_t SYS_NETIFS = 165; // (NetIfInfo*, maxCount) -> count
|
||||
static constexpr uint64_t SYS_GETCHAR_NB = 166; // () -> ascii, 0 if nothing pending; never blocks
|
||||
|
||||
// Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM).
|
||||
static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz
|
||||
|
||||
@@ -189,6 +189,7 @@ extern "C" {
|
||||
#define MTK_SYS_WIFI_RESULTS 163
|
||||
#define MTK_SYS_WIFI_CONNECT_ASYNC 164
|
||||
#define MTK_SYS_NETIFS 165
|
||||
#define MTK_SYS_GETCHAR_NB 166
|
||||
/* @SYSCALLS-END */
|
||||
|
||||
#define MTK_SOCK_TCP 1
|
||||
|
||||
@@ -95,6 +95,17 @@ int ungetc(int c, FILE *stream);
|
||||
char *fgets(char *s, int size, FILE *stream);
|
||||
int fputs(const char *s, FILE *stream);
|
||||
|
||||
/* MontaukOS extension: non-blocking readiness probe for stdin.
|
||||
Returns 1 when a complete line is buffered, so a following fgets/fgetc on
|
||||
stdin returns without blocking; 0 otherwise. Partially typed input is kept.
|
||||
This is what a select()-style poll over stdin has to be built on -- stdio
|
||||
descriptors are not waitable kernel objects. */
|
||||
int montauk_stdin_ready(void);
|
||||
|
||||
/* Blocks until a complete line is buffered on stdin, then returns 1. Same
|
||||
buffer as montauk_stdin_ready(); nothing is consumed either way. */
|
||||
int montauk_stdin_wait(void);
|
||||
|
||||
void perror(const char *s);
|
||||
FILE *tmpfile(void);
|
||||
char *tmpnam(char *s);
|
||||
|
||||
@@ -197,6 +197,8 @@ namespace montauk {
|
||||
inline bool is_key_available() { return (bool)syscall0(montauk::abi::SYS_ISKEYAVAILABLE); }
|
||||
inline void getkey(montauk::abi::KeyEvent* out) { syscall1(montauk::abi::SYS_GETKEY, (uint64_t)out); }
|
||||
inline char getchar() { return (char)syscall0(montauk::abi::SYS_GETCHAR); }
|
||||
// Returns 0 rather than blocking when no character is pending.
|
||||
inline char getchar_nb() { return (char)syscall0(montauk::abi::SYS_GETCHAR_NB); }
|
||||
inline uint64_t input_wait(uint64_t observedSerial, uint64_t timeoutMs) {
|
||||
return (uint64_t)syscall2(montauk::abi::SYS_INPUT_WAIT, observedSerial, timeoutMs);
|
||||
}
|
||||
|
||||
+53
-13
@@ -101,6 +101,7 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
|
||||
#define SYS_FREE 12
|
||||
#define SYS_GETMILLISECONDS 14
|
||||
#define SYS_GETCHAR 18
|
||||
#define SYS_GETCHAR_NB 166
|
||||
#define SYS_SPAWN 20
|
||||
#define SYS_WAITPID 23
|
||||
#define SYS_GETARGS 25
|
||||
@@ -1478,9 +1479,23 @@ FILE *stdin = &_stdin_file;
|
||||
FILE *stdout = &_stdout_file;
|
||||
FILE *stderr = &_stderr_file;
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
stdin line editing.
|
||||
|
||||
Input is gathered a line at a time so backspace can still take a character
|
||||
back. The gather is incremental: _stdin_pump() may be called without
|
||||
blocking, in which case a half-typed line simply stays in the buffer until
|
||||
the next call. That is what lets montauk_stdin_ready() answer "is a line
|
||||
available?" without stealing keystrokes -- see the select() shim in the
|
||||
NetSurf port, which is the reason this is not just a blocking read.
|
||||
|
||||
_stdin_line_ready distinguishes "buffer holds a complete line" from "buffer
|
||||
holds a partial one"; readers must never look at the buffer without it.
|
||||
------------------------------------------------------------------------ */
|
||||
static char _stdin_linebuf[512];
|
||||
static size_t _stdin_line_len = 0;
|
||||
static size_t _stdin_line_pos = 0;
|
||||
static int _stdin_line_ready = 0;
|
||||
|
||||
static void _stdin_echo_char(char c) {
|
||||
if (c == '\b') {
|
||||
@@ -1492,13 +1507,25 @@ static void _stdin_echo_char(char c) {
|
||||
_zos_syscall1(SYS_PUTCHAR, (unsigned char)c);
|
||||
}
|
||||
|
||||
static int _stdin_fill_line(void) {
|
||||
_stdin_line_len = 0;
|
||||
_stdin_line_pos = 0;
|
||||
/* Gather keystrokes into _stdin_linebuf. Returns 1 when a complete line is
|
||||
buffered. With block == 0, returns 0 as soon as input runs dry, keeping
|
||||
whatever has been typed so far for the next call. */
|
||||
static int _stdin_pump(int block) {
|
||||
if (_stdin_line_ready) {
|
||||
if (_stdin_line_pos < _stdin_line_len) return 1;
|
||||
/* Previous line fully read; start gathering the next one. */
|
||||
_stdin_line_ready = 0;
|
||||
_stdin_line_len = 0;
|
||||
_stdin_line_pos = 0;
|
||||
}
|
||||
|
||||
while (_stdin_line_len + 1 < sizeof(_stdin_linebuf)) {
|
||||
int c = (int)_zos_syscall0(SYS_GETCHAR);
|
||||
if (c <= 0) continue;
|
||||
for (;;) {
|
||||
int c = block ? (int)_zos_syscall0(SYS_GETCHAR)
|
||||
: (int)_zos_syscall0(SYS_GETCHAR_NB);
|
||||
if (c <= 0) {
|
||||
if (block) continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == '\r') c = '\n';
|
||||
|
||||
@@ -1513,6 +1540,7 @@ static int _stdin_fill_line(void) {
|
||||
if (c == '\n') {
|
||||
_stdin_linebuf[_stdin_line_len++] = (char)c;
|
||||
_stdin_echo_char((char)c);
|
||||
_stdin_line_ready = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1520,15 +1548,27 @@ static int _stdin_fill_line(void) {
|
||||
_stdin_linebuf[_stdin_line_len++] = (char)c;
|
||||
_stdin_echo_char((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
if (_stdin_line_len > 0) {
|
||||
_stdin_linebuf[_stdin_line_len++] = '\n';
|
||||
_stdin_echo_char('\n');
|
||||
return 1;
|
||||
/* Full buffer: break the line here rather than dropping input. */
|
||||
if (_stdin_line_len + 1 >= sizeof(_stdin_linebuf)) {
|
||||
_stdin_linebuf[_stdin_line_len++] = '\n';
|
||||
_stdin_echo_char('\n');
|
||||
_stdin_line_ready = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
static int _stdin_fill_line(void) {
|
||||
return _stdin_pump(1);
|
||||
}
|
||||
|
||||
int montauk_stdin_ready(void) {
|
||||
return _stdin_pump(0);
|
||||
}
|
||||
|
||||
int montauk_stdin_wait(void) {
|
||||
return _stdin_pump(1);
|
||||
}
|
||||
|
||||
FILE *fopen(const char *path, const char *mode) {
|
||||
@@ -1742,7 +1782,7 @@ int fgetc(FILE *stream) {
|
||||
}
|
||||
|
||||
if (stream->is_std == 1) {
|
||||
if (_stdin_line_pos >= _stdin_line_len) {
|
||||
if (!_stdin_line_ready || _stdin_line_pos >= _stdin_line_len) {
|
||||
if (!_stdin_fill_line()) {
|
||||
stream->eof = 1;
|
||||
return EOF;
|
||||
|
||||
Binary file not shown.
@@ -233,6 +233,12 @@
|
||||
Block until a printable character is typed.
|
||||
char montauk::getchar();
|
||||
|
||||
.B SYS_GETCHAR_NB (166)
|
||||
Return the next printable character, or 0 when none is
|
||||
pending. Never blocks; key events carrying no ascii are
|
||||
drained rather than left to stall a following getchar.
|
||||
char montauk::getchar_nb();
|
||||
|
||||
.B SYS_INPUT_WAIT (123)
|
||||
Block until the input serial number differs from
|
||||
observedSerial or the timeout elapses; used to sleep
|
||||
|
||||
Reference in New Issue
Block a user