Commit: dec1f3d5e9f824da6abb36545b2a3a07054cd0c7
Parent: c59760708c5c34d620221773c128f39e70d4ac85
Author: Randy Palamar
Date: Mon, 17 Feb 2025 16:38:32 -0700
terminal: support device status report CSI (DSR)
Diffstat:
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)
{