/* Shell window using admu (adμ?). * * major missing parts: * - detect child exit (EOF?) */ #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #include // for dietlibc #include "admu.h" #include "xshmu.h" typedef struct { int fd; int pid; } child; child spawn_child(char *program, int cols, int rows) { int master = open("/dev/ptmx", O_RDWR); if (master == -1) { perror("/dev/ptmx"); exit(-1); } if (grantpt(master)) { perror("grantpt"); exit(-1); } if (unlockpt(master)) { perror("unlockpt"); exit(-1); } int pid = fork(); if (pid < 0) { perror("fork"); exit(-1); } if (pid == 0) { /* child */ char *pts = ptsname(master); int slave = open(pts, O_RDWR); if (slave == -1) { perror(pts); exit(-1); } close(master); if (putenv("TERM=adm3a")) { perror("putenv"); exit(-1); } struct winsize size = { .ws_row = rows, .ws_col = cols, .ws_xpixel = 0, .ws_ypixel = 0, }; if (ioctl(slave, TIOCSWINSZ, &size)) { perror("TIOCSWINSZ"); exit(-1); } if (setsid() == -1) { perror("setsid"); exit(-1); } close(0); int fd = dup(slave); close(1); fd = dup(slave); close(2); fd = dup(slave); close(slave); fd = fd; /* suppress compiler warnings */ execl(program, program, NULL); perror(program); exit(-1); abort(); } child rv = { .fd = master, .pid = pid }; return rv; } int do_shell_loop(xshmu w, child kid, admu t) { fd_set read_fds; for (;;) { xshmu_pic fb = xshmu_framebuffer(w); admu_generate_chars(&t, fb.p, fb.w, fb.stride, fb.h); xshmu_flush(w); FD_ZERO(&read_fds); FD_SET(kid.fd, &read_fds); FD_SET(xshmu_fd(w), &read_fds); int max_fd = kid.fd > xshmu_fd(w) ? kid.fd : xshmu_fd(w); int foo = select(max_fd+1, &read_fds, NULL, NULL, NULL); if (foo < 0) { perror("select"); kill(kid.pid, 9); return -1; } for (xshmu_event *ev; (ev = xshmu_get_event(w));) { if (xshmu_as_die_event(ev)) return 0; xshmu_key_event *kev = xshmu_as_key_event(ev); if (kev && kev->down) { if (kev->len) { errno = 0; /* XXX can we get a partial write here? */ if (write(kid.fd, kev->s, kev->len) < 1) { perror("write to child"); } } else { /* These are kind of bogus but they are the closest thing to arrow keys that the ADM-3A had */ int n; switch(kev->keysym) { case xks_left: n = write(kid.fd, "\010", 1); break; /* ^H */ case xks_down: n = write(kid.fd, "\012", 1); break; /* ^J */ case xks_up: n = write(kid.fd, "\013", 1); break; /* ^K */ case xks_right: n = write(kid.fd, "\014", 1); break; /* ^L */ } n = n; /* perhaps we should actually check these writes for errors */ } } } if (FD_ISSET(kid.fd, &read_fds)) { char buf[128]; ssize_t size = read(kid.fd, buf, sizeof buf); if (size == 0) { /* eof from child; this never happens */ return 0; } if (size < 0) { perror("read from child"); kill(kid.pid, 9); return -1; } for (int i = 0; i < size; i++) { admu_handle_char(&t, buf[i]); } } } } int main(int argc, char **argv) { enum { rows = 24, cols = 80, height = rows*admu_font_h, width = cols*admu_font_w }; xshmu w = xshmu_open("admu 3a", width, height, ""); admu t; unsigned char buf[cols*rows]; admu_init(&t, cols, rows, buf); char *shell = getenv("SHELL"); if (!shell) shell = "/bin/sh"; child kid = spawn_child(shell, cols, rows); int rv = do_shell_loop(w, kid, t); xshmu_close(w); return rv; }