Last active
June 28, 2022 16:36
-
-
Save jcubic/87f2b4c5ef567be43796e179ca08c0da to your computer and use it in GitHub Desktop.
POC for Emscripten Async code + jQuery Terminal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
emcc -o main.html -s FETCH=1 -s NO_EXIT_RUNTIME=0 main.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <emscripten/fetch.h> | |
int i = 0; | |
void read_async(); | |
void read_success(emscripten_fetch_t *fetch) { | |
printf("You typed: %s\n", fetch->data); | |
emscripten_fetch_close(fetch); | |
if (strlen(fetch->data) > 0 || i++ == 5) { | |
read_async(); | |
} | |
} | |
void read_fail(emscripten_fetch_t *fetch) { | |
emscripten_fetch_close(fetch); | |
} | |
void read_async() { | |
emscripten_fetch_attr_t attr; | |
emscripten_fetch_attr_init(&attr); | |
strcpy(attr.requestMethod, "GET"); | |
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; | |
attr.onsuccess = read_success; | |
attr.onerror = read_fail; | |
emscripten_fetch(&attr, "___terminal::read"); | |
} | |
int main() { | |
read_async(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> | |
<script src="https://cdn.rawgit.com/jcubic/jquery.terminal/master/js/jquery.terminal.min.js"></script> | |
<link href="https://cdn.rawgit.com/jcubic/jquery.terminal/master/css/jquery.terminal.min.css" rel="stylesheet"/> | |
<script src="https://unpkg.com/xhr-mock/dist/xhr-mock.js"></script> | |
</head> | |
<body> | |
</body> | |
<script> | |
var term = $('body').terminal(); | |
var re = /^___terminal::/; | |
// XHR proxy that handle methods from fetch in C | |
window.XMLHttpRequest = (function(xhr) { | |
return function() { | |
var url; | |
var props = { | |
readyState: 4, | |
status: 200 | |
}; | |
var enc = new TextEncoder("utf-8"); | |
return new Proxy(new xhr(), { | |
get: function(target, name) { | |
if (url && ['response', 'responseText', 'status', 'readyState'].indexOf(name) != -1) { | |
if (name == 'response') { | |
var response = enc.encode(props.responseText); | |
console.log(response); | |
return response; | |
} | |
return props[name]; | |
} else if (name == 'open') { | |
return function(method, open_url) { | |
if (open_url.match(re)) { | |
url = open_url; | |
} else { | |
return target[name].apply(target, arguments); | |
} | |
}; | |
} else if (name == 'send') { | |
return function(data) { | |
if (url) { | |
var payload = url.split('::'); | |
if (payload[1] == 'read') { | |
term.read( | |
payload.length > 2 ? payload[2] : '', | |
function(text) { | |
props.responseText = text; | |
target.onload(); | |
} | |
); | |
} | |
} else { | |
return target[name].apply(target, arguments); | |
} | |
}; | |
} | |
return target[name]; | |
}, | |
set: function(target, name, value) { | |
target[name] = value; | |
} | |
}); | |
}; | |
})(window.XMLHttpRequest); | |
// below code modified from emscripten output html | |
var Module = { | |
preRun: [], | |
postRun: [], | |
print: function(text) { | |
console.log(text); | |
term.echo(text); | |
}, | |
printErr: function(text) { | |
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); | |
term.error(text); | |
}, | |
canvas: (function() { | |
var canvas = document.createElement('canvas'); | |
// As a default initial behavior, pop up an alert when webgl context is lost. To make your | |
// application robust, you may want to override this behavior before shipping! | |
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 | |
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false); | |
return canvas; | |
})(), | |
setStatus: function(text) { | |
}, | |
totalDependencies: 0, | |
monitorRunDependencies: function(left) { | |
this.totalDependencies = Math.max(this.totalDependencies, left); | |
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); | |
} | |
}; | |
Module.setStatus('Downloading...'); | |
window.onerror = function(event) { | |
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus | |
Module.setStatus('Exception thrown, see JavaScript console'); | |
spinnerElement.style.display = 'none'; | |
Module.setStatus = function(text) { | |
if (text) Module.printErr('[post-exception status] ' + text); | |
}; | |
}; | |
</script> | |
<script async type="text/javascript" src="test.js"></script> | |
</html> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is hack over Emscripten
fetch
function that use proxy over XHR (Emscripten don't give any way for async JS code see my issue from 2018 Async comunication with javascript). This is done so you can have C code that call read and it's calling read on terminal not using browser prompt. The whole code can be abstracted away for the user and pack into nice little lib (e.g. in functionterm_read
). Same as with fetchprintf
also can use terminal so you have real terminal for Emscripten apps (you can useemscripten_fetch(&attr, "___terminal::print");
it can be wrapped in niceterm_printf
function).This code was solution to this StackOverflow Question How can I run an interactive program compiled with Emscripten in a web page?
The code like content of StackOverflow is released with CC-BY-SA license.