vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

Commit: dec1f3d5e9f824da6abb36545b2a3a07054cd0c7
Parent: c59760708c5c34d620221773c128f39e70d4ac85
Author: Randy Palamar
Date:   Mon, 17 Feb 2025 16:38:32 -0700

terminal: support device status report CSI (DSR)

Diffstat:
Mterminal.c | 23+++++++++++++++++++++++
Mtests/test-common.c | 11+++++++++++
Mtests/test.c | 41++++++++++++++++++++++++++++++++++++++++-
3 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/terminal.c b/terminal.c @@ -1009,6 +1009,29 @@ handle_csi(Term *t, CSI *csi) case 'h': set_mode(t, csi, 1, MODE_STATE_ALL_MASK, &t->mode); break; case 'l': set_mode(t, csi, 0, MODE_STATE_ALL_MASK, &t->mode); break; case 'm': set_colours(t, csi); break; + case 'n': { + switch (csi->argv[0]) { + case 5: /* NOTE: DSR V-1: Operating Status */ + t->platform->write(t->child, s8("\x1B[0n")); + break; + case 6: /* NOTE: DSR V-2: Cursor Position */ { + iv2 cpos = t->cursor.pos; + if (t->cursor.state & CURSOR_ORIGIN) { + /* TODO(rnp): left/right margin */ + //cpos.x = MAX(0, cpos.x - t->margin.left); + cpos.y = MAX(0, cpos.y - t->top); + } + Stream buf = arena_stream(t->arena_for_frame); + stream_push_s8(&buf, s8("\x1B[")); + stream_push_i64(&buf, cpos.y + 1); + stream_push_byte(&buf, ';'); + stream_push_i64(&buf, cpos.x + 1); + stream_push_byte(&buf, 'R'); + t->platform->write(t->child, stream_to_s8(&buf)); + } break; + default: goto unknown; + } + } break; case 'r': if (csi->priv) { /* NOTE: XTRESTORE: restore the value of a private mode */ diff --git a/tests/test-common.c b/tests/test-common.c @@ -13,6 +13,16 @@ KEYBIND_FN(zoom) { return 0; } #include "font.c" #include "terminal.c" +static PLATFORM_WRITE_FN(test_write) +{ + /* NOTE(rnp): for testing the caller will provide a stream via the platform + * child handle. Then this function writes into it and the caller can compare + * with the expected results */ + Stream *s = (Stream *)file; + stream_push_s8(s, raw); + return !s->errors; +} + static PLATFORM_WINDOW_TITLE_FN(test_get_window_title) { ASSERT(buffer); @@ -66,6 +76,7 @@ place_term_into_memory(MemoryBlock memory, i32 rows, i32 columns) t->platform = push_struct(&tmp, typeof(*t->platform)); t->platform->set_window_title = test_set_window_title; t->platform->get_window_title = test_get_window_title; + t->platform->write = test_write; t->arena_for_frame = tmp; diff --git a/tests/test.c b/tests/test.c @@ -19,6 +19,7 @@ typedef TEST_FN(Test_Fn); * - Cursor Previous Line (CPL) * - Cursor Forward (CUF) * - Set Left and Right Margins (DECSLRM) + * - Delete Line (DL) */ #define GHOSTTY_TESTS \ @@ -53,7 +54,9 @@ typedef TEST_FN(Test_Fn); X(set_top_bottom_margins_v1) \ X(set_top_bottom_margins_v2) \ X(set_top_bottom_margins_v3) \ - X(set_top_bottom_margins_v4) + X(set_top_bottom_margins_v4) \ + X(device_status_report_v1) \ + X(device_status_report_v2) #define X(fn) static TEST_FN(fn); TESTS @@ -1002,6 +1005,42 @@ static TEST_FN(set_top_bottom_margins_v4) return result; } +/* NOTE: DSR V-1: Operating Status */ +static TEST_FN(device_status_report_v1) +{ + struct test_result result = {.info = __FUNCTION__}; + + Stream buffer = {.buf = malloc(KB(1)), .cap = KB(1)}; + term->child = (iptr)&buffer; + s8 raw = launder_static_string(term, CSI(5n)); + handle_input(term, arena, raw); + + result.status = s8_equal(s8("\x1B[0n"), stream_to_s8(&buffer)); + + free(buffer.buf); + + return result; +} + +/* NOTE: DSR V-2: Cursor Position */ +static TEST_FN(device_status_report_v2) +{ + struct test_result result = {.info = __FUNCTION__}; + + Stream buffer = {.buf = malloc(KB(1)), .cap = KB(1)}; + term->child = (iptr)&buffer; + + launder_static_string(term, CSI(2;4H)); + s8 raw = launder_static_string(term, CSI(6n)); + handle_input(term, arena, raw); + + result.status = s8_equal(s8("\x1B[2;4R"), stream_to_s8(&buffer)); + + free(buffer.buf); + + return result; +} + int main(void) {