commit 67b06c3

Devine Lu Linvega  ·  2024-08-25 21:07:40 +0000 UTC
parent 436e579
Added linux subprocess API
5 files changed,  +322, -0
+131, -0
  1@@ -0,0 +1,131 @@
  2+( mp3.tal )
  3+
  4+( commands are ended by newlines: )
  5+( )
  6+( help                         show all these commands )
  7+( load FILE                    load FILE and start playing )
  8+( loadpaused FILE              load FILE without playing )
  9+( pause                        pause, or unpause, playback )
 10+( stop                         stops playback, unloads file )
 11+( seek #                       seeks to sample # )
 12+( seek +N | seek -N            seeks forward, or back, N samples )
 13+( seek +Ns | seek -Ns          seeks forward, or back, N seconds )
 14+( jump #                       jumps to MPEG frame # )
 15+( jump +N | jump -N            jumps forward, or back, N frames )
 16+( jump +Ns | jump -Ns          jumps forward, or back, N seconds )
 17+( volume P                     set volume to P percent, 0-100 )
 18+( mute                         mute playback )
 19+( unmute                       unmute playback )
 20+( tag                          print all ID3 data; seeking back may remove tag data )
 21+
 22+@Console [
 23+  |10 &vector $2
 24+  |12 &read $1
 25+  ( 13 - 14 padding )
 26+  |15 &live $1
 27+  |15 &exit $1
 28+  |17 &type $1
 29+  |18 &write $1
 30+  |19 &error $1
 31+  ( 1a - 1b padding )
 32+  |1c &addr $2
 33+  |1e &mode $1
 34+  |1f &exec $1
 35+]
 36+
 37+|0100
 38+    ( initialize buffer )
 39+    ;buffer ;buffer/pos STA2
 40+
 41+    ( run mpg123 )
 42+    ;on-console .Console/vector DEO2
 43+    ;program .Console/addr DEO2 ( cmd addr )
 44+    #03 .Console/mode DEO ( cmd mode )
 45+    #01 .Console/exec DEO ( exec )
 46+
 47+    ;cmd1 print
 48+    ;cmd2 print
 49+
 50+    BRK
 51+
 52+@printerr ( s* -> )
 53+    LDAk ?{ #0a .Console/error DEO POP2 JMP2r }
 54+    LDAk .Console/error DEO INC2 !printerr
 55+
 56+@print ( s* -> )
 57+    LDAk ?{ POP2 JMP2r }
 58+    LDAk .Console/write DEO INC2 !print
 59+
 60+@on-console ( -> brk )
 61+    .Console/type DEI #01 EQU ?{ BRK }  ( )
 62+    .Console/read DEI #0a EQU ?&newline ( )
 63+    ;buffer/pos LDA2k STH2k             ( pos* buf* [buf*] )
 64+    .Console/read DEI STH2r STA         ( pos* buf ; buf<-c )
 65+    INC2 SWP2 STA2 BRK                  ( ; pos<-buf+1 )
 66+    &newline                            ( )
 67+    #00 ;buffer/pos LDA2 STA on-line    ( ; buf<-0, run on-line )
 68+    ;buffer ;buffer/pos STA2 BRK        ( )
 69+
 70+
 71+( called when a newline is reached )
 72+( buffer is guaranteed to be null-terminated )
 73+( newline is implied but not included )
 74+@on-line ( -> )
 75+    ;buffer LDAk LIT "@ EQU ?{ POP2 JMP2r }
 76+    INC2k LDA LIT "F EQU ?on-frame
 77+    ( INC2k LDA LIT "H EQU ?on-help )
 78+    ( INC2k LDA LIT "I EQU ?on-id3 )
 79+    ( INC2k LDA LIT "P EQU ?on-paused )
 80+    ( INC2k LDA LIT "R EQU ?on-revision )
 81+    ( INC2k LDA LIT "S EQU ?on-status )
 82+    ( INC2k LDA LIT "T EQU ?on-tag )
 83+    !printerr
 84+
 85+( e.g. "@F 184 8713 4.81 227.60" )
 86+( seen when playing )
 87+( the fields here are: )
 88+( @F <curr-frame> <total-frames> <curr-secs> <total-secs> )
 89+@on-frame ( buf* -> )
 90+    POP2 JMP2r
 91+
 92+( e.g. "@H HELP/H: command listing (LONG/SHORT forms), command case insensitve" )
 93+( seen in response to the "help" command )
 94+@on-help ( buf* -> )
 95+    POP2r JMP2r
 96+
 97+( e.g. "@I ID3v2.artist:Chipzel" )
 98+( seen when loading a track )
 99+@on-id3 ( buf* -> )
100+    POP2 JMP2r
101+
102+( e.g. "@P 0" or "@P 1" )
103+( seen when pausing or unpausing )
104+@on-paused ( buf* -> )
105+    POP2 JMP2r
106+
107+( e.g. "@R MPG123 (ThOr) v10" )
108+( seen on start up )
109+@on-revision ( buf* -> )
110+    POP2 JMP2r
111+
112+( e.g. "@S 1.0 3 44100 Joint-Stereo 0 1044 2 0 0 0 320 0 1" )
113+( seen when playback starts )
114+@on-status ( buf* -> )
115+    POP2 JMP2r
116+
117+( e.g. "@T ID3v2.TPE1:" )
118+( seen in response to the "tag" command for extended tag info )
119+@on-tag ( buf* -> )
120+    POP2 JMP2r
121+
122+@program
123+    "mpg123 20 "-R 00
124+
125+@cmd1
126+    "loadpaused 20 "always_wrong.mp3 0a 00
127+
128+@cmd2
129+    "pause 0a 00
130+
131+
132+@buffer $200 &pos $2 ( input buffer )
+186, -0
  1@@ -1,5 +1,22 @@
  2+#undef _POSIX_C_SOURCE
  3+#define _POSIX_C_SOURCE 200112L
  4+
  5+#include <signal.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8+#include <sys/select.h>
  9+#include <sys/wait.h>
 10+#include <unistd.h>
 11+
 12+#ifdef __linux
 13+#include <pty.h>
 14+#include <sys/prctl.h>
 15+#endif
 16+
 17+#ifdef __NetBSD__
 18+#include <sys/ioctl.h>
 19+#include <util.h>
 20+#endif
 21 
 22 #include "../uxn.h"
 23 #include "console.h"
 24@@ -15,6 +32,164 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 25 WITH REGARD TO THIS SOFTWARE.
 26 */
 27 
 28+/* subprocess support */
 29+static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
 30+static int child_mode;
 31+static int to_child_fd[2];
 32+static int from_child_fd[2];
 33+static int saved_in;
 34+static int saved_out;
 35+static pid_t child_pid;
 36+
 37+/* child_mode:
 38+ * 0x01: writes to child's stdin
 39+ * 0x02: reads from child's stdout
 40+ * 0x04: reads from child's stderr
 41+ * 0x08: kill previous process (if any) but do not start
 42+ * (other bits ignored for now )
 43+ */
 44+
 45+#define CMD_LIVE 0x15 // 0x00 not started, 0x01 running, 0xff dead
 46+#define CMD_EXIT 0x16 // if dead, exit code of process
 47+#define CMD_ADDR 0x1c // address to read command args from
 48+#define CMD_MODE 0x1e // mode to execute, 0x00 to 0x07
 49+#define CMD_EXEC 0x1f // write to execute programs, etc
 50+
 51+/* call after we're sure the process has exited */
 52+static void
 53+clean_after_child(void)
 54+{
 55+	child_pid = 0;
 56+	if(child_mode & 0x01) {
 57+		close(to_child_fd[1]);
 58+		dup2(saved_out, 1);
 59+	}
 60+	if(child_mode & (0x04 | 0x02)) {
 61+		close(from_child_fd[0]);
 62+		dup2(saved_in, 0);
 63+	}
 64+	child_mode = 0;
 65+	saved_in = -1;
 66+	saved_out = -1;
 67+}
 68+
 69+static void
 70+start_fork_pipe(void)
 71+{
 72+	fflush(stdout);
 73+	pid_t pid;
 74+	pid_t parent_pid = getpid();
 75+	int addr = PEEK2(&uxn.dev[CMD_ADDR]);
 76+	if(child_mode & 0x08) {
 77+		uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0x00;
 78+		return;
 79+	}
 80+	if(child_mode & 0x01) {
 81+		/* parent writes to child's stdin */
 82+		if(pipe(to_child_fd) == -1) {
 83+			uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
 84+			fprintf(stderr, "pipe error: to child\n");
 85+			return;
 86+		}
 87+	}
 88+	if(child_mode & (0x04 | 0x02)) {
 89+		/* parent reads from child's stdout and/or stderr */
 90+		if(pipe(from_child_fd) == -1) {
 91+			uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
 92+			fprintf(stderr, "pipe error: from child\n");
 93+			return;
 94+		}
 95+	}
 96+
 97+	fork_args[2] = (char *)&uxn.ram[addr];
 98+	pid = fork();
 99+	if(pid < 0) { /* failure */
100+		uxn.dev[CMD_EXIT] = uxn.dev[CMD_LIVE] = 0xff;
101+		fprintf(stderr, "fork failure\n");
102+	} else if(pid == 0) { /* child */
103+
104+#ifdef __linux__
105+		int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
106+		if (r == -1) { perror(0); exit(6); }
107+		// test in case the original parent exited just
108+		// before the prctl() call
109+		if (getppid() != parent_pid) exit(13);
110+#endif
111+
112+		if(child_mode & 0x01) {
113+			dup2(to_child_fd[0], 0);
114+			close(to_child_fd[1]);
115+		}
116+		if(child_mode & (0x04 | 0x02)) {
117+			if(child_mode & 0x02) dup2(from_child_fd[1], 1);
118+			if(child_mode & 0x04) dup2(from_child_fd[1], 2);
119+			close(from_child_fd[0]);
120+		}
121+		fflush(stdout);
122+		execvp(fork_args[0], fork_args);
123+		exit(1);
124+	} else { /*parent*/
125+		child_pid = pid;
126+		uxn.dev[CMD_LIVE] = 0x01;
127+		uxn.dev[CMD_EXIT] = 0x00;
128+		if(child_mode & 0x01) {
129+			saved_out = dup(1);
130+			dup2(to_child_fd[1], 1);
131+			close(to_child_fd[0]);
132+		}
133+		if(child_mode & (0x04 | 0x02)) {
134+			saved_in = dup(0);
135+			dup2(from_child_fd[0], 0);
136+			close(from_child_fd[1]);
137+		}
138+	}
139+}
140+
141+static void
142+check_child(void)
143+{
144+	int wstatus;
145+	if(child_pid) {
146+		if(waitpid(child_pid, &wstatus, WNOHANG)) {
147+			uxn.dev[CMD_LIVE] = 0xff;
148+			uxn.dev[CMD_EXIT] = WEXITSTATUS(wstatus);
149+			clean_after_child();
150+		} else {
151+			uxn.dev[CMD_LIVE] = 0x01;
152+			uxn.dev[CMD_EXIT] = 0x00;
153+		}
154+	}
155+}
156+
157+static void
158+kill_child(void)
159+{
160+	int wstatus;
161+	if(child_pid) {
162+		kill(child_pid, 9);
163+		if(waitpid(child_pid, &wstatus, WNOHANG)) {
164+			uxn.dev[CMD_LIVE] = 0xff;
165+			uxn.dev[CMD_EXIT] = WEXITSTATUS(wstatus);
166+			clean_after_child();
167+		}
168+	}
169+}
170+
171+static void
172+start_fork(void)
173+{
174+	fflush(stderr);
175+	kill_child();
176+	child_mode = uxn.dev[CMD_MODE];
177+	start_fork_pipe();
178+}
179+
180+void
181+close_console(void)
182+{
183+	kill_child();
184+}
185+
186 int
187 console_input(Uint8 c, int type)
188 {
189@@ -33,6 +208,16 @@ console_listen(int i, int argc, char **argv)
190 	}
191 }
192 
193+Uint8
194+console_dei(Uint8 addr)
195+{
196+	switch(addr) {
197+	case CMD_LIVE:
198+	case CMD_EXIT: check_child(); break;
199+	}
200+	return uxn.dev[addr];
201+}
202+
203 void
204 console_deo(Uint8 addr)
205 {
206@@ -40,5 +225,6 @@ console_deo(Uint8 addr)
207 	switch(addr) {
208 	case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break;
209 	case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break;
210+	case CMD_EXEC: start_fork(); break;
211 	}
212 }
+1, -0
1@@ -18,3 +18,4 @@ int console_input(Uint8 c, int type);
2 void console_listen(int i, int argc, char **argv);
3 Uint8 console_dei(Uint8 addr);
4 void console_deo(Uint8 addr);
5+void close_console(void);
+3, -0
 1@@ -49,6 +49,7 @@ emu_dei(Uint8 addr)
 2 {
 3 	switch(addr & 0xf0) {
 4 	case 0x00: return system_dei(addr);
 5+	case 0x10: return console_dei(addr);
 6 	case 0x20: return screen_dei(addr);
 7 	case 0xc0: return datetime_dei(addr);
 8 	}
 9@@ -89,6 +90,7 @@ emu_resize(int w, int h)
10 static void
11 emu_restart(char *rom, int soft)
12 {
13+	close_console();
14 	screen_resize(WIDTH, HEIGHT, uxn_screen.scale);
15 	screen_rect(uxn_screen.bg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
16 	screen_rect(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
17@@ -98,6 +100,7 @@ emu_restart(char *rom, int soft)
18 static int
19 emu_end(void)
20 {
21+	close_console();
22 	free(uxn.ram);
23 	XDestroyImage(ximage);
24 	XDestroyWindow(display, window);
+1, -0
1@@ -25,6 +25,7 @@ emu_dei(Uint8 addr)
2 {
3 	switch(addr & 0xf0) {
4 	case 0x00: return system_dei(addr);
5+	case 0x10: return console_dei(addr);
6 	case 0xc0: return datetime_dei(addr);
7 	}
8 	return uxn.dev[addr];