Commit: 15356a94e46add94d999b8a9fa13eb0dae1eecca
Parent: ad6e8ba279987b30591c6bd5948837b438c995da
Author: Randy Palamar
Date: Sun, 23 Jun 2024 11:49:34 -0600
spawn the child process and read its output
Diffstat:
M | main.c | | | 5 | +++++ |
M | os_unix.c | | | 149 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
M | util.h | | | 28 | ++++++++++++++++++++-------- |
M | vtgl.c | | | 31 | +++++++++++++++++++++++++++---- |
4 files changed, 200 insertions(+), 13 deletions(-)
diff --git a/main.c b/main.c
@@ -6,6 +6,8 @@
#include "util.h"
+#define BACKLOG_SIZE (16 * MEGABYTE)
+
#ifndef _DEBUG
static void do_debug(void) { }
#else
@@ -240,6 +242,9 @@ main(void)
init_window(&term);
init_fonts(&term, font_paths, ARRAY_COUNT(font_paths), 64, &memory);
+ os_alloc_ring_buffer(&term.log, BACKLOG_SIZE);
+ term.child = os_fork_child("/bin/sh");
+
f32 last_time = 0;
while (!glfwWindowShouldClose(term.gl.window)) {
do_debug(&term.gl);
diff --git a/os_unix.c b/os_unix.c
@@ -1,15 +1,25 @@
/* See LICENSE for copyright details */
#include <fcntl.h>
+#include <pty.h>
+#include <pwd.h>
#include <sys/mman.h>
+#include <sys/select.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
typedef struct timespec os_filetime;
+typedef s8 os_mapped_file;
+
typedef struct {
size filesize;
os_filetime timestamp;
} os_file_stats;
-typedef s8 os_mapped_file;
+
+typedef struct {
+ i32 fd;
+ pid_t pid;
+} os_child;
#define OS_MAP_READ PROT_READ
#define OS_MAP_PRIVATE MAP_PRIVATE
@@ -90,3 +100,140 @@ os_unmap_file(u8 *data, size len)
{
munmap(data, len);
}
+
+static void
+os_alloc_ring_buffer(RingBuf *rb, size capacity)
+{
+ size pagesize = sysconf(_SC_PAGESIZE);
+ if (capacity % pagesize != 0)
+ capacity += pagesize - capacity % pagesize;
+
+ rb->widx = 0;
+ rb->filled = 0;
+ rb->cap = capacity;
+ rb->buf = mmap(0, 3 * rb->cap, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+
+ if (rb->buf == MAP_FAILED)
+ die("os_alloc_ring_buffer: initial mmap failed\n");
+
+ for (i32 i = 0; i < 3; i++) {
+ void *ret = mmap(rb->buf + i * rb->cap, rb->cap, PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ if (ret == MAP_FAILED)
+ die("os_alloc_ring_buffer: mmap(%d) failed\n", i);
+ }
+ /* NOTE: start in the middle page */
+ rb->buf += rb->cap;
+}
+
+static void
+execsh(char *defcmd)
+{
+ char *sh;
+ struct passwd *pw;
+
+ if ((pw = getpwuid(getuid())) == NULL)
+ die("are you real?\n");
+
+ if ((sh = getenv("SHELL")) == NULL)
+ sh = pw->pw_shell[0] ? pw->pw_shell : defcmd;
+
+ char *argv[] = {sh, NULL};
+ setenv("USER", pw->pw_name, 1);
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("SHELL", sh, 1);
+ setenv("HOME", pw->pw_dir, 1);
+ /* TODO: don't pretend to be xterm ? */
+ setenv("TERM", "xterm", 1);
+
+ execvp(sh, argv);
+
+ _exit(1);
+}
+
+static os_child
+os_fork_child(char *cmd)
+{
+ i32 cfd;
+
+ struct termios raw;
+ cfmakeraw(&raw);
+ pid_t pid = forkpty(&cfd, NULL, &raw, NULL);
+
+ switch (pid) {
+ case -1:
+ die("os_fork_child: failed to spawn child: %s\n", cmd);
+ case 0: /* child */
+ execsh(cmd);
+ break;
+ }
+
+ i32 flags = fcntl(cfd, F_GETFL);
+ if (flags == -1)
+ die("os_fork_child: fcntl: F_GETFL\n");
+ if (fcntl(cfd, F_SETFL, flags | O_NONBLOCK) == -1)
+ die("os_fork_child: fcntl: F_SETFL\n");
+
+ return (os_child){ .pid = pid, .fd = cfd };
+}
+
+static b32
+os_child_data_available(os_child c)
+{
+ b32 result = 0;
+
+ struct timespec timeout = {0};
+ fd_set rfd;
+ FD_ZERO(&rfd);
+ FD_SET(c.fd, &rfd);
+
+ pselect(c.fd+1, &rfd, NULL, NULL, &timeout, NULL);
+
+ result = FD_ISSET(c.fd, &rfd) != 0;
+ return result;
+}
+
+static b32
+os_child_exited(os_child c)
+{
+ i32 r, status;
+ r = waitpid(c.pid, &status, WNOHANG);
+ return r == c.pid && WIFEXITED(status);
+}
+
+static size
+os_read_from_child(os_child c, RingBuf *rb)
+{
+ size r = read(c.fd, rb->buf + rb->widx, rb->cap);
+
+ ASSERT(rb->widx < rb->cap);
+ ASSERT(r <= rb->cap);
+ ASSERT(r != -1);
+
+ rb->widx += r;
+ rb->filled += r;
+
+ 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);
+ return r;
+}
+
+static void
+os_child_put_s8(os_child c, s8 text)
+{
+ write(c.fd, text.data, text.len);
+}
+
+static void
+os_child_put_char(os_child c, u32 cp)
+{
+ /* TODO: encode to utf-8 */
+ ASSERT(cp <= 0x7f);
+ u8 character = (u8)cp;
+ s8 text = {.len = 1, .data = &character};
+ os_child_put_s8(c, text);
+}
diff --git a/util.h b/util.h
@@ -23,6 +23,7 @@
#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
typedef float f32;
typedef double f64;
@@ -68,6 +69,14 @@ typedef struct { u8 *beg, *end; } Arena;
typedef struct { size len; u8 *data; } s8;
#define s8(s) (s8){.len = ARRAY_COUNT(s) - 1, .data = (u8 *)s}
+/* NOTE: virtual memory ring buffer */
+typedef struct {
+ size cap;
+ size filled;
+ size widx;
+ u8 *buf;
+} RingBuf;
+
#define GL_UNIFORMS \
X(Pmat) \
X(charmap) \
@@ -114,6 +123,13 @@ typedef struct {
#include <ft2build.h>
#include FT_FREETYPE_H
+#include "util.c"
+
+#ifdef __unix__
+#include "os_unix.c"
+#else
+#error Unsupported Platform!
+#endif
typedef struct {
u8 *buf;
@@ -132,16 +148,12 @@ typedef struct {
GLCtx gl;
FontAtlas fa;
- FT_Library ftlib;
-} Term;
+ RingBuf log;
-#include "util.c"
+ os_child child;
-#ifdef __unix__
-#include "os_unix.c"
-#else
-#error Unsupported Platform!
-#endif
+ FT_Library ftlib;
+} Term;
#include "font.c"
diff --git a/vtgl.c b/vtgl.c
@@ -122,7 +122,10 @@ draw_text(GLCtx *gl, s8 text, v2 position, Colour colour, b32 monospaced)
f32 single_space_width = gl->glyph_cache[0].size.w;
for (size i = 0; i < text.len; i++) {
Glyph g;
- i32 glyph_idx = get_gpu_glyph_index(gl, text.data[i], &g);
+ /* TODO: don't do this */
+ u32 cp = text.data[i];
+ CLAMP(cp, ' ', '~');
+ i32 glyph_idx = get_gpu_glyph_index(gl, cp, &g);
v2 texscale = {.x = g.size.w / MAX_FONT_SIZE, .y = g.size.h / MAX_FONT_SIZE};
v2 vertscale = {.x = g.size.w, .y = g.size.h};
v2 vertoff = {.x = position.x + g.delta.x, .y = position.y + g.delta.y};
@@ -194,16 +197,26 @@ fb_callback(GLFWwindow *win, i32 w, i32 h)
}
static void
-key_callback(GLFWwindow *w, i32 key, i32 sc, i32 act, i32 mods)
+key_callback(GLFWwindow *win, i32 key, i32 sc, i32 act, i32 mods)
{
+ Term *t = glfwGetWindowUserPointer(win);
if (key == GLFW_KEY_ESCAPE && act == GLFW_PRESS)
- glfwSetWindowShouldClose(w, GL_TRUE);
+ glfwSetWindowShouldClose(win, GL_TRUE);
+ if (key == GLFW_KEY_ENTER && act == GLFW_PRESS)
+ os_child_put_char(t->child, '\r');
+}
+
+static void
+char_callback(GLFWwindow *win, u32 codepoint)
+{
+ Term *t = glfwGetWindowUserPointer(win);
+ os_child_put_char(t->child, codepoint);
}
DEBUG_EXPORT void
init_callbacks(GLCtx *gl)
{
- //glfwSetCharCallback(gl->window, char_callback);
+ glfwSetCharCallback(gl->window, char_callback);
glfwSetFramebufferSizeCallback(gl->window, fb_callback);
glfwSetKeyCallback(gl->window, key_callback);
//glfwSetWindowRefreshCallback(gl->window, refresh_callback);
@@ -221,6 +234,16 @@ do_terminal(Term *t, Arena a)
clear_colour();
+ if (os_child_data_available(t->child)) {
+ if (os_child_exited(t->child))
+ glfwSetWindowShouldClose(t->gl.window, GL_TRUE);
+ os_read_from_child(t->child, &t->log);
+ }
+ /* TODO: actually parse this data */
+ s8 text = {.len = t->log.widx, .data = t->log.buf};
+ v2 cpos = { .x = 0, .y = t->gl.window_size.h/2 };
+ draw_text(&t->gl, text, cpos, (Colour){.rgba = 0x1e9e33ff}, 1);
+
Rect r1 = {.pos = {.x = 20, .y = 20}, .size = {.x = 200, .y = 100}};
Rect r3 = {.pos = {.x = 100, .y = 600}, .size = {.x = 50, .y = 100}};
static Rect r2 = {.pos = {.x = 50, .y = 300}, .size = {.x = 100, .y = 100}};