vtgl

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

Commit: b3d56a1f2e474df4933ebe4789a5c82764b63c96
Parent: b6025c0413114757296b9fa9f6cba5abf1e42923
Author: Randy Palamar
Date:   Wed, 14 Aug 2024 06:07:27 -0600

alt screen mostly works

Diffstat:
Mdebug.c | 14++++++++------
Mmain.c | 14+++++++++++---
Mos_unix.c | 15++++-----------
Mterminal.c | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mtest.c | 56++++++++++++++++++++++++++++++++++++--------------------
Mutil.c | 25+++++++++++++++++++++++++
Mutil.h | 24+++++++++++++++++-------
Mvtgl.c | 20++++++++++++--------
8 files changed, 207 insertions(+), 93 deletions(-)

diff --git a/debug.c b/debug.c @@ -4,7 +4,7 @@ static Cursor simulate_line(Term *t, Line *line) { Cursor saved_cursor = t->cursor; - s8 l = line_to_s8(line, &t->log); + s8 l = line_to_s8(line, &t->views[t->view_idx].log); t->cursor.state = line->cursor_state; @@ -60,15 +60,16 @@ dump_last_line_to_file(Term *t) printf("dumping line to %s\n", fname); + TermView *tv = t->views + t->view_idx; fputs("Line Idx -1:\n", f); - Line *line = t->log_lines.buf + get_line_idx(&t->log_lines, -1); - s8 l = line_to_s8(line, &t->log); + Line *line = tv->lines.buf + get_line_idx(&tv->lines, -1); + s8 l = line_to_s8(line, &tv->log); fput_line_info(f, t, line); fwrite(l.data, 1, l.len, f); fputs("\nLine Idx 0:\n", f); - line = t->log_lines.buf + get_line_idx(&t->log_lines, 0); - l = line_to_s8(line, &t->log); + line = tv->lines.buf + get_line_idx(&tv->lines, 0); + l = line_to_s8(line, &tv->log); fput_line_info(f, t, line); fwrite(l.data, 1, l.len, f); @@ -85,13 +86,14 @@ fput_cell(FILE *f, Cell *c) static void dump_fb_to_file(Term *t) { + TermView *tv = t->views + t->view_idx; char *fname = "fb.bin"; FILE *f = fopen(fname, "w"); if (!f) return; printf("dumping fb to %s\n", fname); for (u32 r = 0; r < t->size.h; r++) { for (u32 c = 0; c < t->size.w; c++) - fput_cell(f, &t->fb.rows[r][c]); + fput_cell(f, &tv->fb.rows[r][c]); fputc('\n', f); } fclose(f); diff --git a/main.c b/main.c @@ -278,10 +278,18 @@ main(void) init_window(&term, memory); init_fonts(&term, font_paths, ARRAY_COUNT(font_paths), g_default_fontsize, &memory); - cursor_reset(&term); + for (u32 i = 0; i < ARRAY_COUNT(term.saved_cursors); i++) { + cursor_reset(&term); + cursor_move_to(&term, 0, 0); + cursor_alt(&term, 1); + } - os_alloc_ring_buffer(&term.log, BACKLOG_SIZE); - line_buf_alloc(&term.log_lines, &memory, term.log.buf, term.cursor.state, BACKLOG_LINES); + 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, + 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, + ALT_BACKLOG_LINES); term.child = os_fork_child("/bin/sh"); diff --git a/os_unix.c b/os_unix.c @@ -262,24 +262,17 @@ static size os_read_from_child(os_child c, RingBuf *rb, size unprocessed_bytes) { size r = 0, total_bytes_read = 0, remaining = rb->cap - unprocessed_bytes; + + ASSERT(rb->widx < rb->cap); do { remaining -= r; total_bytes_read += r; r = read(c.fd, rb->buf + rb->widx + total_bytes_read, remaining); } while (r != -1); + ASSERT(total_bytes_read <= rb->cap); - ASSERT(rb->widx < rb->cap); - ASSERT(r <= rb->cap); - - rb->widx += total_bytes_read; - rb->filled += total_bytes_read; - - CLAMP(rb->filled, 0, rb->cap); - if (rb->widx >= rb->cap) - rb->widx -= rb->cap; + commit_to_rb(rb, total_bytes_read); - ASSERT(rb->filled >= 0); - ASSERT(rb->widx >= 0 && rb->widx < rb->cap); return total_bytes_read; } diff --git a/terminal.c b/terminal.c @@ -102,16 +102,21 @@ line_to_s8(Line *l, RingBuf *rb) } static void +init_line(Line *l, u8 *position, CellStyle cursor_state) +{ + l->start = position; + l->end = position; + l->has_unicode = 0; + l->cursor_state = cursor_state; +} + +static void feed_line(LineBuf *lb, u8 *position, CellStyle cursor_state) { lb->buf[lb->widx++].end = position; lb->widx = lb->widx >= lb->cap ? 0 : lb->widx; lb->filled += lb->filled <= lb->widx; - - lb->buf[lb->widx].start = position; - lb->buf[lb->widx].end = position; - lb->buf[lb->widx].has_unicode = 0; - lb->buf[lb->widx].cursor_state = cursor_state; + init_line(lb->buf + lb->widx, position, cursor_state); } static void @@ -133,10 +138,11 @@ fb_clear_region(Term *t, u32 r1, u32 r2, u32 c1, u32 c2) CLAMP(r1, 0, t->size.h - 1); CLAMP(r2, 0, t->size.h - 1); + TermView *tv = t->views + t->view_idx; for (u32 r = r1; r <= r2; r++) { for (u32 c = c1; c <= c2; c++) { - t->fb.rows[r][c].style = t->cursor.state; - t->fb.rows[r][c].cp = ' '; + tv->fb.rows[r][c].style = t->cursor.state; + tv->fb.rows[r][c].cp = ' '; } } } @@ -144,30 +150,39 @@ fb_clear_region(Term *t, u32 r1, u32 r2, u32 c1, u32 c2) static void fb_scroll_down(Term *t, u32 top, u32 n) { + TermView *tv = t->views + t->view_idx; // TODO: CLAMP? - u32 bottom = t->size.h - 1; + u32 bottom = t->size.h - 1; fb_clear_region(t, bottom - n + 1, bottom, 0, t->size.w); for (u32 i = bottom; i >= top + n; i--) { - Row tmp = t->fb.rows[i]; - t->fb.rows[i] = t->fb.rows[i - n]; - t->fb.rows[i - n] = tmp; + Row tmp = tv->fb.rows[i]; + tv->fb.rows[i] = tv->fb.rows[i - n]; + tv->fb.rows[i - n] = tmp; } } static void fb_scroll_up(Term *t, u32 top, u32 n) { + TermView *tv = t->views + t->view_idx; // TODO: CLAMP? - u32 bottom = t->size.h - 1; + u32 bottom = t->size.h - 1; fb_clear_region(t, top, top + n - 1, 0, t->size.w); for (u32 i = top; i <= bottom - n; i++) { - Row tmp = t->fb.rows[i]; - t->fb.rows[i] = t->fb.rows[i + n]; - t->fb.rows[i + n] = tmp; + Row tmp = tv->fb.rows[i]; + tv->fb.rows[i] = tv->fb.rows[i + n]; + tv->fb.rows[i + n] = tmp; } } static void +swap_screen(Term *t) +{ + t->mode ^= TM_ALTSCREEN; + t->view_idx = !!(t->mode & TM_ALTSCREEN); +} + +static void cursor_reset(Term *t) { //(Colour){.rgba = 0x1e9e33ff}; @@ -184,6 +199,15 @@ cursor_move_to(Term *t, i32 row, i32 col) } 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]; +} + + +static void cursor_step_column(Term *t, i32 step) { t->cursor.col += step; @@ -200,9 +224,13 @@ cursor_step_column(Term *t, i32 step) static void term_reset(Term *t) { - cursor_reset(t); - cursor_move_to(t, 0, 0); - fb_clear_region(t, 0, t->size.h, 0, t->size.w); + for (u32 i = 0; i < ARRAY_COUNT(t->saved_cursors); i++) { + cursor_reset(t); + cursor_move_to(t, 0, 0); + cursor_alt(t, 1); + swap_screen(t); + fb_clear_region(t, 0, t->size.h, 0, t->size.w); + } } static void @@ -280,6 +308,7 @@ erase_in_line(Term *t, CSI *csi) static void set_mode(Term *t, CSI *csi, b32 set) { + i32 alt = t->view_idx; #define PRIV(a) ((1 << 30) | (a)) for (i32 i = 0; i < csi->argc; i++) { i32 arg = (csi->argv[i]) | ((csi->priv & 1) << 30); @@ -300,9 +329,16 @@ set_mode(Term *t, CSI *csi, b32 set) if (set) t->gl.mode |= WIN_MODE_HIDECURSOR; else t->gl.mode &= ~WIN_MODE_HIDECURSOR; break; - case PRIV(1049): /* TODO: save cursor and switch to alt screen */ - if (set) t->mode |= TM_ALTSCREEN; - else t->mode &= ~TM_ALTSCREEN; + case PRIV(1049): /* xterm: swap cursor then swap screen */ + cursor_alt(t, set); + case PRIV(47): /* xterm: swap screen buffer */ + case PRIV(1047): /* xterm: swap screen buffer */ + if (alt) fb_clear_region(t, 0, t->size.h, 0, t->size.w); + if (set ^ alt) swap_screen(t); + if (csi->argv[i] != 1049) break; + /* FALLTHROUGH */ + case PRIV(1048): /* xterm: swap cursor */ + cursor_alt(t, set); break; default: fputs("set_mode: unhandled mode: ", stderr); @@ -418,6 +454,8 @@ set_colours(Term *t, CSI *csi) static void window_manipulation(Term *t, CSI *csi) { + /* TODO: you cannot reuse the title from GLFW after the fact; it needs to be copied */ + return; switch (csi->argv[0]) { case 22: t->saved_title = glfwGetWindowTitle(t->gl.window); break; case 23: glfwSetWindowTitle(t->gl.window, t->saved_title); break; @@ -482,10 +520,11 @@ handle_csi(Term *t, s8 *raw) case 't': window_manipulation(t, &csi); break; case '!': next = get_ascii(raw); - ASSERT(next == 'p'); - /* NOTE: DECSTR: soft terminal reset (if next == p) */ - /* IGNORED */ - break; + if (next == 'p') { + /* NOTE: DECSTR: soft terminal reset IGNORED */ + break; + } + /* FALLTHROUGH */ default: fputs("unknown csi: ", stderr); dump_csi(&csi); @@ -636,6 +675,7 @@ check_if_csi_moves_cursor(Term *t, s8 *raw) if (csi.argc == -1) return EMC_NEEDS_MORE_BYTES; + u32 term_mode = t->mode; switch (csi.mode) { case 'H': result = EMC_CURSOR_MOVED; break; case 'h': set_mode(t, &csi, 1); break; @@ -644,6 +684,9 @@ check_if_csi_moves_cursor(Term *t, s8 *raw) default: break; } + if (term_mode != t->mode) + result = EMC_CURSOR_MOVED; + return result; } @@ -683,7 +726,7 @@ check_if_escape_moves_cursor(Term *t, s8 *raw) static size split_raw_input_to_lines(Term *t, s8 raw) { - LineBuf *lb = &t->log_lines; + TermView *tv = t->views + t->view_idx; size parsed_lines = 0; __m128i nl = _mm_set1_epi8('\n'); __m128i esc = _mm_set1_epi8(0x1B); @@ -716,7 +759,7 @@ split_raw_input_to_lines(Term *t, s8 raw) count -= 16; data += 16; } - lb->buf[lb->widx].has_unicode |= _mm_movemask_epi8(hasutf8); + tv->lines.buf[tv->lines.widx].has_unicode |= _mm_movemask_epi8(hasutf8); raw = consume(raw, data - raw.data); if (raw.len) { @@ -729,7 +772,18 @@ split_raw_input_to_lines(Term *t, s8 raw) return parsed_lines; case EMC_CURSOR_MOVED: parsed_lines++; - feed_line(lb, old.data, t->cursor.state); + feed_line(&tv->lines, old.data, t->cursor.state); + if ((t->views + t->view_idx) == tv) + break; + /* NOTE: screen swapped */ + 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->log, -raw.len); + commit_to_rb(&nv->log, raw.len); + raw.data = nv->log.buf + nstart; + init_line(nv->lines.buf + nv->lines.widx, raw.data, t->cursor.state); + tv = nv; break; default: break; } @@ -738,9 +792,9 @@ split_raw_input_to_lines(Term *t, s8 raw) if (cp == '\n') { raw = consume(raw, 1); parsed_lines++; - feed_line(lb, raw.data, t->cursor.state); + feed_line(&tv->lines, raw.data, t->cursor.state); } else if (cp & 0x80) { - lb->buf[lb->widx].has_unicode = 1; + tv->lines.buf[tv->lines.widx].has_unicode = 1; /* TODO: this is probably slow */ size old_len = raw.len; if (get_utf8(&raw) == (u32)-1) { @@ -754,11 +808,11 @@ split_raw_input_to_lines(Term *t, s8 raw) } } - lb->buf[lb->widx].end = raw.data; + tv->lines.buf[tv->lines.widx].end = raw.data; - if (line_length(lb->buf + lb->widx) > SPLIT_LONG) { + if (line_length(tv->lines.buf + tv->lines.widx) > SPLIT_LONG) { parsed_lines++; - feed_line(lb, raw.data, t->cursor.state); + feed_line(&tv->lines, raw.data, t->cursor.state); } } t->unprocessed_bytes = 0; @@ -786,8 +840,8 @@ push_tab(Term *t) static void push_line(Term *t, Line *line, Arena a) { - s8 l = line_to_s8(line, &t->log); - + TermView *tv = t->views + t->view_idx; + s8 l = line_to_s8(line, &tv->log); t->cursor.state = line->cursor_state; Cell *c; @@ -814,7 +868,7 @@ push_line(Term *t, Line *line, Arena a) /* TODO properly make sure characters are printable */ CLAMP(cp, ' ', '~'); - c = &t->fb.rows[t->cursor.row][t->cursor.col]; + c = &tv->fb.rows[t->cursor.row][t->cursor.col]; c->cp = cp; c->style = t->cursor.state; @@ -847,8 +901,10 @@ blit_lines(Term *t, Arena a, size line_count) //t->cursor.col = t->mode & TM_ALTSCREEN ? t->cursor.col : 0; t->cursor.col = 0; + TermView *tv = t->views + t->view_idx; + CLAMP(line_count, 0, tv->lines.filled); for (size i = 0; i <= line_count; i++) { - size line_idx = get_line_idx(&t->log_lines, -line_count + i); - push_line(t, t->log_lines.buf + line_idx, a); + size line_idx = get_line_idx(&tv->lines, -line_count + i); + push_line(t, tv->lines.buf + line_idx, a); } } diff --git a/test.c b/test.c @@ -32,11 +32,15 @@ static Test_Fn *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; + t->views[0].log.widx = 0; + t->views[0].log.filled = 0; + t->views[1].log.widx = 0; + t->views[1].log.filled = 0; + t->unprocessed_bytes = 0; + t->views[0].lines.widx = 0; + t->views[0].lines.filled = 0; + t->views[1].lines.widx = 0; + t->views[1].lines.filled = 0; } static size @@ -71,10 +75,11 @@ check_cells_equal(Cell *a, Cell *b) static s8 launder_static_string(Term *term, s8 static_str) { - term->unprocessed_bytes += copy_into_ringbuf(&term->log, static_str); + RingBuf *rb = &term->views[term->view_idx].log; + term->unprocessed_bytes += copy_into_ringbuf(rb, static_str); s8 raw = { .len = term->unprocessed_bytes, - .data = term->log.buf + (term->log.widx - term->unprocessed_bytes) + .data = rb->buf + (rb->widx - term->unprocessed_bytes) }; return raw; } @@ -116,7 +121,7 @@ static TEST_FN(colour_setting) .fg = g_colours.data[3], .attr = (ATTR_INVISIBLE|ATTR_STRUCK), }}; - result.status = check_cells_equal(&c, term->fb.rows[0]); + result.status = check_cells_equal(&c, term->views[term->view_idx].fb.rows[0]); return result; } @@ -156,16 +161,17 @@ 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(term->log_lines.buf) > SPLIT_LONG; + result.status &= line_length(lb->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)); + Cursor line_end = simulate_line(term, lb->buf); + result.status &= !memcmp(&line_end.state, + &lb->buf[1].cursor_state, + sizeof(lb->buf[0].cursor_state)); return result; } @@ -173,9 +179,10 @@ static TEST_FN(cursor_at_line_boundary) static TEST_FN(working_ringbuffer) { struct test_result result = {.info = __FUNCTION__}; - term->log.buf[0] = 0xFE; - result.status = (term->log.buf[0] == term->log.buf[term->log.cap]) && - (term->log.buf[0] == term->log.buf[-term->log.cap]); + RingBuf *rb = &term->views[term->view_idx].log; + rb->buf[0] = 0xFE; + result.status = (rb->buf[0] == rb->buf[ rb->cap]) && + (rb->buf[0] == rb->buf[-rb->cap]); return result; } @@ -187,14 +194,23 @@ main(void) Arena memory = os_new_arena(16 * MEGABYTE); Term term = {0}; - cursor_reset(&term); + for (u32 i = 0; i < ARRAY_COUNT(term.saved_cursors); i++) { + cursor_reset(&term); + cursor_move_to(&term, 0, 0); + cursor_alt(&term, 1); + } - os_alloc_ring_buffer(&term.log, BACKLOG_SIZE); - line_buf_alloc(&term.log_lines, &memory, term.log.buf, term.cursor.state, BACKLOG_LINES); + 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, + 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, + ALT_BACKLOG_LINES); /* TODO: should probably be some odd size */ term.size = (uv2){.w = 80, .h = 24}; - os_alloc_framebuffer(&term.fb, term.size.h, term.size.w); + os_alloc_framebuffer(&term.views[0].fb, term.size.h, term.size.w); + os_alloc_framebuffer(&term.views[1].fb, term.size.h, term.size.w); for (u32 i = 0; i < ARRAY_COUNT(tests); i++) { buffer_reset(&term); diff --git a/util.c b/util.c @@ -15,6 +15,15 @@ die(char *fmt, ...) exit(1); } +static void +mem_copy(s8 src, s8 dest) +{ + ASSERT(src.len > 0 && dest.len > 0); + ASSERT(dest.len >= src.len); + for (size i = 0; i < src.len; i++) + dest.data[i] = src.data[i]; +} + static void * mem_clear(u8 *p, u8 c, size len) { @@ -39,6 +48,22 @@ alloc_(Arena *a, size len, size align, size count) } static void +commit_to_rb(RingBuf *rb, size len) +{ + ASSERT(len <= rb->cap); + + rb->widx += len; + rb->filled += len; + + CLAMP(rb->filled, 0, rb->cap); + if (rb->widx >= rb->cap) + rb->widx -= rb->cap; + + ASSERT(rb->filled >= 0); + ASSERT(rb->widx >= 0 && rb->widx < rb->cap); +} + +static void line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, size capacity) { lb->cap = capacity; diff --git a/util.h b/util.h @@ -37,6 +37,9 @@ #define BACKLOG_SIZE (16 * MEGABYTE) #define BACKLOG_LINES (1024UL) +#define ALT_BACKLOG_SIZE (2 * MEGABYTE) +#define ALT_BACKLOG_LINES (1024UL) + typedef float f32; typedef double f64; typedef uint8_t u8; @@ -143,6 +146,12 @@ typedef struct { } LineBuf; typedef struct { + RingBuf log; + LineBuf lines; + Framebuffer fb; +} TermView; + +typedef struct { /* distance to shift glyph from bounding box origin */ iv2 delta; /* rendered glyph bitmap data (pixels) */ @@ -255,17 +264,18 @@ typedef struct { GLCtx gl; FontAtlas fa; - RingBuf log; - LineBuf log_lines; - size unprocessed_bytes; + Cursor cursor; + Cursor saved_cursors[2]; + TermView views[2]; + i32 view_idx; - Framebuffer fb; + size unprocessed_bytes; os_child child; - uv2 size; - Cursor cursor; - u32 mode; + uv2 size; + + enum terminal_mode mode; /* NOTE: owned by GLFW; doesn't need to be freed */ const char *saved_title; diff --git a/vtgl.c b/vtgl.c @@ -56,7 +56,8 @@ static void resize(Term *t) { v2 ws = t->gl.window_size; - os_alloc_framebuffer(&t->fb, t->size.h, t->size.w); + os_alloc_framebuffer(&t->views[0].fb, t->size.h, t->size.w); + os_alloc_framebuffer(&t->views[1].fb, t->size.h, t->size.w); os_set_term_size(t->child, t->size.h, t->size.w, ws.w, ws.h); t->gl.flags &= ~NEEDS_RESIZE; t->gl.flags |= NEEDS_FULL_BLIT; @@ -257,9 +258,10 @@ render_framebuffer(Term *t, RenderPushBuffer *rpb) v2 cs = get_cell_size(t); Rect cr = {.pos = {.y = t->gl.window_size.h - cs.h}, .size = cs}; + TermView *tv = t->views + t->view_idx; for (u32 r = 0; r < t->size.h; r++) { for (u32 c = 0; c < t->size.w; c++) { - push_cell(rpb, &t->gl, t->fb.rows[r][c], cr, t->fa.deltay); + push_cell(rpb, &t->gl, tv->fb.rows[r][c], cr, t->fa.deltay); cr.pos.x += cs.w; } cr.pos.x = 0; @@ -268,13 +270,14 @@ render_framebuffer(Term *t, RenderPushBuffer *rpb) /* NOTE: draw cursor */ /* TODO: hide cursor doesn't get reset properly */ - //if (!(t->gl.mode & WIN_MODE_HIDECURSOR)) { - Cell cursor = t->fb.rows[t->cursor.row][t->cursor.col]; + //if (!(t->gl.mode & WIN_MODE_HIDECURSOR)) + { + Cell cursor = tv->fb.rows[t->cursor.row][t->cursor.col]; cr.pos.y = t->gl.window_size.h - cs.h * (t->cursor.row + 1); cr.pos.x = t->cursor.col * cs.w; cursor.style.attr ^= ATTR_INVERSE; push_cell(rpb, &t->gl, cursor, cr, t->fa.deltay); - //} + } } /* NOTE: called when the window was resized */ @@ -415,19 +418,20 @@ do_terminal(Term *t, Arena a) if (t->gl.flags & NEEDS_FULL_BLIT) { t->gl.flags &= ~NEEDS_FULL_BLIT; term_reset(t); - blit_lines(t, a, MIN(t->log_lines.filled, t->size.h - 1)); + blit_lines(t, a, t->size.h - 1); } if (os_child_data_available(t->child)) { + RingBuf *rb = &t->views[t->view_idx].log; if (os_child_exited(t->child)) { /* TODO: is there a reason to not immediately exit? */ glfwSetWindowShouldClose(t->gl.window, GL_TRUE); return; } - t->unprocessed_bytes += os_read_from_child(t->child, &t->log, t->unprocessed_bytes); + t->unprocessed_bytes += os_read_from_child(t->child, rb, t->unprocessed_bytes); s8 raw = { .len = t->unprocessed_bytes, - .data = t->log.buf + (t->log.widx - t->unprocessed_bytes) + .data = rb->buf + (rb->widx - t->unprocessed_bytes) }; size parsed_lines = split_raw_input_to_lines(t, raw); blit_lines(t, a, parsed_lines);