Commit: 9d496646ed60c7474aa532fbe0b15d65f557a6fe
Parent: 0fb0a5581dfe26f48d6959528c31d09042fbaf56
Author: Randy Palamar
Date: Sun, 27 Oct 2024 22:13:05 -0600
disable split_raw_input_to_lines for now
the secondary path has quite a few issues due to the need to
maintain consitent state. the input can still be split into lines
in the primary path for use in scrollback. This fixes a number of
bugs related to displaying things incorrectly (especially in the
alt screen).
Diffstat:
M | terminal.c | | | 190 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- |
M | test.c | | | 30 | ++++++++++++------------------ |
M | util.c | | | 2 | +- |
M | vtgl.c | | | 7 | +++++++ |
4 files changed, 165 insertions(+), 64 deletions(-)
diff --git a/terminal.c b/terminal.c
@@ -240,7 +240,6 @@ swap_screen(Term *t)
{
t->mode ^= TM_ALTSCREEN;
t->view_idx = !!(t->mode & TM_ALTSCREEN);
- t->gl.flags |= NEEDS_FULL_REFILL;
}
static void
@@ -1108,6 +1107,8 @@ end:
return result;
}
+#define SPLIT_LONG 4096L
+#if 0
static size
split_raw_input_to_lines(Term *t, s8 raw)
{
@@ -1210,6 +1211,59 @@ end:
return parsed_lines;
}
+#endif
+
+static void
+push_normal_cp(Term *t, TermView *tv, u32 cp)
+{
+ BEGIN_TIMED_BLOCK();
+
+ if (t->mode & TM_AUTO_WRAP && t->cursor.state & CURSOR_WRAP_NEXT)
+ push_newline(t, 1);
+
+ u32 width = 1;
+ if (cp > 0x7F) {
+ /* TODO: this is obviously complete crap but wcwidth from libc doesn't
+ * actually work so we must check from the rendered glyph */
+ CachedGlyph *cg;
+ get_gpu_glyph_index(t->arena_for_frame, &t->gl, &t->fa, cp, 0, 0, &cg);
+ width = cg->tile_count;
+ }
+
+ /* NOTE: make this '>=' for fun in vis */
+ if (t->cursor.pos.x + width > t->size.w) {
+ /* NOTE: make space for character if mode enabled else
+ * clobber whatever was on the end of the line */
+ if (t->mode & TM_AUTO_WRAP)
+ push_newline(t, 1);
+ else
+ cursor_move_to(t, t->cursor.pos.y, t->size.w - width);
+ }
+
+ Cell *c = &tv->fb.rows[t->cursor.pos.y][t->cursor.pos.x];
+ /* TODO: pack cell into ssbo */
+ c->cp = cp;
+ c->style = t->cursor.style;
+
+ for (u32 w = width - 1; w; w--) {
+ c->style.attr |= ATTR_WIDE;
+ if (t->cursor.pos.x + w < t->size.w) {
+ Cell *nc = c + w;
+ nc->style.attr |= ATTR_WDUMMY;
+ }
+ }
+
+ if (t->cursor.pos.x + width < t->size.w)
+ cursor_step_column(t, width);
+ else
+ t->cursor.state |= CURSOR_WRAP_NEXT;
+
+ /* TODO: region detection */
+ if (is_selected(&t->selection, t->cursor.pos.x, t->cursor.pos.y))
+ selection_clear(&t->selection);
+
+ END_TIMED_BLOCK();
+}
static void
push_line(Term *t, Line *line, Arena a)
@@ -1220,7 +1274,6 @@ push_line(Term *t, Line *line, Arena a)
s8 l = line_to_s8(line, &tv->log);
t->cursor.style = line->cursor_state;
- Cell *c;
while (l.len) {
u32 cp;
if (line->has_unicode) cp = get_utf8(&l);
@@ -1241,49 +1294,7 @@ push_line(Term *t, Line *line, Arena a)
continue;
}
- if (t->mode & TM_AUTO_WRAP && t->cursor.state & CURSOR_WRAP_NEXT)
- push_newline(t, 1);
-
- u32 width;
- if (line->has_unicode) {
- /* TODO: this is obviously complete crap but wcwidth from libc doesn't
- * actually work so we must check from the rendered glyph */
- CachedGlyph *cg;
- get_gpu_glyph_index(t->arena_for_frame, &t->gl, &t->fa, cp, 0, 0, &cg);
- width = cg->tile_count;
- } else {
- width = 1;
- }
-
- /* NOTE: make this '>=' for fun in vis */
- if (t->cursor.pos.x + width > t->size.w) {
- /* NOTE: make space for character if mode enabled else
- * clobber whatever was on the end of the line */
- if (t->mode & TM_AUTO_WRAP)
- push_newline(t, 1);
- else
- cursor_move_to(t, t->cursor.pos.y, t->size.w - width);
- }
-
- c = &tv->fb.rows[t->cursor.pos.y][t->cursor.pos.x];
- c->cp = cp;
- c->style = t->cursor.style;
-
- if (width == 2) {
- c->style.attr |= ATTR_WIDE;
- if (t->cursor.pos.x + 1 < t->size.w) {
- Cell *nc = c + 1;
- nc->style.attr |= ATTR_WDUMMY;
- }
- }
-
- if (t->cursor.pos.x + width < t->size.w)
- cursor_step_column(t, width);
- else
- t->cursor.state |= CURSOR_WRAP_NEXT;
-
- if (is_selected(&t->selection, t->cursor.pos.x, t->cursor.pos.y))
- selection_clear(&t->selection);
+ push_normal_cp(t, tv, cp);
}
END_TIMED_BLOCK();
}
@@ -1314,10 +1325,12 @@ blit_lines(Term *t, Arena a, size line_count)
CLAMP(line_count, 0, tv->lines.filled);
for (size idx = -line_count; idx <= 0; idx++) {
size line_idx = get_line_idx(&tv->lines, idx - off);
+ #if 0
if (line_idx == tv->last_line_idx)
t->cursor.pos = tv->last_cursor_pos;
tv->last_cursor_pos = t->cursor.pos;
tv->last_line_idx = line_idx;
+ #endif
push_line(t, tv->lines.buf + line_idx, a);
/* TODO: can we avoid this? */
ASSERT(t->escape == 0);
@@ -1327,3 +1340,90 @@ blit_lines(Term *t, Arena a, size line_count)
END_TIMED_BLOCK();
}
+
+static void
+handle_input(Term *t, Arena a, s8 raw)
+{
+ BEGIN_TIMED_BLOCK();
+
+ TermView *tv = t->views + t->view_idx;
+
+ /* TODO: SIMD look ahead */
+ while (raw.len) {
+ size start_len = raw.len;
+ u32 cp = peek(raw, 0);
+ if (cp & 0x80) {
+ cp = get_utf8(&raw);
+ tv->lines.buf[tv->lines.widx].has_unicode = 1;
+ if (cp == (u32)-1) {
+ /* NOTE: Need More Bytes! */
+ raw.len = start_len;
+ goto end;
+ }
+ } else {
+ cp = get_ascii(&raw);
+ }
+
+ /* TODO: cleanup */
+ if (cp == '\n')
+ feed_line(&tv->lines, raw.data, t->cursor.style);
+
+ ASSERT(cp != (u32)-1);
+
+ if (ISCONTROL(cp)) {
+ if (cp == 0x1B && raw.len == 0) {
+ raw.len = start_len;
+ goto end;
+ } else {
+ iv2 old_curs = t->cursor.pos;
+ push_control(t, &raw, cp, a);
+ if (!equal_iv2(t->cursor.pos, old_curs)) {
+ if (line_length(tv->lines.buf + tv->lines.widx))
+ feed_line(&tv->lines, raw.data, t->cursor.style);
+ }
+ }
+ continue;
+ } else if (t->escape & EM_CSI) {
+ t->csi.raw.len++;
+ if (BETWEEN(cp, '@', '~')) {
+ iv2 old_curs = t->cursor.pos;
+ i32 mode = t->mode & TM_ALTSCREEN;
+ handle_csi(t, &t->csi);
+ t->escape &= ~EM_CSI;
+ if ((t->mode & TM_ALTSCREEN) != mode) {
+ u8 *old = raw.data - t->csi.raw.len - 2;
+ ASSERT(*old == 0x1B);
+ feed_line(&tv->lines, old, t->cursor.style);
+ TermView *nv = t->views + t->view_idx;
+ size nstart = nv->log.widx;
+ mem_copy(raw, (s8){nv->log.cap, nv->log.buf + nstart});
+ commit_to_rb(tv, -raw.len);
+ commit_to_rb(nv, raw.len);
+ raw.data = nv->log.buf + nstart;
+ init_line(nv->lines.buf + nv->lines.widx, raw.data,
+ t->cursor.style);
+ tv = nv;
+ } else if (!equal_iv2(t->cursor.pos, old_curs)) {
+ if (line_length(tv->lines.buf + tv->lines.widx))
+ feed_line(&tv->lines, raw.data, t->cursor.style);
+ }
+ }
+ continue;
+ }
+
+ push_normal_cp(t, tv, cp);
+
+ }
+end:
+ tv->lines.buf[tv->lines.widx].end = raw.data;
+
+ /* TODO: this shouldn't be needed */
+ if (tv->lines.buf[tv->lines.widx].end < tv->lines.buf[tv->lines.widx].start)
+ tv->lines.buf[tv->lines.widx].start -= tv->log.cap;
+
+ if (!t->escape && line_length(tv->lines.buf + tv->lines.widx) > SPLIT_LONG)
+ feed_line(&tv->lines, raw.data, t->cursor.style);
+
+ t->unprocessed_bytes = raw.len;
+ END_TIMED_BLOCK();
+}
diff --git a/test.c b/test.c
@@ -138,8 +138,7 @@ static TEST_FN(csi_embedded_control)
launder_static_string(term, s8("1"));
launder_static_string(term, CSI(48;2;7\b5;63;4\n2m));
s8 raw = launder_static_string(term, s8(" 2"));
- size parsed_lines = split_raw_input_to_lines(term, raw);
- blit_lines(term, arena, parsed_lines);
+ handle_input(term, arena, raw);
#if 0
dump_csi(&term->csi);
@@ -174,8 +173,7 @@ static TEST_FN(colour_setting)
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);
+ handle_input(term, arena, raw);
Cell c = { .cp = 'A', .style = {
.bg = (Colour){.r = 75, .g = 63, .b = 42, .a = 0xFF},
@@ -192,12 +190,10 @@ static TEST_FN(cursor_movement)
struct test_result result = {.info = __FUNCTION__};
s8 raw = launder_static_string(term, CSI(17;2H));
- size parsed_lines = split_raw_input_to_lines(term, raw);
- blit_lines(term, arena, parsed_lines);
+ handle_input(term, arena, raw);
raw = launder_static_string(term, CSI(7G));
- parsed_lines = split_raw_input_to_lines(term, raw);
- blit_lines(term, arena, parsed_lines);
+ handle_input(term, arena, raw);
result.status = term->cursor.pos.y == 16 && term->cursor.pos.x == 6;
@@ -209,9 +205,8 @@ static TEST_FN(cursor_tabs)
struct test_result result = {.info = __FUNCTION__};
/* 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);
+ s8 raw = launder_static_string(term, s8("123\t"));
+ handle_input(term, arena, raw);
result.status = term->cursor.pos.x == (g_tabstop);
@@ -220,8 +215,7 @@ static TEST_FN(cursor_tabs)
launder_static_string(term, ESC(H));
launder_static_string(term, s8("34\t"));
raw = launder_static_string(term, CSI(2Z));
- parsed_lines = split_raw_input_to_lines(term, raw);
- blit_lines(term, arena, parsed_lines);
+ handle_input(term, arena, raw);
result.status &= term->cursor.pos.x == (g_tabstop);
@@ -252,7 +246,7 @@ static TEST_FN(cursor_at_line_boundary)
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 */
+ /* NOTE: shove a newline at the end so that the line completes */
long_line.data[long_line.len - 1] = '\n';
#if 0
@@ -264,10 +258,10 @@ static TEST_FN(cursor_at_line_boundary)
s8 raw = launder_static_string(term, long_line);
LineBuf *lb = &term->views[term->view_idx].lines;
- /* 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(lb->buf) > SPLIT_LONG;
+ size line_count = lb->filled;
+ /* NOTE: ensure line didn't split on red dragon */
+ handle_input(term, arena, raw);
+ result.status = line_length(lb->buf) > SPLIT_LONG;
/* NOTE: check that cursor state was transferred */
Cursor line_end = simulate_line(term, lb->buf);
diff --git a/util.c b/util.c
@@ -98,7 +98,7 @@ make_arena(Arena *a, size size)
static void
commit_to_rb(TermView *tv, size len)
{
- ASSERT(len <= tv->log.cap);
+ ASSERT(ABS(len) <= tv->log.cap);
tv->log.widx += len;
tv->log.filled += len;
diff --git a/vtgl.c b/vtgl.c
@@ -820,6 +820,8 @@ do_terminal(Term *t, f32 dt)
resize(t);
if (t->gl.flags & INIT_DEBUG) {
+ /* TODO: cleanup this needs a better place */
+ term_reset(t);
debug_init(t, t->arena_for_frame);
t->gl.flags &= ~INIT_DEBUG;
}
@@ -843,8 +845,13 @@ do_terminal(Term *t, f32 dt)
.len = t->unprocessed_bytes,
.data = rb->buf + (rb->widx - t->unprocessed_bytes)
};
+ #if 1
+ handle_input(t, t->arena_for_frame, raw);
+ t->gl.flags |= UPDATE_RENDER_BUFFER;
+ #else
parsed_lines = split_raw_input_to_lines(t, raw);
t->gl.flags |= NEEDS_REFILL;
+ #endif
}
if (t->gl.flags & (NEEDS_REFILL|NEEDS_FULL_REFILL)) {