vtgl

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

Commit: d8353baac949359f4b4a29985b9719320658222c
Parent: a006d6184f4c4bccfc549bf150cda3438a85110b
Author: Randy Palamar
Date:   Sun, 25 Aug 2024 14:54:55 -0600

DECAWM: Auto-Wrap Mode (this was previously just assumed)

Diffstat:
Mdebug.c | 10+++++-----
Mmain.c | 4++--
Mterminal.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mtest.c | 6+++---
Mutil.h | 11+++++++++--
5 files changed, 90 insertions(+), 56 deletions(-)

diff --git a/debug.c b/debug.c @@ -6,7 +6,7 @@ simulate_line(Term *t, Line *line) Cursor saved_cursor = t->cursor; s8 l = line_to_s8(line, &t->views[t->view_idx].log); - t->cursor.state = line->cursor_state; + t->cursor.style = line->cursor_state; while (l.len) { u32 cp; @@ -35,9 +35,9 @@ simulate_line(Term *t, Line *line) static void fput_cursor_info(FILE *f, Cursor c) { - fprintf(f, "\tFG: 0x%08x\n", c.state.fg.rgba); - fprintf(f, "\tBG: 0x%08x\n", c.state.bg.rgba); - fprintf(f, "\tAttr: 0x%08x\n", c.state.attr); + fprintf(f, "\tFG: 0x%08x\n", c.style.fg.rgba); + fprintf(f, "\tBG: 0x%08x\n", c.style.bg.rgba); + fprintf(f, "\tAttr: 0x%08x\n", c.style.attr); fprintf(f, "\tPos: {%d, %d}\n", c.pos.y, c.pos.x); } @@ -45,7 +45,7 @@ static void fput_line_info(FILE *f, Term *t, Line *l) { Cursor start = t->cursor; - start.state = l->cursor_state; + start.style = l->cursor_state; fprintf(f, "Line Info:\n"); fprintf(f, "\tLength: %ld\n", line_length(l)); fprintf(f, "\tHas Unicode: %d\n", l->has_unicode); diff --git a/main.c b/main.c @@ -280,10 +280,10 @@ main(void) init_term(&term, &memory); os_alloc_ring_buffer(&term.views[0].log, BACKLOG_SIZE); - line_buf_alloc(&term.views[0].lines, &memory, term.views[0].log.buf, term.cursor.state, + line_buf_alloc(&term.views[0].lines, &memory, term.views[0].log.buf, term.cursor.style, BACKLOG_LINES); os_alloc_ring_buffer(&term.views[1].log, ALT_BACKLOG_SIZE); - line_buf_alloc(&term.views[1].lines, &memory, term.views[1].log.buf, term.cursor.state, + line_buf_alloc(&term.views[1].lines, &memory, term.views[1].log.buf, term.cursor.style, ALT_BACKLOG_LINES); term.child = os_fork_child("/bin/sh"); diff --git a/terminal.c b/terminal.c @@ -1,5 +1,7 @@ #include <immintrin.h> +#include <wchar.h> /* NOTE: for wcwidth; TODO: delete this */ + #define ESC_ARG_SIZ 6 typedef struct { @@ -176,7 +178,7 @@ fb_clear_region(Term *t, u32 r1, u32 r2, u32 c1, u32 c2) TermView *tv = t->views + t->view_idx; for (u32 r = r1; r <= r2; r++) { for (u32 c = c1; c <= c2; c++) { - tv->fb.rows[r][c].style = t->cursor.state; + tv->fb.rows[r][c].style = t->cursor.style; tv->fb.rows[r][c].cp = ' '; if (is_selected(&t->selection, c, r)) selection_clear(&t->selection); @@ -226,42 +228,45 @@ static void cursor_reset(Term *t) { //(Colour){.rgba = 0x1e9e33ff}; - t->cursor.state.fg = g_colours.data[g_colours.fgidx]; - t->cursor.state.bg = g_colours.data[g_colours.bgidx]; - t->cursor.state.attr = ATTR_NULL; + t->cursor.style.fg = g_colours.data[g_colours.fgidx]; + t->cursor.style.bg = g_colours.data[g_colours.bgidx]; + t->cursor.style.attr = ATTR_NULL; + t->cursor.state = CURSOR_NORMAL; } static void cursor_move_to(Term *t, i32 row, i32 col) { - t->cursor.pos.y = CLAMP(row, 0, t->size.h - 1); - t->cursor.pos.x = CLAMP(col, 0, t->size.w - 1); + t->cursor.pos.y = CLAMP(row, 0, t->size.h - 1); + t->cursor.pos.x = CLAMP(col, 0, t->size.w - 1); + t->cursor.state &= ~CURSOR_WRAP_NEXT; } static void cursor_alt(Term *t, b32 save) { i32 mode = t->view_idx; - if (save) t->saved_cursors[mode] = t->cursor; - else t->cursor = t->saved_cursors[mode]; + if (save) { + t->saved_cursors[mode] = t->cursor; + } else { + t->cursor = t->saved_cursors[mode]; + cursor_move_to(t, t->cursor.pos.y, t->cursor.pos.x); + } } - /* NOTE: advance the cursor by <n> cells; handles reverse movement */ static void cursor_step_column(Term *t, i32 n) { - 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; - t->cursor.pos.y++; - if (t->cursor.pos.y < t->size.h) - return; - t->cursor.pos.y = t->size.h - 1; - fb_scroll_up(t, 0, 1); + i32 col = t->cursor.pos.x + n; + i32 row = t->cursor.pos.y; + if (col >= t->size.w) { + row++; + col = 0; + if (row >= t->size.h) + fb_scroll_up(t, 0, 1); + } + cursor_move_to(t, row, col); } /* NOTE: steps the cursor without causing a scroll */ @@ -276,6 +281,7 @@ cursor_step_raw(Term *t, i32 step, i32 rows, i32 cols) static void term_reset(Term *t) { + i32 mode = t->mode & TM_ALTSCREEN; for (u32 i = 0; i < ARRAY_COUNT(t->saved_cursors); i++) { cursor_reset(t); cursor_move_to(t, 0, 0); @@ -283,6 +289,8 @@ term_reset(Term *t) swap_screen(t); fb_clear_region(t, 0, t->size.h, 0, t->size.w); } + /* TODO: why is term_reset() being called when we are in the altscreen */ + t->mode = mode|TM_AUTO_WRAP; } static void @@ -381,6 +389,10 @@ set_mode(Term *t, CSI *csi, b32 set) if (set) t->gl.mode |= WIN_MODE_APPCURSOR; else t->gl.mode &= ~WIN_MODE_APPCURSOR; break; + case PRIV(7): /* DECAWM: Auto-Wrap Mode */ + if (set) t->mode |= TM_AUTO_WRAP; + else t->mode &= ~TM_AUTO_WRAP; + break; case PRIV(3): /* DECCOLM: 132/80 Column Mode */ case PRIV(4): /* DECSCLM: Fast/Slow Scroll */ case PRIV(12): /* AT&T 610: Start blinking cursor */ @@ -496,7 +508,7 @@ direct_colour(i32 *argv, i32 argc, i32 *idx) static void set_colours(Term *t, CSI *csi) { - CellStyle *cs = &t->cursor.state; + CellStyle *cs = &t->cursor.style; struct direct_colour_result dcr; for (i32 i = 0; i < csi->argc; i++) { switch (csi->argv[i]) { @@ -578,13 +590,12 @@ window_manipulation(Term *t, CSI *csi) } static void -push_newline(Term *t) +push_newline(Term *t, b32 move_to_first_col) { - t->cursor.pos.y++; - if (t->cursor.pos.y == t->size.h) { - t->cursor.pos.y = t->size.h - 1; + i32 row = t->cursor.pos.y + 1; + if (row == t->size.h) fb_scroll_up(t, 0, 1); - } + cursor_move_to(t, row, move_to_first_col? 0 : t->cursor.pos.x); } static void @@ -779,8 +790,7 @@ handle_escape(Term *t, s8 *raw, Arena a) term_reset(t); break; case 'E': /* NEL - Next Line */ - t->cursor.pos.x = 0; - push_newline(t); + push_newline(t, 1); break; case 'M': /* RI -- Reverse Index */ if (t->cursor.pos.y == 0) { @@ -931,18 +941,18 @@ split_raw_input_to_lines(Term *t, s8 raw) return parsed_lines; case EMC_CURSOR_MOVED: parsed_lines++; - feed_line(&tv->lines, old.data, t->cursor.state); + feed_line(&tv->lines, old.data, t->cursor.style); break; case EMC_SWAPPED_SCREEN: parsed_lines++; - feed_line(&tv->lines, old.data, t->cursor.state); + feed_line(&tv->lines, old.data, 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.state); + init_line(nv->lines.buf + nv->lines.widx, raw.data, t->cursor.style); tv = nv; break; default: break; @@ -950,7 +960,7 @@ split_raw_input_to_lines(Term *t, s8 raw) } else if (cp == '\n') { raw = consume(raw, 1); parsed_lines++; - feed_line(&tv->lines, raw.data, t->cursor.state); + feed_line(&tv->lines, raw.data, t->cursor.style); } else if (cp & 0x80) { tv->lines.buf[tv->lines.widx].has_unicode = 1; /* TODO: this is probably slow */ @@ -969,7 +979,7 @@ split_raw_input_to_lines(Term *t, s8 raw) if (line_length(tv->lines.buf + tv->lines.widx) > SPLIT_LONG) { parsed_lines++; - feed_line(&tv->lines, raw.data, t->cursor.state); + feed_line(&tv->lines, raw.data, t->cursor.style); } } t->unprocessed_bytes = 0; @@ -981,10 +991,9 @@ push_line(Term *t, Line *line, Arena a) { TermView *tv = t->views + t->view_idx; s8 l = line_to_s8(line, &tv->log); - t->cursor.state = line->cursor_state; + t->cursor.style = line->cursor_state; Cell *c; - b32 wrap_next = 0; while (l.len) { u32 cp; if (line->has_unicode) cp = get_utf8(&l); @@ -993,32 +1002,50 @@ push_line(Term *t, Line *line, Arena a) /* TODO: handle error case */ ASSERT(cp != (u32)-1); + i32 width; switch (cp) { case 0x1B: handle_escape(t, &l, a); break; case '\r': t->cursor.pos.x = 0; break; - case '\n': push_newline(t); break; + case '\n': push_newline(t, 0); 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); break; default: - if (wrap_next) - cursor_step_column(t, 1); + if (t->mode & TM_AUTO_WRAP && t->cursor.state & CURSOR_WRAP_NEXT) + push_newline(t, 1); + + if (line->has_unicode) { + width = wcwidth(cp); + ASSERT(width != -1); + } else { + width = 1; + } + + if (t->cursor.pos.x + width > t->size.w) { + /* NOTE: make space 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.state; + c->style = t->cursor.style; + + /* TODO: wide characters (need some blank spaces?) */ - wrap_next = t->cursor.pos.x + 1 == t->size.w; - if (!wrap_next) - cursor_step_column(t, 1); + 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); } - if (wrap_next && (t->cursor.pos.y != t->size.h - 1)) - cursor_step_column(t, 1); } static size diff --git a/test.c b/test.c @@ -194,7 +194,7 @@ static TEST_FN(cursor_at_line_boundary) /* NOTE: check that cursor state was transferred */ Cursor line_end = simulate_line(term, lb->buf); - result.status &= !memcmp(&line_end.state, + result.status &= !memcmp(&line_end.style, &lb->buf[1].cursor_state, sizeof(lb->buf[0].cursor_state)); @@ -226,10 +226,10 @@ main(void) } os_alloc_ring_buffer(&term.views[0].log, BACKLOG_SIZE); - line_buf_alloc(&term.views[0].lines, &memory, term.views[0].log.buf, term.cursor.state, + line_buf_alloc(&term.views[0].lines, &memory, term.views[0].log.buf, term.cursor.style, BACKLOG_LINES); os_alloc_ring_buffer(&term.views[1].log, ALT_BACKLOG_SIZE); - line_buf_alloc(&term.views[1].lines, &memory, term.views[1].log.buf, term.cursor.state, + line_buf_alloc(&term.views[1].lines, &memory, term.views[1].log.buf, term.cursor.style, ALT_BACKLOG_LINES); /* TODO: should probably be some odd size */ diff --git a/util.h b/util.h @@ -117,9 +117,15 @@ typedef struct { CellStyle style; } Cell; +enum cursor_state { + CURSOR_NORMAL = 0 << 0, + CURSOR_WRAP_NEXT = 1 << 1, +}; + typedef struct { - iv2 pos; - CellStyle state; + iv2 pos; + CellStyle style; + enum cursor_state state; } Cursor; typedef Cell *Row; @@ -328,6 +334,7 @@ typedef struct { enum terminal_mode { TM_ALTSCREEN = 1 << 0, TM_REPLACE = 1 << 1, + TM_AUTO_WRAP = 1 << 2, }; typedef struct {