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];