vtgl

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

Commit: 6160a04640f513c17cf282212f0444cb954db875
Parent: 9edafe03dabd3bca362786b4e7d98ed8d21a2c34
Author: Randy Palamar
Date:   Sat, 15 Feb 2025 15:28:05 -0700

terminal: fix cursor up handling

Diffstat:
Mterminal.c | 11++++++++++-
Mtests/test.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/terminal.c b/terminal.c @@ -373,6 +373,15 @@ cursor_step_raw(Term *t, i32 step, i32 rows, i32 cols) } static void +cursor_up(Term *t, i32 requested_count) +{ + i32 cursor_y = t->cursor.pos.y; + i32 max = cursor_y >= t->top ? (cursor_y - t->top) : cursor_y; + i32 count = MIN(max, MAX(requested_count, 1)); + cursor_move_to(t, t->cursor.pos.y - count, t->cursor.pos.x); +} + +static void cursor_down(Term *t, i32 requested_count) { i32 cursor_y = t->cursor.pos.y; @@ -933,7 +942,7 @@ handle_csi(Term *t, CSI *csi) iv2 p = t->cursor.pos; switch (csi->mode) { - case 'A': cursor_step_raw(t, ORONE(csi->argv[0]), -1, 0); break; + case 'A': cursor_up(t, csi->argv[0]); break; case 'B': cursor_down(t, csi->argv[0]); break; case 'C': cursor_step_raw(t, ORONE(csi->argv[0]), 0, 1); break; case 'D': cursor_step_raw(t, ORONE(csi->argv[0]), 0, -1); break; diff --git a/tests/test.c b/tests/test.c @@ -42,7 +42,6 @@ typedef TEST_FN(Test_Fn); * - Cursor Next Line (CNL) * - Cursor Previous Line (CPL) * - Cursor Forward (CUF) - * - Cursor Up (CUU) * - Set Left and Right Margins (DECSLRM) */ @@ -67,6 +66,9 @@ typedef TEST_FN(Test_Fn); X(cursor_position_v4) \ X(cursor_position_v5) \ X(cursor_position_v6) \ + X(cursor_up_v1) \ + X(cursor_up_v2) \ + X(cursor_up_v3) \ X(delete_characters_v1) \ X(delete_characters_v2) \ X(delete_characters_v3) \ @@ -771,6 +773,64 @@ static TEST_FN(cursor_position_v6) return result; } +/* NOTE: CUU V-1: Cursor Up */ +static TEST_FN(cursor_up_v1) +{ + struct test_result result = {.info = __FUNCTION__}; + + launder_static_string(term, CSI(3;1H)); + launder_static_string(term, s8("A")); + launder_static_string(term, CSI(2A)); + s8 raw = launder_static_string(term, s8("X")); + handle_input(term, arena, raw); + + result.status = term->views[term->view_idx].fb.rows[0][1].cp == 'X'; + result.status &= term->views[term->view_idx].fb.rows[2][0].cp == 'A'; + result.status &= term->cursor.pos.y == 0 && term->cursor.pos.x == 2; + + return result; +} + +/* NOTE: CUU V-2: Cursor Up Below Top Margin */ +static TEST_FN(cursor_up_v2) +{ + struct test_result result = {.info = __FUNCTION__}; + + launder_static_string(term, CSI(2;4r)); + launder_static_string(term, CSI(3;1H)); + launder_static_string(term, s8("A")); + launder_static_string(term, CSI(5A)); + s8 raw = launder_static_string(term, s8("X")); + handle_input(term, arena, raw); + + result.status = term->views[term->view_idx].fb.rows[1][1].cp == 'X'; + result.status &= term->views[term->view_idx].fb.rows[2][0].cp == 'A'; + result.status &= term->cursor.pos.y == 1 && term->cursor.pos.x == 2; + + return result; +} + +/* NOTE: CUU V-3: Cursor Up Above Top Margin */ +static TEST_FN(cursor_up_v3) +{ + struct test_result result = {.info = __FUNCTION__}; + + launder_static_string(term, CSI(3;5r)); + launder_static_string(term, CSI(3;1H)); + launder_static_string(term, s8("A")); + launder_static_string(term, CSI(2;1H)); + launder_static_string(term, CSI(5A)); + s8 raw = launder_static_string(term, s8("X")); + handle_input(term, arena, raw); + + result.status = term->views[term->view_idx].fb.rows[0][0].cp == 'X'; + result.status &= term->views[term->view_idx].fb.rows[2][0].cp == 'A'; + result.status &= term->cursor.pos.y == 0 && term->cursor.pos.x == 1; + + return result; +} + + /* NOTE: DCH V-1: Simple Delete Character */ static TEST_FN(delete_characters_v1) {