Commit: 5891ce7bae709d3026df0a9f89c57798e1dd8478
Parent: 7d1c35b39e63aeb8dfea640aa6724de442955fba
Author: Randy Palamar
Date: Wed, 3 Jul 2024 21:52:38 -0600
handle most escapes produced by less
Diffstat:
M | build.sh | | | 2 | +- |
M | main.c | | | 1 | + |
M | terminal.c | | | 87 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
M | util.h | | | 7 | +++++++ |
M | vtgl.c | | | 45 | +++++++++++++++++++++++++++++++++++++++++---- |
5 files changed, 126 insertions(+), 16 deletions(-)
diff --git a/build.sh b/build.sh
@@ -6,7 +6,7 @@ ldflags=""
ldflags="$ldflags -lfreetype $(pkg-config --static --libs glfw3 gl)"
# Hot Reloading/Debugging
-cflags="$cflags -D_DEBUG -Wno-unused-function"
+cflags="$cflags -D_DEBUG -Wno-unused-function -Wno-undefined-internal"
libcflags="$cflags -fPIC -Wno-unused-function"
libldflags="$ldflags -shared"
diff --git a/main.c b/main.c
@@ -55,6 +55,7 @@ do_debug(GLCtx *gl)
nanosleep(&sleep_time, &sleep_time);
load_library(libname);
init_callbacks(gl);
+ fputs("Reloaded Main Program\n", stderr);
}
}
diff --git a/terminal.c b/terminal.c
@@ -126,6 +126,8 @@ dump_csi(CSI *csi)
fputs(" } }\n", stderr);
}
+
+/* ED/DECSED: Erase in Display */
static void
erase_in_display(Term *t, CSI *csi)
{
@@ -142,10 +144,50 @@ erase_in_display(Term *t, CSI *csi)
case 3: /* Erase Saved Lines (xterm) */
/* NOTE: ignored; we don't save lines in the way xterm does */
break;
- default: ASSERT(0); break;
+ default: ASSERT(0);
}
}
+/* EL/DECSEL: Erase in Line */
+static void
+erase_in_line(Term *t, CSI *csi)
+{
+ Cursor *c = &t->cursor;
+ switch (csi->argv[0]) {
+ case 0: /* Erase to Right */
+ push_empty_cell_rect(t, c->row, c->row, c->col, t->size.w);
+ break;
+ case 1: /* Erase to Left */
+ push_empty_cell_rect(t, c->row, c->row, 0, c->col);
+ break;
+ case 2: /* Erase All */
+ push_empty_cell_rect(t, c->row, c->row, 0, t->size.w);
+ break;
+ default: ASSERT(0);
+ }
+}
+
+/* SM/DECSET: Set Mode & RM/DECRST Reset Mode */
+static void
+set_mode(Term *t, CSI *csi, b32 set)
+{
+ #define PRIV(a) ((1 << 30) | (a))
+ for (i32 i = 0; i < csi->argc; i++) {
+ i32 arg = (csi->argv[i]) | ((csi->priv & 1) << 30);
+ switch (arg) {
+ case PRIV(1): /* DECCKM: use application cursor keys */
+ if (set) t->gl.mode |= WIN_MODE_APPCURSOR;
+ else t->gl.mode &= ~WIN_MODE_APPCURSOR;
+ break;
+ case PRIV(1049): /* TODO: save cursor and switch to alt screen */
+ break;
+ default:
+ fputs("set_mode: unhandled mode: ", stderr);
+ dump_csi(csi);
+ }
+ }
+ #undef PRIV
+}
/* SGR: Select Graphic Rendition */
static void
@@ -154,15 +196,22 @@ set_colours(Term *t, CSI *csi)
CursorState *cs = &t->cursor.state;
for (i32 i = 0; i < csi->argc; i++) {
switch (csi->argv[i]) {
- case 0: cursor_reset(t); break;
- case 1: cs->attr |= ATTR_BOLD; break;
- case 2: cs->attr |= ATTR_FAINT; break;
- case 3: cs->attr |= ATTR_ITALIC; break;
- case 4: cs->attr |= ATTR_UNDERLINED; break;
- case 5: cs->attr |= ATTR_BLINK; break;
- case 7: cs->attr |= ATTR_INVERSE; break;
- case 8: cs->attr |= ATTR_INVISIBLE; break;
- case 9: cs->attr |= ATTR_STRUCK; break;
+ case 0: cursor_reset(t); break;
+ case 1: cs->attr |= ATTR_BOLD; break;
+ case 2: cs->attr |= ATTR_FAINT; break;
+ case 3: cs->attr |= ATTR_ITALIC; break;
+ case 4: cs->attr |= ATTR_UNDERLINED; break;
+ case 5: cs->attr |= ATTR_BLINK; break;
+ case 7: cs->attr |= ATTR_INVERSE; break;
+ case 8: cs->attr |= ATTR_INVISIBLE; break;
+ case 9: cs->attr |= ATTR_STRUCK; break;
+ case 22: cs->attr &= ~(ATTR_BOLD|ATTR_FAINT); break;
+ case 23: cs->attr &= ~ATTR_ITALIC; break;
+ case 24: cs->attr &= ~ATTR_UNDERLINED; break;
+ case 25: cs->attr &= ~ATTR_BLINK; break;
+ case 27: cs->attr &= ~ATTR_INVERSE; break;
+ case 28: cs->attr &= ~ATTR_INVISIBLE; break;
+ case 29: cs->attr &= ~ATTR_STRUCK; break;
default:
fprintf(stderr, "unhandled colour arg: %d\n", csi->argv[i]);
dump_csi(csi);
@@ -170,6 +219,18 @@ set_colours(Term *t, CSI *csi)
}
}
+static void
+window_manipulation(Term *t, CSI *csi)
+{
+ switch (csi->argv[0]) {
+ case 22: /* TODO: save title */ break;
+ case 23: /* TODO: restore title */ break;
+ default:
+ fprintf(stderr, "unhandled xtwinops: %d\n", csi->argv[0]);
+ dump_csi(csi);
+ }
+}
+
static CSI
parse_csi(s8 *r)
{
@@ -212,9 +273,13 @@ handle_csi(Term *t, s8 *raw)
CSI csi = parse_csi(raw);
switch (csi.mode) {
- case 'm': set_colours(t, &csi); break;
case 'H': cursor_move_to(t, csi.argv[0], csi.argv[1]); break;
case 'J': erase_in_display(t, &csi); break;
+ case 'K': erase_in_line(t, &csi); break;
+ case 'h': set_mode(t, &csi, 1); break;
+ case 'l': set_mode(t, &csi, 0); break;
+ case 'm': set_colours(t, &csi); break;
+ case 't': window_manipulation(t, &csi); break;
default:
fputs("unknown csi: ", stderr);
dump_csi(&csi);
diff --git a/util.h b/util.h
@@ -151,6 +151,10 @@ enum gl_flags {
UPDATE_POST_UNIFORMS = 1 << 30,
};
+enum win_mode {
+ WIN_MODE_APPCURSOR = 1 << 0,
+};
+
enum shader_stages {
SHADER_RENDER,
SHADER_POST,
@@ -180,6 +184,7 @@ typedef struct {
#undef X
u32 flags;
+ u32 mode;
u32 glyph_tex;
u32 glyph_cache_len;
@@ -225,6 +230,8 @@ typedef struct {
FT_Library ftlib;
} Term;
+static void push_empty_cell_rect(Term *, u32 minrow, u32 maxrow, u32 mincol, u32 maxcol);
+
#include "config.h"
#include "font.c"
#include "terminal.c"
diff --git a/vtgl.c b/vtgl.c
@@ -83,15 +83,21 @@ update_uniforms(Term *t, enum shader_stages stage)
switch (stage) {
case SHADER_RENDER:
- for (u32 i = 0; i < ARRAY_COUNT(t->gl.render.uniforms); i++)
+ for (u32 i = 0; i < ARRAY_COUNT(t->gl.render.uniforms); i++) {
t->gl.render.uniforms[i] = glGetUniformLocation(t->gl.programs[stage],
render_uniform_names[i]);
+ //fprintf(stderr, "uniform (RENDER): %s; id %d\n",
+ // render_uniform_names[i], t->gl.render.uniforms[i]);
+ }
t->gl.flags &= ~UPDATE_RENDER_UNIFORMS;
break;
case SHADER_POST:
- for (u32 i = 0; i < ARRAY_COUNT(t->gl.post.uniforms); i++)
+ for (u32 i = 0; i < ARRAY_COUNT(t->gl.post.uniforms); i++) {
t->gl.post.uniforms[i] = glGetUniformLocation(t->gl.programs[stage],
post_uniform_names[i]);
+ //fprintf(stderr, "uniform (POST): %s; id %d\n",
+ // post_uniform_names[i], t->gl.post.uniforms[i]);
+ }
t->gl.flags &= ~UPDATE_POST_UNIFORMS;
break;
case SHADER_LAST: ASSERT(0); break;
@@ -139,6 +145,21 @@ push_char(GLCtx *gl, v2 vertscale, v2 vertoff, v2 texscale, uv2 colours, i32 cha
}
static void
+push_empty_cell_rect(Term *t, u32 minrow, u32 maxrow, u32 mincol, u32 maxcol)
+{
+ ASSERT(minrow <= maxrow && mincol <= maxcol);
+ ASSERT(maxrow <= t->size.h && maxcol <= t->size.w);
+
+ v2 cs = get_cell_size(t);
+ v2 size = {.x = (maxcol - mincol + 1) * cs.w, .y = (maxrow - minrow + 1) * cs.h};
+ v2 pos = {.x = mincol * cs.w, .y = t->gl.window_size.h - cs.h * (minrow + 1)};
+
+ /* TODO: global colour table */
+ Colour colour = {.r = 20, .g = 20, .b = 20, .a = 255};
+ push_char(&t->gl, size, pos, (v2){0}, (uv2){.y = colour.rgba}, 0);
+}
+
+static void
draw_text(GLCtx *gl, s8 text, v2 position, Colour colour, b32 monospaced)
{
f32 single_space_width = gl->glyph_cache[0].size.w;
@@ -232,6 +253,7 @@ push_line(Term *t, Line *line, v2 start_pos)
v2 cs = get_cell_size(t);
Rect cr = {.pos = start_pos, .size = cs};
+
while (l.len) {
/* TODO: handle unicode case */
u32 cp = get_ascii(&l);
@@ -311,6 +333,15 @@ key_callback(GLFWwindow *win, i32 key, i32 sc, i32 act, i32 mods)
{
Term *t = glfwGetWindowUserPointer(win);
+ if (mods & GLFW_MOD_CONTROL && key < 0x80) {
+ if (act == GLFW_RELEASE)
+ return;
+ if (BETWEEN(key, 0x40, 0x5F)) {
+ os_child_put_char(t->child, key - 0x40);
+ return;
+ }
+ }
+
switch (ENCODE_KEY(act, 0, key)) {
case ENCODE_KEY(GLFW_PRESS, 0, GLFW_KEY_ESCAPE):
case ENCODE_KEY(GLFW_REPEAT, 0, GLFW_KEY_ESCAPE):
@@ -356,9 +387,15 @@ do_terminal(Term *t, Arena a)
if (t->gl.flags & UPDATE_POST_UNIFORMS)
update_uniforms(t, SHADER_POST);
+ /* TODO: don't let the input splitting cause draw calls */
+ glUseProgram(t->gl.programs[SHADER_RENDER]);
+
if (os_child_data_available(t->child)) {
- if (os_child_exited(t->child))
+ 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);
s8 raw = {
.len = t->unprocessed_bytes,
@@ -372,7 +409,7 @@ do_terminal(Term *t, Arena a)
v2 ws = t->gl.window_size;
/* NOTE: reset the camera/viewport */
- glUseProgram(t->gl.programs[SHADER_RENDER]);
+ //glUseProgram(t->gl.programs[SHADER_RENDER]);
glUniform1i(t->gl.render.texslot, 0);
glBindFramebuffer(GL_FRAMEBUFFER, t->gl.fb);
clear_colour();