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:
M | debug.c | | | 10 | +++++----- |
M | main.c | | | 4 | ++-- |
M | terminal.c | | | 115 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
M | test.c | | | 6 | +++--- |
M | util.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 {