vtgl

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

Commit: 2f79cb6313b4316457fe9f2f3333575ec8c69386
Parent: d49dcdf55714cc86063d211aa80d0ee7c541b153
Author: Randy Palamar
Date:   Sun, 11 Aug 2024 15:53:28 -0600

properly ensure utf-8 is added to line_end and add this test case

Diffstat:
Mterminal.c | 19++++++++++++++-----
Mtest.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/terminal.c b/terminal.c @@ -711,7 +711,6 @@ split_raw_input_to_lines(Term *t, s8 raw) lb->buf[lb->widx].has_unicode |= _mm_movemask_epi8(hasutf8); raw = consume(raw, data - raw.data); - u32 last_c = 0; if (raw.len) { if (peek(raw, 0) == 0x1B) { s8 old = raw; @@ -727,12 +726,22 @@ split_raw_input_to_lines(Term *t, s8 raw) default: break; } } else { - last_c = get_ascii(&raw); - if (last_c == '\n') { + u32 cp = peek(raw, 0); + if (cp == '\n') { + raw = consume(raw, 1); parsed_lines++; feed_line(lb, raw.data, t->cursor.state); - } else if (last_c & 0x80) { + } else if (cp & 0x80) { lb->buf[lb->widx].has_unicode = 1; + /* TODO: this is probably slow */ + size old_len = raw.len; + if (get_utf8(&raw) == (u32)-1) { + /* NOTE: Need More Bytes! */ + t->unprocessed_bytes = old_len; + return parsed_lines; + } + } else { + raw = consume(raw, 1); } } } @@ -740,7 +749,7 @@ split_raw_input_to_lines(Term *t, s8 raw) lb->buf[lb->widx].end = raw.data; //if (tv->lines.buf[tv->lines.widx].end < tv->lines.buf[tv->lines.widx].start) // tv->lines.buf[tv->lines.widx].end += tv->log.cap; - if (line_length(lb->buf + lb->widx) > SPLIT_LONG && !(last_c & 0x80)) { + if (line_length(lb->buf + lb->widx) > SPLIT_LONG) { parsed_lines++; feed_line(lb, raw.data, t->cursor.state); } diff --git a/test.c b/test.c @@ -5,6 +5,8 @@ static const char *glfwGetWindowTitle(GLFWwindow *w) { return "test"; }; #include "util.h" +#include <string.h> /* memcmp */ + #define ESC(a) s8("\x1B"#a) #define CSI(a) ESC([a) @@ -12,8 +14,9 @@ struct test_result { b32 status; const char *info; }; #define TEST_FN(name) struct test_result name(Term *term, Arena arena) typedef TEST_FN(Test_Fn); -#define TESTS \ - X(colour_setting) \ +#define TESTS \ + X(colour_setting) \ + X(cursor_at_line_boundary) \ X(cursor_movement) #define X(fn) static TEST_FN(fn); @@ -25,6 +28,16 @@ static Test_Fn *tests[] = { TESTS }; +static void +buffer_reset(Term *t) +{ + t->log.widx = 0; + t->log.filled = 0; + t->unprocessed_bytes = 0; + t->log_lines.widx = 0; + t->log_lines.filled = 0; +} + static size copy_into_ringbuf(RingBuf *rb, s8 raw) { @@ -107,6 +120,55 @@ static TEST_FN(colour_setting) return result; } +static TEST_FN(cursor_at_line_boundary) +{ + /* NOTE: two tests here + * 1. split_raw_input_to_lines should modify cursor style properties + * 2. split_raw_input_to_lines should not split utf-8 characters into separate lines + */ + struct test_result result = {.info = __FUNCTION__}; + + s8 long_line = s8alloc(&arena, 8192); + mem_clear(long_line.data, ' ', long_line.len); + + /* NOTE: change the cursor state in the middle of the line */ + long_line.data[128 + 0] = 0x1B; + long_line.data[128 + 1] = '['; + long_line.data[128 + 2] = '5'; + long_line.data[128 + 3] = 'm'; + + /* NOTE: place a non-ASCII character on the long line edge */ + s8 red_dragon = utf8_encode(0x1F004); + long_line.data[SPLIT_LONG - 1] = red_dragon.data[0]; + long_line.data[SPLIT_LONG + 0] = red_dragon.data[1]; + long_line.data[SPLIT_LONG + 1] = red_dragon.data[2]; + long_line.data[SPLIT_LONG + 2] = red_dragon.data[3]; + + /* NOTE: shove a newline at the end so that finish parsing the second line */ + long_line.data[long_line.len - 1] = '\n'; + + #if 0 + long_line.data[SPLIT_LONG + 3] = 0; + printf("encoded char: %s\n", (char *)(long_line.data + SPLIT_LONG - 1)); + long_line.data[SPLIT_LONG + 3] = ' '; + #endif + + s8 raw = launder_static_string(term, long_line); + + /* NOTE: check if line actually split and that it was split after red dragon */ + size parsed_lines = split_raw_input_to_lines(term, raw); + result.status = parsed_lines == 2; + result.status &= line_length(term->log_lines.buf) > SPLIT_LONG; + + /* NOTE: check that cursor state was transferred */ + Cursor line_end = simulate_line(term, term->log_lines.buf); + result.status &= !memcmp(&line_end.state, + &term->log_lines.buf[1].cursor_state, + sizeof(term->log_lines.buf[0].cursor_state)); + + return result; +} + static u32 failure_count; int @@ -125,6 +187,7 @@ main(void) os_alloc_framebuffer(&term.fb, term.size.h, term.size.w); for (u32 i = 0; i < ARRAY_COUNT(tests); i++) { + buffer_reset(&term); cursor_reset(&term); struct test_result result = tests[i](&term, memory); if (result.status == 0) {