Commit: 6160a04640f513c17cf282212f0444cb954db875
Parent: 9edafe03dabd3bca362786b4e7d98ed8d21a2c34
Author: Randy Palamar
Date: Sat, 15 Feb 2025 15:28:05 -0700
terminal: fix cursor up handling
Diffstat:
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)
{