fix numerous bugs, add tty controls, etc

This commit is contained in:
~d6 2023-12-14 23:43:37 -05:00
parent 05cf2c6ef3
commit 8ddc1b7676
6 changed files with 109 additions and 73 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') - 0x01 - (execute) command string (e.g. 'gcc -o "demo" demo.c')
- 0x02 - (getpid) unused - 0x02 - (getpid) unused
- 0x03 - (kill) unused - 0x03 - (kill) unused
- 0x04 - (raw-tty) unused
- 0x04 - (restore-tty) unused
- 0x10 - (getenv) name string (e.g. "TERM") - 0x10 - (getenv) name string (e.g. "TERM")
- 0x11 - (setenv) assignment string (e.g. "TERM=vt100") - 0x11 - (setenv) assignment string (e.g. "TERM=vt100")
strings must be null-terminated. commands are parsed by /bin/sh -c. 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 - 0x01 - execute: reads command string, starts a subprocess
- 0x02 - getpid: responds with child process pid (if any) - 0x02 - getpid: responds with child process pid (if any)
- 0x03 - kill: shuts down child process - 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 - 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 - 0x11 - setenv: reads an assignment (e.g. TERM=vt100), updates env

View File

@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#ifdef __linux #ifdef __linux
@ -32,27 +33,28 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. 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_ptys(mode) (mode & CONSOLE_MODE_PTY)
#define child_reads(mode) (mode & CONSOLE_CHILD_READS) #define child_reads(mode) (mode & CONSOLE_CHILD_READS)
#define child_writes(mode) (mode & CONSOLE_CHILD_WRITES) #define child_writes(mode) (mode & CONSOLE_CHILD_WRITES)
#define child_writes_out(mode) (mode & CONSOLE_MODE_OUT) #define child_writes_out(mode) (mode & CONSOLE_MODE_OUT)
#define child_writes_err(mode) (mode & CONSOLE_MODE_ERR) #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 static void
handle_sigchld(int sig) handle_sigchld(int sig)
{ {
@ -61,13 +63,16 @@ handle_sigchld(int sig)
while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) { while((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
UxnSubprocess *child = find_child_by_pid(pid); UxnSubprocess *child = find_child_by_pid(pid);
if (child != NULL) { if (child != NULL) {
/* set running to negative when exiting for clean up */
child->running = WEXITSTATUS(wstatus) - 256; child->running = WEXITSTATUS(wstatus) - 256;
/* close relevant file descriptors */
if(child_ptys(child->mode)) if(child_ptys(child->mode))
close(child->pty); close(child->pty);
else { else {
if(child_reads(child->mode)) close(child->fd_in); if(child_reads(child->mode)) close(child->fd_in);
if(child_writes(child->mode)) close(child->fd_out); if(child_writes(child->mode)) close(child->fd_out);
} }
/* reset fields to starting values */
child->pid = child->mode = 0; child->pid = child->mode = 0;
child->fd_out = child->fd_in = child->pty = -1; child->fd_out = child->fd_in = child->pty = -1;
return; return;
@ -79,7 +84,7 @@ static void
start_fork_pty(UxnSubprocess *child, int mode) start_fork_pty(UxnSubprocess *child, int mode)
{ {
int fd = -1; 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); pid_t pid = forkpty(&fd, NULL, NULL, &ws);
if(pid < 0) { /* failure */ if(pid < 0) { /* failure */
fprintf(stderr, "parent fork failure\n"); fprintf(stderr, "parent fork failure\n");
@ -95,6 +100,7 @@ start_fork_pty(UxnSubprocess *child, int mode)
child->fd_in = fd; child->fd_in = fd;
child->fd_out = fd; child->fd_out = fd;
} while (child->pid != pid); } while (child->pid != pid);
/* loop to avoid possible race with clean up */
} }
} }
@ -120,6 +126,7 @@ start_fork_pipe(UxnSubprocess *child, int mode)
pid_t pid = fork(); pid_t pid = fork();
if(pid < 0) { /* failure */ if(pid < 0) { /* failure */
fprintf(stderr, "fork failure\n"); fprintf(stderr, "fork failure\n");
return;
} else if(pid == 0) { /* child */ } else if(pid == 0) { /* child */
if(child_reads(mode)) { if(child_reads(mode)) {
dup2(to_child_fd[0], 0); dup2(to_child_fd[0], 0);
@ -142,6 +149,9 @@ start_fork_pipe(UxnSubprocess *child, int mode)
child->fd_in = child_reads(mode) ? to_child_fd[1] : -1; child->fd_in = child_reads(mode) ? to_child_fd[1] : -1;
child->fd_out = child_writes(mode) ? from_child_fd[0] : -1; child->fd_out = child_writes(mode) ? from_child_fd[0] : -1;
} while (child->pid != pid); } while (child->pid != 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_reads(mode)) close(to_child_fd[0]);
if(child_writes(mode)) close(from_child_fd[1]); if(child_writes(mode)) close(from_child_fd[1]);
} }
@ -173,7 +183,7 @@ host_execute(Uxn *u, Uint8 *d)
UxnSubprocess *child = &children[opts & 0x3]; UxnSubprocess *child = &children[opts & 0x3];
fork_args[2] = cmd; fork_args[2] = cmd;
if(child->pid) if(child->pid > 0)
kill(child->pid, 9); kill(child->pid, 9);
if(opts >= 0x80) if(opts >= 0x80)
@ -202,13 +212,42 @@ static void
host_kill(Uxn *u, Uint8 *d) host_kill(Uxn *u, Uint8 *d)
{ {
int n = d[0xe] & 0x3; int n = d[0xe] & 0x3;
if(children[n].pid > 0)
kill(children[n].pid, 15); 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 static void
host_getenv(Uxn *u, Uint8 *d) host_getenv(Uxn *u, Uint8 *d)
{ {
int addr = PEEK2(d + 0x3); int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr]; char *name = (char *)&u->ram[addr];
host_response(u, getenv(name)); host_response(u, getenv(name));
} }
@ -216,23 +255,29 @@ host_getenv(Uxn *u, Uint8 *d)
static void static void
host_setenv(Uxn *u, Uint8 *d) host_setenv(Uxn *u, Uint8 *d)
{ {
int addr = PEEK2(d + 0x3); int addr = PEEK2(d + 0xc);
char *name = (char *)&u->ram[addr]; char *name = (char *)&u->ram[addr];
int i = 0; int i = 0;
while(name[i] != '=') i++; while(name[i] != '=') i++;
if(name[i] == '=') {
name[i] = '\0'; name[i] = '\0';
char *value = &name[i+1]; char *value = &name[i+1];
setenv(name, value, 1); setenv(name, value, 1);
} else {
fprintf(stderr, "mal-formed setenv\n");
}
} }
static void static void
host_put(Uxn *u, Uint8 *d) host_put(Uxn *u, Uint8 *d)
{ {
switch(d[0xf]) { switch(d[0xf]) {
case 0x0: /* nop */ break; case 0x00: /* nop */ break;
case 0x1: host_execute(u, d); break; case 0x01: host_execute(u, d); break;
case 0x2: host_getpid(u, d); break; case 0x02: host_getpid(u, d); break;
case 0x3: host_kill(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 0x10: host_getenv(u, d); break;
case 0x11: host_setenv(u, d); break; case 0x11: host_setenv(u, d); break;
} }
@ -266,7 +311,7 @@ UxnSubprocess
void void
console_monitor(Uxn *u) console_monitor(Uxn *u)
{ {
fflush(stdout); /*fflush(stdout);*/ /* TODO: needed? stderr? children? */
for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) { for(int n = 0; n < CONSOLE_MAX_CHILDREN; n++) {
UxnSubprocess *child = &children[n]; UxnSubprocess *child = &children[n];
if(child->running < 0) { if(child->running < 0) {
@ -277,7 +322,6 @@ console_monitor(Uxn *u)
} }
} }
// TODO ignore port
int int
console_input(Uxn *u, char c, int type) console_input(Uxn *u, char c, int type)
{ {

View File

@ -47,8 +47,11 @@ void console_deo(Uxn *u, Uint8 *d, Uint8 port);
#define CONSOLE_MODE_OUT 0x20 #define CONSOLE_MODE_OUT 0x20
#define CONSOLE_MODE_INP 0x10 #define CONSOLE_MODE_INP 0x10
#define CONSOLE_CHILD_READS 0x90 /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */ /* CONSOLE_MODE_PTY | CONSOLE_MODE_INP */
#define CONSOLE_CHILD_WRITES 0xe0 /* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */ #define CONSOLE_CHILD_READS 0x90
/* CONSOLE_MODE_PTY | CONSOLE_MODE_OUT | CONSOLE_MODE_ERR */
#define CONSOLE_CHILD_WRITES 0xe0
#define CONSOLE_MAX_CHILDREN 4 #define CONSOLE_MAX_CHILDREN 4

View File

@ -268,17 +268,14 @@ emu_run(Uxn *u, char *rom)
timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL); timerfd_settime(fds[1].fd, 0, &screen_tspec, NULL);
fds[2].fd = STDIN_FILENO; fds[2].fd = STDIN_FILENO;
fds[0].events = fds[1].events = fds[2].events = POLLIN; 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 */ /* main loop */
while(!u->dev[0x0f]) { 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].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; continue;
while(XPending(display)) while(XPending(display))
emu_event(u); emu_event(u);
@ -297,7 +294,7 @@ emu_run(Uxn *u, char *rom)
handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END); handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
/* read input from child processes */ /* read input from child processes */
for(i = 0; i < 4; i++) for(i = 0; i < CONSOLE_MAX_CHILDREN; i++)
handle_input(u, fds[i + 3], coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i); handle_input(u, fds[i + 3], coninp, CONSOLE_CHILD_DATA | i, CONSOLE_CHILD_END | i);
/* check to see if any children exited */ /* check to see if any children exited */

View File

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

View File

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