Add webapp

This allows running di-edid-decode from a web browser.

The C code is compiled to Web Assembly via Emscripten. The HTML shell
is based on [1].

[1]: https://github.com/emscripten-core/emscripten/blob/main/src/shell_minimal.html

Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Simon Ser 2022-08-17 13:13:13 +02:00
parent ebee35935d
commit 63152283ee
6 changed files with 143 additions and 0 deletions

View file

@ -9,3 +9,11 @@ di_edid_decode = executable(
dependencies: [di_dep, math], dependencies: [di_dep, math],
install: true, install: true,
) )
if target_machine.system() == 'emscripten'
fs = import('fs')
foreach filename : ['index.html', 'main.js', 'style.css']
fs.copyfile('web' / filename)
endforeach
endif

View file

@ -0,0 +1,11 @@
# Webapp
To build `di-edid-decode` as a webapp, install Emscripten and run:
meson setup build-wasm/ --cross-file di-edid-decode/web/wasm.txt
ninja -C build-wasm/
Then start a web server serving static files in `build-wasm/di-edid-decode/`,
for instance:
python -m http.server --directory build-wasm/di-edid-decode/

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>libdisplay-info</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>libdisplay-info</h1>
<p>
This webapp parses EDID files via
<a href="https://gitlab.freedesktop.org/emersion/libdisplay-info">libdisplay-info</a>.
</p>
<noscript>
<p>JavaScript is required.</p>
</noscript>
<p id="status">Loading...</p>
<div id="app">
<p>Select an EDID file:</p>
<input type="file" id="file-input">
<pre id="output"></pre>
</div>
<script src="main.js"></script>
<script src="di-edid-decode.js" async></script>
</body>
</html>

View file

@ -0,0 +1,70 @@
let appElt = document.getElementById('app');
let statusElt = document.getElementById('status');
let fileInput = document.getElementById('file-input');
let outputElt = document.getElementById('output');
function handleFileChange() {
if (fileInput.files.length === 0) {
return;
}
let file = fileInput.files[0];
let reader = new FileReader();
reader.addEventListener('load', () => {
let filename = 'edid';
FS.writeFile(filename, new Uint8Array(reader.result));
outputElt.innerHTML = '';
let argv = ['di-edid-decode', filename];
let ptrSize = 4;
let cArgv = Module._malloc(argv.length * ptrSize);
for (let i = 0; i < argv.length; i++) {
let str = argv[i];
let cStr = Module._malloc(str.length + 1);
Module.stringToUTF8(str, cStr, str.length + 1);
Module.setValue(cArgv + i * ptrSize, cStr, '*');
}
let ret = Module._main(argv.length, cArgv);
console.log('di-edid-decode exited with code ' + ret);
for (let i = 0; i < argv.length; i++) {
let cStr = Module.getValue(cArgv + i * ptrSize, '*');
Module._free(cStr);
}
Module._free(cArgv);
});
reader.readAsArrayBuffer(file);
}
fileInput.addEventListener('change', handleFileChange);
window.Module = {
noInitialRun: true,
setStatus: (text) => {
console.log(text || 'Done');
statusElt.innerText = text;
statusElt.style.display = text ? 'block' : 'none';
},
print: (text) => {
outputElt.appendChild(document.createTextNode(text + '\n'));
},
printErr: (text) => {
let elt = document.createElement('span');
elt.className = 'error';
elt.innerText = text + '\n';
outputElt.appendChild(elt);
},
onRuntimeInitialized: () => {
app.style.display = 'block';
handleFileChange();
},
onAbort: (text) => {
Module.setStatus('Abort: ' + text);
},
};
window.addEventListener('error', (event) => {
Module.setStatus('Error: ' + event.message);
});

View file

@ -0,0 +1,7 @@
#app {
display: none;
}
.error {
color: red;
}

View file

@ -0,0 +1,15 @@
[binaries]
c = '/usr/lib/emscripten/emcc'
cpp = '/usr/lib/emscripten/em++'
ar = '/usr/lib/emscripten/emar'
strip = '/usr/lib/emscripten/emstrip'
[built-in options]
default_library = 'static'
c_link_args = ['-sEXPORTED_FUNCTIONS=_main,_malloc,_free', '-sEXPORTED_RUNTIME_METHODS=getValue,setValue,stringToUTF8']
[host_machine]
system = 'emscripten'
cpu_family = 'wasm32'
cpu = 'wasm32'
endian = 'little'