MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

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

introduce tty control utilities for terminal manipulation

+175 -87
+17
include/tty_ctrl.h
··· 1 + #ifndef ANT_TTY_CTRL_H 2 + #define ANT_TTY_CTRL_H 3 + 4 + #include <stdbool.h> 5 + #include <stddef.h> 6 + #include <stdio.h> 7 + 8 + bool tty_ctrl_write_fd(int fd, const char *data, size_t len); 9 + bool tty_ctrl_write_stream(FILE *stream, const char *data, size_t len, bool flush); 10 + 11 + const char *tty_ctrl_clear_line_seq(int dir, size_t *len_out); 12 + const char *tty_ctrl_clear_screen_down_seq(size_t *len_out); 13 + 14 + bool tty_ctrl_build_cursor_to(char *buf, size_t buf_size, int x, bool has_y, int y, size_t *len_out); 15 + bool tty_ctrl_build_move_cursor_axis(char *buf, size_t buf_size, int delta, bool horizontal, size_t *len_out); 16 + 17 + #endif
+41 -35
src/modules/readline.c
··· 27 27 #include "runtime.h" 28 28 #include "internal.h" 29 29 #include "descriptors.h" 30 + #include "tty_ctrl.h" 30 31 #include "silver/engine.h" 31 32 32 33 #include "modules/readline.h" ··· 1192 1193 } 1193 1194 1194 1195 static ant_value_t rl_clear_line(ant_t *js, ant_value_t *args, int nargs) { 1196 + (void)js; 1195 1197 if (nargs < 2) return js_false; 1196 1198 int dir = (int)js_getnum(args[1]); 1197 - 1198 - const char *seq; 1199 - switch (dir) { 1200 - case -1: seq = "\033[1K"; break; 1201 - case 1: seq = "\033[0K"; break; 1202 - case 0: 1203 - default: seq = "\033[2K\r"; break; 1204 - } 1205 - 1206 - fputs(seq, stdout); 1207 - fflush(stdout); 1208 - 1209 - return js_true; 1199 + 1200 + size_t seq_len = 0; 1201 + const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); 1202 + return js_bool(tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1210 1203 } 1211 1204 1212 1205 static ant_value_t rl_clear_screen_down(ant_t *js, ant_value_t *args, int nargs) { 1213 - (void)args; (void)nargs; 1214 - 1215 - printf("\033[J"); 1216 - fflush(stdout); 1217 - 1218 - return js_true; 1206 + (void)js; (void)args; (void)nargs; 1207 + 1208 + size_t seq_len = 0; 1209 + const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); 1210 + return js_bool(tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1219 1211 } 1220 1212 1221 1213 static ant_value_t rl_cursor_to(ant_t *js, ant_value_t *args, int nargs) { 1214 + (void)js; 1222 1215 if (nargs < 2) return js_false; 1223 1216 int x = (int)js_getnum(args[1]); 1224 - 1217 + 1218 + char seq[64]; 1219 + size_t seq_len = 0; 1220 + bool ok = false; 1225 1221 if (nargs >= 3 && vtype(args[2]) == T_NUM) { 1226 1222 int y = (int)js_getnum(args[2]); 1227 - printf("\033[%d;%dH", y + 1, x + 1); 1223 + ok = tty_ctrl_build_cursor_to(seq, sizeof(seq), x, true, y, &seq_len); 1228 1224 } else { 1229 - printf("\033[%dG", x + 1); 1225 + ok = tty_ctrl_build_cursor_to(seq, sizeof(seq), x, false, 0, &seq_len); 1230 1226 } 1231 - fflush(stdout); 1232 - 1233 - return js_true; 1227 + if (!ok) return js_false; 1228 + 1229 + return js_bool(tty_ctrl_write_stream(stdout, seq, seq_len, true)); 1234 1230 } 1235 1231 1236 1232 static ant_value_t rl_move_cursor(ant_t *js, ant_value_t *args, int nargs) { 1233 + (void)js; 1237 1234 if (nargs < 3) return js_false; 1238 - 1235 + 1239 1236 int dx = (int)js_getnum(args[1]); 1240 1237 int dy = (int)js_getnum(args[2]); 1241 - 1242 - if (dx > 0) printf("\033[%dC", dx); 1243 - else if (dx < 0) printf("\033[%dD", -dx); 1244 - 1245 - if (dy > 0) printf("\033[%dB", dy); 1246 - else if (dy < 0) printf("\033[%dA", -dy); 1247 - 1248 - fflush(stdout); 1249 - return js_true; 1238 + 1239 + bool ok = true; 1240 + if (dx != 0) { 1241 + char seq_x[32]; 1242 + size_t len_x = 0; 1243 + ok = tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x); 1244 + if (ok) ok = tty_ctrl_write_stream(stdout, seq_x, len_x, false); 1245 + } 1246 + 1247 + if (ok && dy != 0) { 1248 + char seq_y[32]; 1249 + size_t len_y = 0; 1250 + ok = tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y); 1251 + if (ok) ok = tty_ctrl_write_stream(stdout, seq_y, len_y, false); 1252 + } 1253 + 1254 + if (!ok) return js_false; 1255 + return js_bool(fflush(stdout) == 0); 1250 1256 } 1251 1257 1252 1258 static ant_value_t rl_emit_keypress_events(ant_t *js, ant_value_t *args, int nargs) {
+23 -52
src/modules/tty.c
··· 1 1 #include <compat.h> // IWYU pragma: keep 2 2 3 - #include <errno.h> 4 3 #include <limits.h> 5 4 #include <stdbool.h> 6 5 #include <stdio.h> ··· 14 13 #include <io.h> 15 14 #define WIN32_LEAN_AND_MEAN 16 15 #include <windows.h> 17 - #define ANT_WRITE_FD _write 18 16 #define ANT_ISATTY _isatty 19 17 #define ANT_STDIN_FD 0 20 18 #define ANT_STDOUT_FD 1 ··· 23 21 #include <sys/ioctl.h> 24 22 #include <termios.h> 25 23 #include <unistd.h> 26 - #define ANT_WRITE_FD write 27 24 #define ANT_ISATTY isatty 28 25 #define ANT_STDIN_FD STDIN_FILENO 29 26 #define ANT_STDOUT_FD STDOUT_FILENO ··· 35 32 #include "errors.h" 36 33 #include "internal.h" 37 34 #include "runtime.h" 35 + #include "tty_ctrl.h" 38 36 #include "silver/engine.h" 39 37 40 38 #include "modules/symbol.h" ··· 77 75 int fd = 0; 78 76 if (!parse_fd(fd_val, &fd)) return fallback_fd; 79 77 return fd; 80 - } 81 - 82 - static bool write_to_fd(int fd, const char *data, size_t len) { 83 - if (fd < 0 || !data) return false; 84 - if (len == 0) return true; 85 - 86 - size_t off = 0; 87 - while (off < len) { 88 - #ifdef _WIN32 89 - size_t rem = len - off; 90 - unsigned int chunk = (rem > (size_t)INT_MAX) ? (unsigned int)INT_MAX : (unsigned int)rem; 91 - int wrote = ANT_WRITE_FD(fd, data + off, chunk); 92 - if (wrote <= 0) return false; 93 - off += (size_t)wrote; 94 - #else 95 - ssize_t wrote = ANT_WRITE_FD(fd, data + off, len - off); 96 - if (wrote < 0) { 97 - if (errno == EINTR) continue; 98 - return false; 99 - } 100 - if (wrote == 0) return false; 101 - off += (size_t)wrote; 102 - #endif 103 - } 104 - return true; 105 78 } 106 79 107 80 static void get_tty_size(int fd, int *rows, int *cols) { ··· 386 359 if (nargs > 1 && is_callable(args[1])) cb = args[1]; 387 360 388 361 int fd = stream_fd_from_this(js, ANT_STDOUT_FD); 389 - bool ok = write_to_fd(fd, data, len); 362 + bool ok = tty_ctrl_write_fd(fd, data, len); 390 363 391 364 if (is_callable(cb)) { 392 365 if (ok) invoke_callback_if_needed(js, cb, js_mknull()); ··· 447 420 ant_value_t cb = js_mkundef(); 448 421 if (nargs > 1 && is_callable(args[1])) cb = args[1]; 449 422 450 - const char *seq = "\033[2K\r"; 451 - if (dir < 0) seq = "\033[1K"; 452 - else if (dir > 0) seq = "\033[0K"; 453 - 454 423 int fd = stream_fd_from_this(js, ANT_STDOUT_FD); 455 - bool ok = write_to_fd(fd, seq, strlen(seq)); 424 + size_t seq_len = 0; 425 + const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); 426 + bool ok = tty_ctrl_write_fd(fd, seq, seq_len); 456 427 return maybe_callback_or_throw(js, this_obj, cb, ok, "clearLine", fd); 457 428 } 458 429 ··· 462 433 ant_value_t cb = js_mkundef(); 463 434 if (nargs > 0 && is_callable(args[0])) cb = args[0]; 464 435 465 - static const char seq[] = "\033[0J"; 466 436 int fd = stream_fd_from_this(js, ANT_STDOUT_FD); 467 - bool ok = write_to_fd(fd, seq, sizeof(seq) - 1); 437 + size_t seq_len = 0; 438 + const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); 439 + bool ok = tty_ctrl_write_fd(fd, seq, seq_len); 468 440 return maybe_callback_or_throw(js, this_obj, cb, ok, "clearScreenDown", fd); 469 441 } 470 442 ··· 497 469 } 498 470 499 471 char seq[64]; 500 - int n = 0; 501 - if (has_y) n = snprintf(seq, sizeof(seq), "\033[%d;%dH", y + 1, x + 1); 502 - else n = snprintf(seq, sizeof(seq), "\033[%dG", x + 1); 503 - 504 - if (n < 0 || (size_t)n >= sizeof(seq)) return js_mkerr(js, "Failed to build cursor sequence"); 472 + size_t seq_len = 0; 473 + if (!tty_ctrl_build_cursor_to(seq, sizeof(seq), x, has_y, y, &seq_len)) { 474 + return js_mkerr(js, "Failed to build cursor sequence"); 475 + } 505 476 506 477 int fd = stream_fd_from_this(js, ANT_STDOUT_FD); 507 - bool ok = write_to_fd(fd, seq, (size_t)n); 478 + bool ok = tty_ctrl_write_fd(fd, seq, seq_len); 508 479 return maybe_callback_or_throw(js, this_obj, cb, ok, "cursorTo", fd); 509 480 } 510 481 ··· 528 499 529 500 if (dx != 0) { 530 501 char seq_x[32]; 531 - int n_x = 0; 532 - if (dx > 0) n_x = snprintf(seq_x, sizeof(seq_x), "\033[%dC", dx); 533 - else n_x = snprintf(seq_x, sizeof(seq_x), "\033[%dD", -dx); 534 - if (n_x < 0 || (size_t)n_x >= sizeof(seq_x)) return js_mkerr(js, "Failed to build moveCursor sequence"); 535 - if (!write_to_fd(fd, seq_x, (size_t)n_x)) ok = false; 502 + size_t len_x = 0; 503 + if (!tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x)) { 504 + return js_mkerr(js, "Failed to build moveCursor sequence"); 505 + } 506 + if (!tty_ctrl_write_fd(fd, seq_x, len_x)) ok = false; 536 507 } 537 508 538 509 if (ok && dy != 0) { 539 510 char seq_y[32]; 540 - int n_y = 0; 541 - if (dy > 0) n_y = snprintf(seq_y, sizeof(seq_y), "\033[%dB", dy); 542 - else n_y = snprintf(seq_y, sizeof(seq_y), "\033[%dA", -dy); 543 - if (n_y < 0 || (size_t)n_y >= sizeof(seq_y)) return js_mkerr(js, "Failed to build moveCursor sequence"); 544 - if (!write_to_fd(fd, seq_y, (size_t)n_y)) ok = false; 511 + size_t len_y = 0; 512 + if (!tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y)) { 513 + return js_mkerr(js, "Failed to build moveCursor sequence"); 514 + } 515 + if (!tty_ctrl_write_fd(fd, seq_y, len_y)) ok = false; 545 516 } 546 517 547 518 return maybe_callback_or_throw(js, this_obj, cb, ok, "moveCursor", fd);
+94
src/tty_ctrl.c
··· 1 + #include <compat.h> // IWYU pragma: keep 2 + #include "tty_ctrl.h" 3 + 4 + #include <errno.h> 5 + #include <stdio.h> 6 + #include <string.h> 7 + 8 + #ifdef _WIN32 9 + #include <io.h> 10 + #define ANT_WRITE_FD _write 11 + #else 12 + #include <unistd.h> 13 + #define ANT_WRITE_FD write 14 + #endif 15 + 16 + bool tty_ctrl_write_fd(int fd, const char *data, size_t len) { 17 + if (fd < 0 || !data) return false; 18 + if (len == 0) return true; 19 + 20 + size_t off = 0; 21 + while (off < len) { 22 + #ifdef _WIN32 23 + size_t rem = len - off; 24 + unsigned int chunk = (rem > (size_t)INT_MAX) ? (unsigned int)INT_MAX : (unsigned int)rem; 25 + int wrote = ANT_WRITE_FD(fd, data + off, chunk); 26 + if (wrote <= 0) return false; 27 + off += (size_t)wrote; 28 + #else 29 + ssize_t wrote = ANT_WRITE_FD(fd, data + off, len - off); 30 + if (wrote < 0) { 31 + if (errno == EINTR) continue; 32 + return false; 33 + } 34 + if (wrote == 0) return false; 35 + off += (size_t)wrote; 36 + #endif 37 + } 38 + 39 + return true; 40 + } 41 + 42 + bool tty_ctrl_write_stream(FILE *stream, const char *data, size_t len, bool flush) { 43 + if (!stream || !data) return false; 44 + if (len > 0 && fwrite(data, 1, len, stream) != len) return false; 45 + if (flush && fflush(stream) != 0) return false; 46 + return true; 47 + } 48 + 49 + const char *tty_ctrl_clear_line_seq(int dir, size_t *len_out) { 50 + const char *seq = "\033[2K\r"; 51 + if (dir < 0) seq = "\033[1K"; 52 + else if (dir > 0) seq = "\033[0K"; 53 + 54 + if (len_out) *len_out = strlen(seq); 55 + return seq; 56 + } 57 + 58 + const char *tty_ctrl_clear_screen_down_seq(size_t *len_out) { 59 + static const char seq[] = "\033[0J"; 60 + if (len_out) *len_out = sizeof(seq) - 1; 61 + return seq; 62 + } 63 + 64 + bool tty_ctrl_build_cursor_to(char *buf, size_t buf_size, int x, bool has_y, int y, size_t *len_out) { 65 + if (!buf || buf_size == 0) return false; 66 + 67 + int n = 0; 68 + if (has_y) n = snprintf(buf, buf_size, "\033[%d;%dH", y + 1, x + 1); 69 + else n = snprintf(buf, buf_size, "\033[%dG", x + 1); 70 + 71 + if (n < 0 || (size_t)n >= buf_size) return false; 72 + if (len_out) *len_out = (size_t)n; 73 + return true; 74 + } 75 + 76 + bool tty_ctrl_build_move_cursor_axis(char *buf, size_t buf_size, int delta, bool horizontal, size_t *len_out) { 77 + if (!buf || buf_size == 0) return false; 78 + 79 + if (delta == 0) { 80 + if (len_out) *len_out = 0; 81 + return true; 82 + } 83 + 84 + int amount = (delta < 0) ? -delta : delta; 85 + char cmd = 0; 86 + if (horizontal) cmd = (delta > 0) ? 'C' : 'D'; 87 + else cmd = (delta > 0) ? 'B' : 'A'; 88 + 89 + int n = snprintf(buf, buf_size, "\033[%d%c", amount, cmd); 90 + if (n < 0 || (size_t)n >= buf_size) return false; 91 + 92 + if (len_out) *len_out = (size_t)n; 93 + return true; 94 + }