Files
imhex/dist/web/source/wasm-config.js
Nik d62abaed8d build: Add support for x86 macOS 10.15 and ARM64 macOS 11.0 (#2607)
This PR drops the use of brew for dependency management in favor of
macports so we can support lower macOS versions instead of just the
lowest one supported by Apple

Closes #2586
2026-01-09 17:42:51 +01:00

291 lines
11 KiB
JavaScript

let wasmSize = null;
// See comment in dist/web/Dockerfile about imhex.wasm.size
fetch("imhex.wasm.size").then(async (resp) => {
wasmSize = parseInt((await resp.text()).trim());
console.log(`Real WASM binary size is ${wasmSize} bytes`);
});
// Monkeypatch WebAssembly to have a progress bar
// inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)
function monkeyPatch(progressFun) {
const _instantiateStreaming = WebAssembly.instantiateStreaming;
WebAssembly.instantiateStreaming = async (responsePromise, ...args) => {
// Do not collect wasm content length here see above
let response = await responsePromise
const file = response.url.substring(
new URL(response.url).origin.length + 1
);
const reportingResponse = new Response(
new ReadableStream(
{
async start(controller) {
const reader = response.clone().body.getReader();
let loaded = 0;
for (; ;) {
const { done, value } = await reader.read();
if (done) {
if(wasmSize) progressFun(file, wasmSize);
break;
}
loaded += value.byteLength;
progressFun(file, loaded);
controller.enqueue(value);
}
controller.close();
}
},
{
status: response.status,
statusText: response.statusText
}
)
);
for (const pair of response.headers.entries()) {
reportingResponse.headers.set(pair[0], pair[1]);
}
return _instantiateStreaming(reportingResponse, ...args);
}
}
monkeyPatch((file, done) => {
if (!wasmSize) return;
if (done > wasmSize) {
console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);
return;
}
const percent = ((done / wasmSize) * 100).toFixed(0);
const mibNow = (done / 1024**2).toFixed(1);
const mibTotal = (wasmSize / 1024**2).toFixed(1);
let root = document.querySelector(':root');
if (root != null) {
root.style.setProperty("--progress", `${percent}%`)
let progressBar = document.getElementById("progress-bar-content");
if (progressBar != null) {
progressBar.innerHTML = `${percent}%  [${mibNow} MiB / ${mibTotal} MiB]`;
}
}
});
function glfwSetCursorCustom(wnd, shape) {
let body = document.getElementsByTagName("body")[0]
switch (shape) {
case 0x00036001: // GLFW_ARROW_CURSOR
body.style.cursor = "default";
break;
case 0x00036002: // GLFW_IBEAM_CURSOR
body.style.cursor = "text";
break;
case 0x00036003: // GLFW_CROSSHAIR_CURSOR
body.style.cursor = "crosshair";
break;
case 0x00036004: // GLFW_HAND_CURSOR
body.style.cursor = "pointer";
break;
case 0x00036005: // GLFW_HRESIZE_CURSOR
body.style.cursor = "ew-resize";
break;
case 0x00036006: // GLFW_VRESIZE_CURSOR
body.style.cursor = "ns-resize";
break;
default:
body.style.cursor = "default";
break;
}
}
function glfwCreateStandardCursorCustom(shape) {
return shape
}
var notWorkingTimer = setTimeout(() => {
document.getElementById("not_working").classList.add("visible")
}, 5000);
var Module = {
preRun: () => {
ENV.IMHEX_SKIP_SPLASH_SCREEN = "1";
},
postRun: function() {
// Patch the emscripten GLFW module to send mouse and touch events in the right order
// For ImGui interactions to correctly work with touch input, MousePos events need
// to be processed first and then MouseButton events in the next frame. By default,
// GLFW does the exact opposite, which causes buttons to require two taps to register
// and windows get "stuck" to the cursor when dragged or resized
GLFW.onMousemove = event => {
if (event.type === "touchmove") {
event.preventDefault();
let primaryChanged = false;
for (let i of event.changedTouches) {
if (GLFW.primaryTouchId === i.identifier) {
Browser.setMouseCoords(i.pageX, i.pageY);
primaryChanged = true;
break;
}
}
if (!primaryChanged) {
return;
}
} else {
Browser.calculateMouseEvent(event);
}
};
GLFW.onMouseButtonChanged = (event, status) => {
if (!GLFW.active) return;
if (event.target != Module["canvas"]) return;
const isTouchType = event.type === "touchstart" || event.type === "touchend" || event.type === "touchcancel";
let eventButton = 0;
if (isTouchType) {
event.preventDefault();
let primaryChanged = false;
if (GLFW.primaryTouchId === null && event.type === "touchstart" && event.targetTouches.length > 0) {
const chosenTouch = event.targetTouches[0];
GLFW.primaryTouchId = chosenTouch.identifier;
Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY);
primaryChanged = true;
} else if (event.type === "touchend" || event.type === "touchcancel") {
for (let i of event.changedTouches) {
if (GLFW.primaryTouchId === i.identifier) {
GLFW.primaryTouchId = null;
primaryChanged = true;
break;
}
}
}
if (!primaryChanged) {
return;
}
} else {
Browser.calculateMouseEvent(event);
eventButton = GLFW.DOMToGLFWMouseButton(event);
}
if (status == 1) {
GLFW.active.buttons |= (1 << eventButton);
try {
event.target.setPointerCapture(event.pointerId);
} catch (e) {}
} else {
GLFW.active.buttons &= ~(1 << eventButton);
}
if (GLFW.active.cursorPosFunc) {
getWasmTableEntry(GLFW.active.cursorPosFunc)(GLFW.active.id, Browser.mouseX, Browser.mouseY);
}
if (GLFW.active.mouseButtonFunc) {
getWasmTableEntry(GLFW.active.mouseButtonFunc)(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active));
}
};
},
onRuntimeInitialized: function() {
// Triggered when the wasm module is loaded and ready to use.
let loading = document.getElementById("loading");
if (loading != null)
document.getElementById("loading").style.display = "none"
document.getElementById("canvas").style.display = "initial"
clearTimeout(notWorkingTimer);
},
print: (function() { })(),
printErr: function(text) { },
canvas: (function() {
const canvas = document.getElementById('canvas');
canvas.addEventListener("webglcontextlost", function(e) {
alert('WebGL context lost, please reload the page');
e.preventDefault();
}, false);
js_resizeCanvas()
// Turn long touches into right-clicks
let timer = null;
canvas.addEventListener('touchstart', event => {
timer = setTimeout(() => {
let eventArgs = {
bubbles: true,
cancelable: true,
view: window,
screenX: event.touches[0].screenX,
screenY: event.touches[0].screenY,
clientX: event.touches[0].clientX,
clientY: event.touches[0].clientY,
button: 2,
buttons: 2,
relatedTarget: event.target,
region: event.region
}
canvas.dispatchEvent(new MouseEvent('mousedown', eventArgs));
canvas.dispatchEvent(new MouseEvent('mouseup', eventArgs));
}, 400);
});
canvas.addEventListener('touchend', event => {
if (timer) {
clearTimeout(timer);
timer = null;
}
});
if (typeof WebGL2RenderingContext !== 'undefined') {
let gl = canvas.getContext('webgl2', { stencil: true });
if (!gl) {
console.error('WebGL 2 not available, falling back to WebGL');
gl = canvas.getContext('webgl', { stencil: true });
}
if (!gl) {
alert('WebGL not available with stencil buffer');
}
return canvas;
} else {
alert('WebGL 2 not supported by this browser');
}
})(),
setStatus: function(text) { },
totalDependencies: 0,
monitorRunDependencies: function(left) {
},
instantiateWasm: async function(imports, successCallback) {
imports.env.glfwSetCursor = glfwSetCursorCustom
imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
let result = await instantiateAsync(null, findWasmBinary(), imports);
successCallback(result.instance, result.module)
},
arguments: []
};
// Handle passing arguments to the wasm module
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has("lang")) {
Module["arguments"].push("--language");
Module["arguments"].push(urlParams.get("lang"));
} else if (urlParams.has("save-editor")) {
Module["arguments"].push("--save-editor");
Module["arguments"].push("gist");
Module["arguments"].push(urlParams.get("save-editor"));
}
window.addEventListener('resize', js_resizeCanvas, false);
function js_resizeCanvas() {
let canvas = document.getElementById('canvas');
canvas.top = canvas.parentElement.clientTop;
canvas.left = canvas.parentElement.clientLeft;
let width = Math.min(canvas.parentElement.clientWidth, window.innerWidth || 0);
let height = Math.min(canvas.parentElement.clientHeight, window.innerHeight || 0);
canvas.style.width = width + "px";
canvas.style.height = height + "px";
}
// Prevent some default browser shortcuts from preventing ImHex ones to work
document.addEventListener('keydown', e => {
if (e.ctrlKey) {
if (e.which == 83) e.preventDefault();
}
})