vtgl

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

Commit: 8b63ad912b83a4103f5745d979af7c6a19b93228
Parent: bf4d6cd4d7ace61e9aff996f6ab1f00b6cacfd63
Author: Randy Palamar
Date:   Sun, 25 Aug 2024 12:46:33 -0600

CBT: cursor backwards tabulation (move cursor backwards to tabstop)

Diffstat:
Mterminal.c | 19++++++++++++-------
Mtest.c | 54+++++++++++++++++++++++++++++++++++++++---------------
Mutil.h | 6++++--
3 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/terminal.c b/terminal.c @@ -247,10 +247,13 @@ cursor_alt(Term *t, b32 save) } +/* NOTE: advance the cursor by <n> cells; handles reverse movement */ static void -cursor_step_column(Term *t, i32 step) +cursor_step_column(Term *t, i32 n) { - t->cursor.pos.x += step; + t->cursor.pos.x += n; + if (t->cursor.pos.x < 0) + t->cursor.pos.x = 0; if (t->cursor.pos.x < t->size.w) return; t->cursor.pos.x = 0; @@ -581,11 +584,12 @@ push_newline(Term *t) } static void -push_tab(Term *t) +push_tab(Term *t, i32 n) { - u32 advance = g_tabstop - (t->cursor.pos.x % g_tabstop); - fb_clear_region(t, t->cursor.pos.y, t->cursor.pos.y, t->cursor.pos.x, t->cursor.pos.x + advance); - cursor_step_column(t, advance); + i32 n_abs = ABS(n); + i32 n_sgn = SGN(n); + i32 advance = n_abs * g_tabstop - (t->cursor.pos.x % g_tabstop); + cursor_step_column(t, n_sgn * advance); } static i32 @@ -647,6 +651,7 @@ handle_csi(Term *t, s8 *raw) case 'J': erase_in_display(t, &csi); break; case 'K': erase_in_line(t, &csi); break; case 'X': erase_characters(t, ORONE(csi.argv[0])); break; + case 'Z': push_tab(t, -(ORONE(csi.argv[0]))); break; case 'a': cursor_step_raw(t, ORONE(csi.argv[0]), 0, 1); break; case 'd': cursor_move_to(t, csi.argv[0] - 1, p.x); break; case 'e': cursor_step_raw(t, ORONE(csi.argv[0]), 1, 0); break; @@ -988,7 +993,7 @@ push_line(Term *t, Line *line, Arena a) case 0x1B: handle_escape(t, &l, a); break; case '\r': t->cursor.pos.x = 0; break; case '\n': push_newline(t); break; - case '\t': push_tab(t); break; + case '\t': push_tab(t, 1); break; case '\a': /* TODO: ding ding? */ break; case '\b': cursor_move_to(t, t->cursor.pos.y, t->cursor.pos.x - 1); diff --git a/test.c b/test.c @@ -20,6 +20,7 @@ typedef TEST_FN(Test_Fn); X(colour_setting) \ X(cursor_at_line_boundary) \ X(cursor_movement) \ + X(cursor_tabs) \ X(working_ringbuffer) #define X(fn) static TEST_FN(fn); @@ -86,6 +87,31 @@ launder_static_string(Term *term, s8 static_str) return raw; } +static TEST_FN(colour_setting) +{ + struct test_result result = {.info = __FUNCTION__}; + + launder_static_string(term, CSI(8m)); + launder_static_string(term, CSI(4m)); + launder_static_string(term, CSI(9m)); + launder_static_string(term, CSI(24m)); + launder_static_string(term, CSI(33m)); + launder_static_string(term, CSI(48;2;75;63;42m)); + s8 raw = launder_static_string(term, s8("A")); + + size parsed_lines = split_raw_input_to_lines(term, raw); + blit_lines(term, arena, parsed_lines); + + Cell c = { .cp = 'A', .style = { + .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}, + .fg = g_colours.data[3], + .attr = (ATTR_INVISIBLE|ATTR_STRUCK), + }}; + result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]); + + return result; +} + static TEST_FN(cursor_movement) { struct test_result result = {.info = __FUNCTION__}; @@ -103,27 +129,24 @@ static TEST_FN(cursor_movement) return result; } -static TEST_FN(colour_setting) +static TEST_FN(cursor_tabs) { struct test_result result = {.info = __FUNCTION__}; - launder_static_string(term, CSI(8m)); - launder_static_string(term, CSI(4m)); - launder_static_string(term, CSI(9m)); - launder_static_string(term, CSI(24m)); - launder_static_string(term, CSI(33m)); - launder_static_string(term, CSI(48;2;75;63;42m)); - s8 raw = launder_static_string(term, s8("A")); - + /* NOTE: first test advancing to a tabstop */ + s8 raw = launder_static_string(term, s8("123\t")); size parsed_lines = split_raw_input_to_lines(term, raw); blit_lines(term, arena, parsed_lines); - Cell c = { .cp = 'A', .style = { - .bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF}, - .fg = g_colours.data[3], - .attr = (ATTR_INVISIBLE|ATTR_STRUCK), - }}; - result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]); + result.status = term->cursor.pos.x == (g_tabstop); + + /* NOTE: now test negative tabstop movement */ + launder_static_string(term, s8("\t1234\t")); + raw = launder_static_string(term, CSI(2Z)); + parsed_lines = split_raw_input_to_lines(term, raw); + blit_lines(term, arena, parsed_lines); + + result.status &= term->cursor.pos.x == (g_tabstop); return result; } @@ -217,6 +240,7 @@ main(void) for (u32 i = 0; i < ARRAY_COUNT(tests); i++) { buffer_reset(&term); cursor_reset(&term); + cursor_move_to(&term, 0, 0); struct test_result result = tests[i](&term, memory); if (result.status == 0) { failure_count++; diff --git a/util.h b/util.h @@ -24,10 +24,12 @@ #define MEGABYTE (1024ULL * 1024ULL) #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a)) +#define ABS(a) ((a) < 0 ? (-a) : (a)) #define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b)) -#define MIN(a, b) ((a) <= (b) ? (a) : (b)) -#define MAX(a, b) ((a) >= (b) ? (a) : (b)) #define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)) +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +#define MIN(a, b) ((a) <= (b) ? (a) : (b)) +#define SGN(a) ((a) < 0 ? (-1) : (1)) #define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t') #define ISPRINT(c) BETWEEN((c), ' ', '~')