Skip to content

Commit fe0db79

Browse files
authored
Add low-level APIs for running Emscripten programs in xterm.js (#19499)
When an Emscripten program calls `ioctl(fd, TCGETS, argp)` for TTY, `stream.tty.ops.ioctl_tcgets` is called if defined. This allows a user to hook this event by defining `TTY.default_tty_ops.ioctl_tcgets`. `stream.tty.ops.ioctl_tcgets` should return an object like the following format: ``` { c_iflag: number, c_oflag: number, c_cflag: number, c_lflag: number, c_cc: number[32], } ```
1 parent bedb50e commit fe0db79

8 files changed

+224
-5
lines changed

src/generated_struct_info32.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
"AUDIO_F32": 33056,
4646
"AUDIO_S16LSB": 32784,
4747
"AUDIO_U8": 8,
48+
"B38400": 15,
4849
"CLOCK_REALTIME": 0,
50+
"CREAD": 128,
51+
"CSIZE": 48,
4952
"E2BIG": 1,
5053
"EACCES": 2,
5154
"EADDRINUSE": 3,
@@ -75,6 +78,11 @@
7578
"EBUSY": 10,
7679
"ECANCELED": 11,
7780
"ECHILD": 12,
81+
"ECHO": 8,
82+
"ECHOCTL": 512,
83+
"ECHOE": 16,
84+
"ECHOK": 32,
85+
"ECHOKE": 2048,
7886
"ECHRNG": 106,
7987
"ECOMM": 124,
8088
"ECONNABORTED": 13,
@@ -311,17 +319,27 @@
311319
"File::DirectoryKind": 2,
312320
"File::SymlinkKind": 3,
313321
"File::UnknownKind": 0,
322+
"ICANON": 2,
323+
"ICRNL": 256,
324+
"IEXTEN": 32768,
325+
"IMAXBEL": 8192,
314326
"INADDR_LOOPBACK": 2130706433,
315327
"IPPROTO_TCP": 6,
316328
"IPPROTO_UDP": 17,
329+
"ISIG": 1,
330+
"IUTF8": 16384,
331+
"IXON": 1024,
317332
"MAP_ANONYMOUS": 32,
318333
"MAP_FIXED": 16,
319334
"MAP_PRIVATE": 2,
335+
"NCCS": 32,
320336
"NI_NAMEREQD": 8,
321337
"NI_NUMERICHOST": 1,
322338
"NOTIFICATION_NONE": 0,
323339
"NOTIFICATION_PENDING": 2,
324340
"NOTIFICATION_RECEIVED": 1,
341+
"ONLCR": 4,
342+
"OPOST": 1,
325343
"O_ACCMODE": 2097155,
326344
"O_APPEND": 1024,
327345
"O_CLOEXEC": 524288,
@@ -382,6 +400,7 @@
382400
"S_ISVTX": 512,
383401
"S_IWUGO": 146,
384402
"S_IXUGO": 73,
403+
"TCFLSH": 21515,
385404
"TCGETA": 21509,
386405
"TCGETS": 21505,
387406
"TCSETA": 21510,
@@ -1499,6 +1518,17 @@
14991518
"f_fsid": 28,
15001519
"f_namelen": 36
15011520
},
1521+
"termios": {
1522+
"__c_ispeed": 52,
1523+
"__c_ospeed": 56,
1524+
"__size__": 60,
1525+
"c_cc": 17,
1526+
"c_cflag": 8,
1527+
"c_iflag": 0,
1528+
"c_lflag": 12,
1529+
"c_line": 16,
1530+
"c_oflag": 4
1531+
},
15021532
"thread_profiler_block": {
15031533
"__size__": 104,
15041534
"name": 72,
@@ -1510,6 +1540,11 @@
15101540
"tv_nsec": 8,
15111541
"tv_sec": 0
15121542
},
1543+
"timeval": {
1544+
"__size__": 16,
1545+
"tv_sec": 0,
1546+
"tv_usec": 8
1547+
},
15131548
"tm": {
15141549
"__size__": 44,
15151550
"tm_gmtoff": 36,

src/generated_struct_info64.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
"AUDIO_F32": 33056,
4646
"AUDIO_S16LSB": 32784,
4747
"AUDIO_U8": 8,
48+
"B38400": 15,
4849
"CLOCK_REALTIME": 0,
50+
"CREAD": 128,
51+
"CSIZE": 48,
4952
"E2BIG": 1,
5053
"EACCES": 2,
5154
"EADDRINUSE": 3,
@@ -75,6 +78,11 @@
7578
"EBUSY": 10,
7679
"ECANCELED": 11,
7780
"ECHILD": 12,
81+
"ECHO": 8,
82+
"ECHOCTL": 512,
83+
"ECHOE": 16,
84+
"ECHOK": 32,
85+
"ECHOKE": 2048,
7886
"ECHRNG": 106,
7987
"ECOMM": 124,
8088
"ECONNABORTED": 13,
@@ -311,17 +319,27 @@
311319
"File::DirectoryKind": 2,
312320
"File::SymlinkKind": 3,
313321
"File::UnknownKind": 0,
322+
"ICANON": 2,
323+
"ICRNL": 256,
324+
"IEXTEN": 32768,
325+
"IMAXBEL": 8192,
314326
"INADDR_LOOPBACK": 2130706433,
315327
"IPPROTO_TCP": 6,
316328
"IPPROTO_UDP": 17,
329+
"ISIG": 1,
330+
"IUTF8": 16384,
331+
"IXON": 1024,
317332
"MAP_ANONYMOUS": 32,
318333
"MAP_FIXED": 16,
319334
"MAP_PRIVATE": 2,
335+
"NCCS": 32,
320336
"NI_NAMEREQD": 8,
321337
"NI_NUMERICHOST": 1,
322338
"NOTIFICATION_NONE": 0,
323339
"NOTIFICATION_PENDING": 2,
324340
"NOTIFICATION_RECEIVED": 1,
341+
"ONLCR": 4,
342+
"OPOST": 1,
325343
"O_ACCMODE": 2097155,
326344
"O_APPEND": 1024,
327345
"O_CLOEXEC": 524288,
@@ -382,6 +400,7 @@
382400
"S_ISVTX": 512,
383401
"S_IWUGO": 146,
384402
"S_IXUGO": 73,
403+
"TCFLSH": 21515,
385404
"TCGETA": 21509,
386405
"TCGETS": 21505,
387406
"TCSETA": 21510,
@@ -1499,6 +1518,17 @@
14991518
"f_fsid": 36,
15001519
"f_namelen": 48
15011520
},
1521+
"termios": {
1522+
"__c_ispeed": 52,
1523+
"__c_ospeed": 56,
1524+
"__size__": 60,
1525+
"c_cc": 17,
1526+
"c_cflag": 8,
1527+
"c_iflag": 0,
1528+
"c_lflag": 12,
1529+
"c_line": 16,
1530+
"c_oflag": 4
1531+
},
15021532
"thread_profiler_block": {
15031533
"__size__": 104,
15041534
"name": 72,
@@ -1510,6 +1540,11 @@
15101540
"tv_nsec": 8,
15111541
"tv_sec": 0
15121542
},
1543+
"timeval": {
1544+
"__size__": 16,
1545+
"tv_sec": 0,
1546+
"tv_usec": 8
1547+
},
15131548
"tm": {
15141549
"__size__": 56,
15151550
"tm_gmtoff": 40,

src/library_syscall.js

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,21 +207,54 @@ var SyscallsLibrary = {
207207
#else
208208
var stream = SYSCALLS.getStreamFromFD(fd);
209209
switch (op) {
210-
case {{{ cDefs.TCGETA }}}:
210+
case {{{ cDefs.TCGETA }}}: {
211+
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
212+
#if SYSCALL_DEBUG
213+
dbg('warning: not filling tio struct');
214+
#endif
215+
return 0;
216+
}
211217
case {{{ cDefs.TCGETS }}}: {
212218
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
219+
if (stream.tty.ops.ioctl_tcgets) {
220+
var termios = stream.tty.ops.ioctl_tcgets(stream);
221+
var argp = SYSCALLS.get();
222+
{{{ makeSetValue('argp', C_STRUCTS.termios.c_iflag, 'termios.c_iflag || 0', 'i32') }}};
223+
{{{ makeSetValue('argp', C_STRUCTS.termios.c_oflag, 'termios.c_oflag || 0', 'i32') }}};
224+
{{{ makeSetValue('argp', C_STRUCTS.termios.c_cflag, 'termios.c_cflag || 0', 'i32') }}};
225+
{{{ makeSetValue('argp', C_STRUCTS.termios.c_lflag, 'termios.c_lflag || 0', 'i32') }}};
226+
for (var i = 0; i < {{{ cDefs.NCCS }}}; i++) {
227+
{{{ makeSetValue('argp + i', C_STRUCTS.termios.c_cc, 'termios.c_cc[i] || 0', 'i8') }}};
228+
}
229+
return 0;
230+
}
213231
#if SYSCALL_DEBUG
214232
dbg('warning: not filling tio struct');
215233
#endif
216234
return 0;
217235
}
218236
case {{{ cDefs.TCSETA }}}:
219237
case {{{ cDefs.TCSETAW }}}:
220-
case {{{ cDefs.TCSETAF }}}:
238+
case {{{ cDefs.TCSETAF }}}: {
239+
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
240+
return 0; // no-op, not actually adjusting terminal settings
241+
}
221242
case {{{ cDefs.TCSETS }}}:
222243
case {{{ cDefs.TCSETSW }}}:
223244
case {{{ cDefs.TCSETSF }}}: {
224245
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
246+
if (stream.tty.ops.ioctl_tcsets) {
247+
var argp = SYSCALLS.get();
248+
var c_iflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_iflag, 'i32') }}};
249+
var c_oflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_oflag, 'i32') }}};
250+
var c_cflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_cflag, 'i32') }}};
251+
var c_lflag = {{{ makeGetValue('argp', C_STRUCTS.termios.c_lflag, 'i32') }}};
252+
var c_cc = []
253+
for (var i = 0; i < {{{ cDefs.NCCS }}}; i++) {
254+
c_cc.push({{{ makeGetValue('argp + i', C_STRUCTS.termios.c_cc, 'i8') }}});
255+
}
256+
return stream.tty.ops.ioctl_tcsets(stream.tty, op, { c_iflag, c_oflag, c_cflag, c_lflag, c_cc });
257+
}
225258
return 0; // no-op, not actually adjusting terminal settings
226259
}
227260
case {{{ cDefs.TIOCGPGRP }}}: {
@@ -242,6 +275,12 @@ var SyscallsLibrary = {
242275
// TODO: in theory we should write to the winsize struct that gets
243276
// passed in, but for now musl doesn't read anything on it
244277
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
278+
if (stream.tty.ops.ioctl_tiocgwinsz) {
279+
var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty);
280+
var argp = SYSCALLS.get();
281+
{{{ makeSetValue('argp', 0, 'winsize[0]', 'i16') }}};
282+
{{{ makeSetValue('argp', 2, 'winsize[1]', 'i16') }}};
283+
}
245284
return 0;
246285
}
247286
case {{{ cDefs.TIOCSWINSZ }}}: {
@@ -251,6 +290,10 @@ var SyscallsLibrary = {
251290
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
252291
return 0;
253292
}
293+
case {{{ cDefs.TCFLSH }}}: {
294+
if (!stream.tty) return -{{{ cDefs.ENOTTY }}};
295+
return 0;
296+
}
254297
default: return -{{{ cDefs.EINVAL }}}; // not supported
255298
}
256299
#endif // SYSCALLS_REQUIRE_FILESYSTEM
@@ -542,7 +585,13 @@ var SyscallsLibrary = {
542585
var flags = SYSCALLS.DEFAULT_POLLMASK;
543586

544587
if (stream.stream_ops.poll) {
545-
flags = stream.stream_ops.poll(stream);
588+
var timeoutInMillis = -1;
589+
if (timeout) {
590+
var tv_sec = (readfds ? {{{ makeGetValue('timeout', C_STRUCTS.timeval.tv_sec, 'i32') }}} : 0),
591+
tv_usec = (readfds ? {{{ makeGetValue('timeout', C_STRUCTS.timeval.tv_usec, 'i32') }}} : 0);
592+
timeoutInMillis = (tv_sec + tv_usec / 1000000) * 1000;
593+
}
594+
flags = stream.stream_ops.poll(stream, timeoutInMillis);
546595
}
547596

548597
if ((flags & {{{ cDefs.POLLIN }}}) && check(fd, srcReadLow, srcReadHigh, mask)) {
@@ -593,7 +642,7 @@ var SyscallsLibrary = {
593642
if (stream) {
594643
mask = SYSCALLS.DEFAULT_POLLMASK;
595644
if (stream.stream_ops.poll) {
596-
mask = stream.stream_ops.poll(stream);
645+
mask = stream.stream_ops.poll(stream, -1);
597646
}
598647
}
599648
mask &= events | {{{ cDefs.POLLERR }}} | {{{ cDefs.POLLHUP }}};

src/library_tty.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,27 @@ mergeInto(LibraryManager.library, {
161161
out(UTF8ArrayToString(tty.output, 0));
162162
tty.output = [];
163163
}
164+
},
165+
ioctl_tcgets: function(tty) {
166+
// typical setting
167+
return {
168+
c_iflag: {{{ cDefs.ICRNL | cDefs.IXON | cDefs.IMAXBEL | cDefs.IUTF8 }}},
169+
c_oflag: {{{ cDefs.OPOST | cDefs.ONLCR }}},
170+
c_cflag: {{{ cDefs.B38400 | cDefs.CSIZE | cDefs.CREAD }}},
171+
c_lflag: {{{ cDefs.ISIG | cDefs.ICANON | cDefs.ECHO | cDefs.ECHOE | cDefs.ECHOK | cDefs.ECHOCTL | cDefs.ECHOKE | cDefs.IEXTEN }}},
172+
c_cc: [
173+
0x03, 0x1c, 0x7f, 0x15, 0x04, 0x00, 0x01, 0x00, 0x11, 0x13, 0x1a, 0x00,
174+
0x12, 0x0f, 0x17, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176+
]
177+
};
178+
},
179+
ioctl_tcsets: function(tty, optional_actions, data) {
180+
// currently just ignore
181+
return 0;
182+
},
183+
ioctl_tiocgwinsz: function(tty) {
184+
return [24, 80];
164185
}
165186
},
166187
default_tty1_ops: {

src/struct_info.json

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@
150150
"tm_gmtoff",
151151
"tm_zone"
152152
],
153+
"timeval": [
154+
"tv_sec",
155+
"tv_usec"
156+
],
153157
"timespec": [
154158
"tv_sec",
155159
"tv_nsec"
@@ -305,7 +309,8 @@
305309
"TIOCGPGRP",
306310
"TIOCSPGRP",
307311
"TIOCGWINSZ",
308-
"TIOCSWINSZ"
312+
"TIOCSWINSZ",
313+
"TCFLSH"
309314
]
310315
},
311316
{
@@ -479,6 +484,41 @@
479484
"RTLD_NODELETE"
480485
]
481486
},
487+
{
488+
"file": "termios.h",
489+
"defines": [
490+
"NCCS",
491+
"ICRNL",
492+
"IXON",
493+
"IMAXBEL",
494+
"IUTF8",
495+
"OPOST",
496+
"ONLCR",
497+
"B38400",
498+
"CSIZE",
499+
"CREAD",
500+
"ISIG",
501+
"ICANON",
502+
"ECHO",
503+
"ECHOE",
504+
"ECHOK",
505+
"ECHOCTL",
506+
"ECHOKE",
507+
"IEXTEN"
508+
],
509+
"structs": {
510+
"termios": [
511+
"c_iflag",
512+
"c_oflag",
513+
"c_cflag",
514+
"c_lflag",
515+
"c_line",
516+
"c_cc",
517+
"__c_ispeed",
518+
"__c_ospeed"
519+
]
520+
}
521+
},
482522
// ===========================================
483523
// SDL
484524
// ===========================================

0 commit comments

Comments
 (0)