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')
- 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,13 +63,16 @@ 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;
/* 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;
@ -79,7 +84,7 @@ 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");
@ -95,6 +100,7 @@ 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 */
}
}
@ -120,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);
@ -142,7 +149,10 @@ 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);
if(child_reads(mode)) close(to_child_fd[0]);
/* 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]);
}
}
@ -173,7 +183,7 @@ host_execute(Uxn *u, Uint8 *d)
UxnSubprocess *child = &children[opts & 0x3];
fork_args[2] = cmd;
if(child->pid)
if(child->pid > 0)
kill(child->pid, 9);
if(opts >= 0x80)
@ -202,13 +212,42 @@ static void
host_kill(Uxn *u, Uint8 *d)
{
int n = d[0xe] & 0x3;
kill(children[n].pid, 15);
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));
}
@ -216,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++;
name[i] = '\0';
char *value = &name[i+1];
setenv(name, value, 1);
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;
}
@ -266,7 +311,7 @@ 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];
if(child->running < 0) {
@ -277,7 +322,6 @@ console_monitor(Uxn *u)
}
}
// TODO ignore port
int
console_input(Uxn *u, char c, int type)
{

View File

@ -25,13 +25,13 @@ WITH REGARD TO THIS SOFTWARE.
#define CONSOLE_CHILD_EXIT 0x80 /* exits from child-n: 0x80 | n */
typedef struct UxnSubprocess {
int id; /* identifier: 0-3 */
pid_t pid; /* when running, pid > 0 */
int mode; /* bit flags: CONSOLE_MODE_ */
int id; /* identifier: 0-3 */
pid_t pid; /* when running, pid > 0 */
int mode; /* bit flags: CONSOLE_MODE_ */
int running; /* 0=stopped, >0 running, <0 exiting */
int fd_in; /* fd to send to child; -1 is unset */
int fd_out; /* fd to read from child; -1 is unset */
int pty; /* pty for child; -1 is unset */
int fd_in; /* fd to send to child; -1 is unset */
int fd_out; /* fd to read from child; -1 is unset */
int pty; /* pty for child; -1 is unset */
} UxnSubprocess;
void init_console(void);
@ -47,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

@ -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);
@ -297,7 +294,7 @@ emu_run(Uxn *u, char *rom)
handle_input(u, fds[2], coninp, CONSOLE_STDIN, CONSOLE_STDIN_END);
/* 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);
/* check to see if any children exited */

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