13

I've got a simple program, say the following:

#include <stdio.h>

int main()
{
    char buf[100];
    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        printf("You typed: %s", buf);
    }
}

and I have compiled it using Emscripten:

emcc -o hello.html hello.cpp

This gives me a pretty basic Emscripten-generated web page that contains a simple window for program output. However, the fgets() call causes a browser popup window, presumably from prompt(). I can type things, and the results eventually get shown in the output window. This is not an ideal interactive experience.

What I would like is a more conventional "console" interface, where the user can type interactively in the terminal window to supply input to the interactive program.

I suspect the solution may lie in something like JQueryTerminal, Hyper, or Xterm.js, but I am so far unclear on how to actually connect any of those to an Emscripten-compiled program.

How can I provide a "console" interface to my Emscripten code?

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • I'm not sure but I think that emscripted is adding it's own handler for stdout/stdin using prompt and console.log functions because the functions need to be asynchronous, check this issue in emscripten repo https://github.com/kripken/emscripten/issues/2272 for this to work you probably will need to modify the emscripten to output callbacks or promises for code like this, and output of midified version could use jQuery Terminal. As for XTerm. it was created as front for backed tty programs that send ANSI escapes code and front-end is only for sending keystrokes to server. – jcubic Jan 17 '18 at 18:29
  • @jcubic: Thanks, those issues you linked to are helpful as it shows other people have asked about this before! It looks like it may be possible to use [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to run the emscripten-compiled code in the background, using a terminal in the foreground, and pass messages for stdin/stdout. Just found that the [Emscripten API has Web Worker support](https://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html#worker-api), so this has certainly been explored. – Greg Hewgill Jan 17 '18 at 19:47
  • 1
    The problem is that the c functions are blocking, I've look at the output code and get_char function use `readSync` in node and `window.prompt` when run as website, so the worker will not solve the problem. You could send fgets to worker but still fgets need to block the execution of the script. In order to have terminal like look and feel in browser you'll need to have async fgets function in emscripted itself. Maybe try ask on github in emscripten repo maybe they have solution for async code. – jcubic Jan 17 '18 at 20:38

1 Answers1

6

It's not exactly what you wanted, I think that is not possible, but here is Prof of Concept of Async code, it abuse fetch API in C and use Proxy in JavaScript for XHR that handle messages from C. You can extend it to have any async code that need to be done in JS. The example use jQuery Terminal and it behave exactly as your example C code.

https://gist.github.com/jcubic/87f2b4c5ef567be43796e179ca08c0da

I've also created an issue about async code in emscripten repo

EDIT: Someone was able to compile R programming language to WebAssemly with this approach: https://github.com/georgestagg/webR

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Thank you, that's excellent! I should be able (in my real code) to refactor the main loop into an async form like this for emscripten build. – Greg Hewgill Jan 18 '18 at 17:37
  • 1
    I was able to refactor my code so that it works in the same way as your prototype. I'm going to leave this question open for a while to see if anybody has any other ideas. – Greg Hewgill Jan 21 '18 at 00:55
  • it looks like a good alternative way to do this, so that no C changes need be made, might be to copy src/library_tty.js out of the emscripten source, rename it and change the put_char and get_char functions, and load it with `emcc --js-library` to replace the TTY library. – fuzzyTew Mar 18 '20 at 13:02
  • I started that at https://github.com/xloem/emterm . It's easiest to only handle nonblocking input. – fuzzyTew Mar 18 '20 at 17:07
  • @fuzzyTew you have typo in title `emtern`. Documentation would be nice, I don't understand what I should do with this repo. – jcubic Mar 19 '20 at 14:17
  • @fuzzyTew monkey patch is not good solution, you will need to maintain all the changes when they are done in oriignal library and update your code. – jcubic Mar 19 '20 at 14:26
  • @jcubic very sorry, not sure if I will be able to finish this repo so I shared with low-answer post so people would have resources. if you can make example run, you can see basics of idea. I am hoping to submit PR to emscripten if I ever finish it. It is designed to have files that would work in a PR. – fuzzyTew Mar 19 '20 at 15:29
  • @fuzzyTew it would be awesome to see this in emscripten (if it works as I assume), you should add comment this issue: https://github.com/emscripten-core/emscripten/issues/6115 that it's possible to make it working with xterm.js you would be able to compile any interactive app, with little bit more work maybe even compile vi/vim or other program like this. You should at least create online demo of the app with this, maybe github pages so anyone can see for himself that this actually works. – jcubic Mar 20 '20 at 08:06
  • @jcubic based on your gist i pushed an example in this repo https://github.com/kalwalt/Emscripten-sketches/tree/main/fetch_example – kalwalt Apr 28 '22 at 18:56
  • @kalwalt you should work on the README put links to directories and add a description of what each example does. Another thing is that no one will be able to use your code if there is no Open Source LICENSE I suggest CC0 Public Domain. – jcubic May 01 '22 at 12:35