Compare commits

...

2 Commits

Author SHA1 Message Date
~d6 8ddc1b7676 fix numerous bugs, add tty controls, etc 2023-12-14 23:43:37 -05:00
~d6 05cf2c6ef3 clean up 2023-12-13 12:20:05 -05:00
7 changed files with 126 additions and 123 deletions

View File

@ -63,6 +63,8 @@ for a host-put (0x1f). the meaning values by host-put value:
- 0x01 - (execute) command string (e.g. 'gcc -o "demo" demo.c')
- 0x02 - (getpid) unused
- 0x03 - (kill) unused
- 0x04 - (raw-tty) unused
- 0x04 - (restore-tty) unused
- 0x10 - (getenv) name string (e.g. "TERM")
- 0x11 - (setenv) assignment string (e.g. "TERM=vt100")
strings must be null-terminated. commands are parsed by /bin/sh -c.
@ -86,6 +88,8 @@ the host-put port (0x1f) specifies which host action to take:
- 0x01 - execute: reads command string, starts a subprocess
- 0x02 - getpid: responds with child process pid (if any)
- 0x03 - kill: shuts down child process
- 0x04 - put stdin tty into raw mode
- 0x05 - restore stdin tty to canonical mode
- 0x10 - getenv: looks up a name (e.g. TERM) in env, responds with value
- 0x11 - setenv: reads an assignment (e.g. TERM=vt100), updates env

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#ifdef __linux
@ -32,27 +33,28 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE.
*/
UxnSubprocess children[CONSOLE_MAX_CHILDREN];
static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
static int to_child_fd[2], from_child_fd[2];
static char buf[16];
static UxnSubprocess*
find_child_by_pid(pid_t pid)
{
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
if(children[n].pid == pid)
return &children[n];
}
return NULL;
}
#define child_ptys(mode) (mode & CONSOLE_MODE_PTY)
#define child_reads(mode) (mode & CONSOLE_CHILD_READS)
#define child_writes(mode) (mode & CONSOLE_CHILD_WRITES)
#define child_writes_out(mode) (mode & CONSOLE_MODE_OUT)
#define child_writes_err(mode) (mode & CONSOLE_MODE_ERR)
UxnSubprocess children[CONSOLE_MAX_CHILDREN];
static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
static int to_child_fd[2], from_child_fd[2];
static char buf[16];
static int raw_tty = 0;
struct termios old_termios;
static UxnSubprocess*
find_child_by_pid(pid_t pid)
{
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++)
if(children[n].pid == pid)
return &children[n];
return NULL;
}
static void
handle_sigchld(int sig)
{
@ -61,29 +63,28 @@ handle_sigchld(int sig)
while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
UxnSubprocess *child = find_child_by_pid(pid);
if (child != NULL) {
/* set running to negative when exiting for clean up */
child->running = WEXITSTATUS(wstatus) - 256;
if(CONSOLE_DEBUG)
fprintf(stderr, "handle_sigchld: %d -> child %d (%d) -> %d\n", sig, child->id, pid, child->running);
/* close relevant file descriptors */
if(child_ptys(child->mode))
close(child->pty);
else {
if(child_reads(child->mode)) close(child->fd_in);
if(child_writes(child->mode)) close(child->fd_out);
}
/* reset fields to starting values */
child->pid = child->mode = 0;
child->fd_out = child->fd_in = child->pty = -1;
return;
}
}
if(CONSOLE_DEBUG)
fprintf(stderr, "handle_sigchld: no child found", sig);
}
static void
start_fork_pty(UxnSubprocess *child, int mode)
{
int fd = -1;
struct winsize ws = {23, 80, 8, 12}; // rows, cols, xps, ypx
struct winsize ws = {23, 80, 8, 12};
pid_t pid = forkpty(&fd, NULL, NULL, &ws);
if(pid < 0) { /* failure */
fprintf(stderr, "parent fork failure\n");
@ -99,13 +100,13 @@ start_fork_pty(UxnSubprocess *child, int mode)
child->fd_in = fd;
child->fd_out = fd;
} while (child->pid != pid);
/* loop to avoid possible race with clean up */
}
}
static void
start_fork_pipe(UxnSubprocess *child, int mode)
{
//printf("start_fork_pipe(..., %d)\n", mode);
if(child_reads(mode)) {
/* parent writes to child's stdin */
if(pipe(to_child_fd) == -1) {
@ -125,6 +126,7 @@ start_fork_pipe(UxnSubprocess *child, int mode)
pid_t pid = fork();
if(pid < 0) { /* failure */
fprintf(stderr, "fork failure\n");
return;
} else if(pid == 0) { /* child */
if(child_reads(mode)) {
dup2(to_child_fd[0], 0);
@ -147,19 +149,12 @@ start_fork_pipe(UxnSubprocess *child, int mode)
child->fd_in = child_reads(mode) ? to_child_fd[1] : -1;
child->fd_out = child_writes(mode) ? from_child_fd[0] : -1;
} while (child->pid != pid);
//printf("child %d: pid=%d mode=%d running=%d in=%d out=%d\n", child->id, child->pid, child->mode, child->running, child->fd_in, child->fd_out);
if(CONSOLE_DEBUG)
fprintf(stderr, "child has pid %d\n", child->pid);
/* loop to avoid possible race with clean up */
/* close the unused end of pipes we opened */
if(child_reads(mode)) close(to_child_fd[0]);
if(child_writes(mode)) close(from_child_fd[1]);
}
/* /\* TODO *\/ */
/* for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) { */
/* UxnSubprocess *child = &children[n]; */
/* printf("child: %d/%d (%d, %d)\n", n, child->id, child->fd_out, child->pid); */
/* } */
}
static void
@ -174,8 +169,6 @@ proc_put(Uxn *u, Uint8 *d)
{
Uint8 c = d[0xa];
int n = d[0xe] & 0x3;
if (CONSOLE_DEBUG)
fprintf(stderr, " fd %d: write %02x\n", children[n].fd_in, c);
write(children[n].fd_in, &c, 1);
fdatasync(children[n].fd_in);
}
@ -190,13 +183,8 @@ host_execute(Uxn *u, Uint8 *d)
UxnSubprocess *child = &children[opts & 0x3];
fork_args[2] = cmd;
if(CONSOLE_DEBUG)
fprintf(stderr, "running execute: %s (#%02x)\n", cmd, opts);
if(child->pid) {
if(CONSOLE_DEBUG)
fprintf(stderr, " killing previous child: %d\n", child->pid);
if(child->pid > 0)
kill(child->pid, 9);
}
if(opts >= 0x80)
start_fork_pty(child, mode);
@ -208,8 +196,8 @@ static void
host_response(Uxn *u, char *s)
{
for(int i = 0;; i++)
console_input(u, 0x5, s[i], CONSOLE_HOST);
console_input(u, 0x5, '\0', CONSOLE_HOST_END);
console_input(u, s[i], CONSOLE_HOST);
console_input(u, '\0', CONSOLE_HOST_END);
}
static void
@ -224,13 +212,42 @@ static void
host_kill(Uxn *u, Uint8 *d)
{
int n = d[0xe] & 0x3;
if(children[n].pid > 0)
kill(children[n].pid, 15);
}
static void
host_raw_tty()
{
if(!raw_tty) {
tcgetattr(STDIN_FILENO, &old_termios);
struct termios term = old_termios;
/* corresponds to `stty raw -echo` */
term.c_cflag |= (CS8);
term.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP);
term.c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF | IUCLC | IXANY | IMAXBEL);
term.c_lflag &= ~(ICANON | ISIG | ECHO);
term.c_oflag &= ~(OPOST);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSAFLUSH, &term);
raw_tty = 1;
}
}
static void
host_restore_tty()
{
if(raw_tty) {
tcsetattr(0, TCSAFLUSH, &old_termios);
raw_tty = 0;
}
}
static void
host_getenv(Uxn *u, Uint8 *d)
{
int addr = PEEK2(d + 0x3);
int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr];
host_response(u, getenv(name));
}
@ -238,23 +255,29 @@ host_getenv(Uxn *u, Uint8 *d)
static void
host_setenv(Uxn *u, Uint8 *d)
{
int addr = PEEK2(d + 0x3);
int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr];
int i = 0;
while (name[i] != '=') i++;
while(name[i] != '=') i++;
if(name[i] == '=') {
name[i] = '\0';
char *value = &name[i+1];
setenv(name, value, 1);
} else {
fprintf(stderr, "mal-formed setenv\n");
}
}
static void
host_put(Uxn *u, Uint8 *d)
{
switch(d[0xf]) {
case 0x0: /* nop */ break;
case 0x1: host_execute(u, d); break;
case 0x2: host_getpid(u, d); break;
case 0x3: host_kill(u, d); break;
case 0x00: /* nop */ break;
case 0x01: host_execute(u, d); break;
case 0x02: host_getpid(u, d); break;
case 0x03: host_kill(u, d); break;
case 0x04: host_raw_tty(); break;
case 0x05: host_restore_tty(); break;
case 0x10: host_getenv(u, d); break;
case 0x11: host_setenv(u, d); break;
}
@ -288,23 +311,19 @@ UxnSubprocess
void
console_monitor(Uxn *u)
{
fflush(stdout);
/*fflush(stdout);*/ /* TODO: needed? stderr? children? */
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
UxnSubprocess *child = &children[n];
/* printf("child: %d/%d (%d, %d)\n", n, child->id, child->fd_out, child->pid); */
if(child->running < 0) {
/* printf("child ready for clean up: %d\n", n); */
/* fflush(stdout); */
Uint8 status = child->running + 256;
child->running = 0;
console_input(u, 0x4, status, CONSOLE_CHILD_EXIT | n);
console_input(u, status, CONSOLE_CHILD_EXIT | n);
}
}
}
// TODO ignore port
int
console_input(Uxn *u, int port, char c, int type)
console_input(Uxn *u, char c, int type)
{
Uint8 *d = &u->dev[0x10];
d[0x2] = c;
@ -317,8 +336,8 @@ console_listen(Uxn *u, int i, int argc, char **argv)
{
for(; i < argc; i++) {
char *p = argv[i];
while(*p) console_input(u, 0x2, *p++, CONSOLE_ARG);
console_input(u, 0x2, '\n', i == argc - 1 ? CONSOLE_ARG_END : CONSOLE_ARG_SPACER);
while(*p) console_input(u, *p++, CONSOLE_ARG);
console_input(u, '\n', i == argc - 1 ? CONSOLE_ARG_END : CONSOLE_ARG_SPACER);
}
}

View File

@ -11,8 +11,6 @@ WITH REGARD TO THIS SOFTWARE.
#define CONSOLE_VERSION 1
#define CONSOLE_DEBUG 0
#define CONSOLE_NONE 0x00
#define CONSOLE_STDIN 0x01
#define CONSOLE_ARG 0x02
@ -39,7 +37,7 @@ typedef struct UxnSubprocess {
void init_console(void);
UxnSubprocess *get_child(int n);
void console_monitor(Uxn *u);
int console_input(Uxn *u, int port, char c, int type);
int console_input(Uxn *u, char c, int type);
void console_listen(Uxn *u, int i, int argc, char **argv);
Uint8 console_dei(Uxn *u, Uint8 addr);
void console_deo(Uxn *u, Uint8 *d, Uint8 port);
@ -49,8 +47,11 @@ void console_deo(Uxn *u, Uint8 *d, Uint8 port);
#define CONSOLE_MODE_OUT 0x20
#define CONSOLE_MODE_INP 0x10
#define CONSOLE_CHILD_READS 0x90 /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */
#define CONSOLE_CHILD_WRITES 0xe0 /* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */
/* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */
#define CONSOLE_CHILD_READS 0x90
/* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */
#define CONSOLE_CHILD_WRITES 0xe0
#define CONSOLE_MAX_CHILDREN 4

View File

@ -152,16 +152,16 @@ toggle_scale(Uxn *u)
/* returns true if the fd ended (has been closed), false otherwise */
static int
handle_input(Uxn *u, struct pollfd pollfd, int port, char *coninp, int argdata, int argend)
handle_input(Uxn *u, struct pollfd pollfd, char *coninp, int argdata, int argend)
{
if((pollfd.revents & POLLIN) != 0) {
int n = read(pollfd.fd, coninp, CONINBUFSIZE - 1);
coninp[n] = 0;
if(n == 0)
console_input(u, port, 0, argend);
console_input(u, 0, argend);
else
for(int i = 0; i < n; i++)
console_input(u, port, coninp[i], argdata);
console_input(u, coninp[i], argdata);
return n == 0;
} else
return 0;
@ -268,17 +268,14 @@ emu_run(Uxn *u, char *rom)
timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL);
fds[2].fd = STDIN_FILENO;
fds[0].events = fds[1].events = fds[2].events = POLLIN;
for(i = 0; i < 4; i++) {
fds[i + 3].fd = get_child(i)->fd_out;
fds[i + 3].events = POLLIN;
}
/* main loop */
while(!u->dev[0x0f]) {
for(i = 0; i < 4; i++) {
for(i = 0; i < CONSOLE_MAX_CHILDREN; i++) {
fds[i + 3].fd = get_child(i)->fd_out;
fds[i + 3].events = POLLIN;
}
if(poll(fds, 7, 1000) <= 0)
if(poll(fds, 3 + CONSOLE_MAX_CHILDREN, 1000) <= 0)
continue;
while(XPending(display))
emu_event(u);
@ -294,11 +291,11 @@ emu_run(Uxn *u, char *rom)
}
/* read input from stdin */
handle_input(u, fds[2], 0x2, coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
/* read input from child processes */
for(i = 0; i < 4; i++)
handle_input(u, fds[i + 3], 0x4, coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
for(i = 0; i < CONSOLE_MAX_CHILDREN; i++)
handle_input(u, fds[i + 3], coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
/* check to see if any children exited */
console_monitor(u);

View File

@ -50,17 +50,11 @@ handle_input(Uxn *u, int fd, int port, int argdata, int argend)
{
char buf[32];
int n = read(fd, &buf, 32);
if(n == 0) {
if(CONSOLE_DEBUG)
fprintf(stderr, " fd %d: closed\n", fd);
console_input(u, port, 0x00, argend);
} else {
for(int i = 0; i < n; i++) {
if(CONSOLE_DEBUG)
fprintf(stderr, " fd %d: read %02x\n", fd, buf[i]);
console_input(u, port, buf[i], argdata);
}
}
if(n == 0)
console_input(u, 0x00, argend);
else
for(int i = 0; i < n; i++)
console_input(u, buf[i], argdata);
}
static int

View File

@ -171,16 +171,12 @@
#01 .Console/host-put DEO JMP2r
( restore the terminal to "normal" settings )
@restore-terminal
;stty-sane !run-cmd
@restore-terminal ( -> )
#05 .Console/host-put DEO JMP2r
( put the terminal in raw mode )
@init-terminal ( -> )
;stty-raw !run-cmd
( command strings to use )
@stty-sane "stty 20 "sane 00
@stty-raw "stty 20 "raw 20 "-echo 00
#04 .Console/host-put DEO JMP2r
( import uxn regex library )
~regex.tal
@ -487,11 +483,7 @@
( )
( this definition is needed so the address can be used by JCN2. )
@quit-now
;finish-quit .Console/vector DEO2
restore-terminal BRK
@finish-quit
.Console/type DEI #81 NEQ ?{ #80 .System/halt DEO } BRK
restore-terminal #010f DEO BRK
( label that calls BRK )
( )

View File

@ -409,7 +409,6 @@
( send ESC [ $c )
@arrow ( c^ -> )
( .Console/w STH )
.Console/proc-put STH
#1b STHkr DEO LIT "[ STHkr DEO STHr DEO
JMP2r
@ -417,12 +416,10 @@
@paste-from-buf ( size* -> )
;paste-buf SWP2 OVR2 ADD2 SWP2 ( limit* start* )
&loop ( limit* pos* )
( LDAk .Console/w DEO INC2 ( limit* pos+1* ) )
LDAk .Console/proc-put DEO INC2 ( limit* pos+1* )
GTH2k ?&loop POP2 POP2 JMP2r
@bracket-paste ( c^ -> )
( .Console/w STH )
.Console/proc-put STH
#1b STHkr DEO
LIT "[ STHkr DEO
@ -638,7 +635,6 @@
.Controller/key DEI
DUP #08 NEQ ?&done
POP #7f ( send DEL instead of BS )
( &done .Console/w DEO BRK )
&done .Console/proc-put DEO BRK
@ctrl ( -> is-down? ) .Controller/button DEI #01 AND JMP2r
@ -646,10 +642,8 @@
( alt-XYZ emits ESC and then emits XYZ )
@on-alt-key ( -> BRK )
( #1b .Console/w DEO )
#1b .Console/proc-put DEO
ctrl ?on-ctrl-key
( .Controller/key DEI .Console/w DEO BRK )
.Controller/key DEI .Console/proc-put DEO BRK
( control seqs: )
@ -685,7 +679,6 @@
&rs #1e !&done
&us #1f !&done
&c1 LIT "@ SUB
( &done .Console/w DEO BRK )
&done .Console/proc-put DEO BRK
@on-read-priv ( -> BRK )
@ -911,15 +904,11 @@
@dsr ( n* -> )
#0006 NEQ2 ?&done
( #1b .Console/w DEO )
#1b .Console/proc-put DEO
( LIT "[ .Console/w DEO )
LIT "[ .Console/proc-put DEO
.cur-y LDZ2 INC2 emit-dec2
( LIT "; .Console/w DEO )
LIT "; .Console/proc-put DEO
.cur-x LDZ2 INC2 emit-dec2
( LIT "R .Console/w DEO )
LIT "R .Console/proc-put DEO
&done BRK
@ -1300,14 +1289,21 @@
( emit a signed short as a decimal )
@emit-sdec2 ( n* -> )
DUP2k #1f SFT2 EQUk ?&s LIT2 "- 18 DEO
DUP2k #1f SFT2 EQUk ?&s LIT "- .Console/proc-put DEO
&s MUL2 SUB2 ( fall-through to emit-dec2 )
( emit an unsigned short as a decimal )
@emit-dec2 ( n* -> )
LITr ff00 &read #000a DIV2k STH2k MUL2 SUB2 STH2r INCr ORAk ?&read
POP2 &write NIP #30 ADD #18 DEO OVRr ADDr STHkr ?&write
POP2r JMP2r
LIT2r ff00 ( n* [ff^ 0^] )
&read ( ... x* )
#000a DIV2k STH2k ( x* 10* x/10* [ff^ i^ x/10*] )
MUL2 SUB2 STH2r ( x%10* x/10* [ff^ i^] )
INCr ORAk ?&read ( x%10* x/10* [ff^ i+1^] )
POP2 ( x0* ... xn* [ff^ i+1^] )
&write
NIP #30 ADD .Console/proc-put DEO ( x0* ... xn-1* [ff^ j^] )
OVRr ADDr STHkr ?&write ( x* ... xn-1* [ff^ j-1^] )
POP2r JMP2r ( )
@debug-log "debug_term.log 00
@scratch $40 &pos $2