From 90bb5d187c352d037bc16fa43f41eb883ce86de0 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 3 Jul 2024 22:32:33 +0200 Subject: [PATCH] web: Fix touch input --- dist/web/source/wasm-config.js | 73 ++++++++++++++++++++++++++- main/gui/source/window/web_window.cpp | 25 +++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/dist/web/source/wasm-config.js b/dist/web/source/wasm-config.js index 66dc0931f..a301e847e 100644 --- a/dist/web/source/wasm-config.js +++ b/dist/web/source/wasm-config.js @@ -100,7 +100,77 @@ var notWorkingTimer = setTimeout(() => { var Module = { preRun: [], - postRun: [], + 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.setCapture(); + } 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. document.getElementById("loading").style.display = "none" @@ -157,7 +227,6 @@ window.addEventListener('resize', js_resizeCanvas, false); function js_resizeCanvas() { let canvas = document.getElementById('canvas'); - canvas.top = document.documentElement.clientTop; canvas.left = document.documentElement.clientLeft; canvas.width = Math.min(document.documentElement.clientWidth, window.innerWidth || 0); diff --git a/main/gui/source/window/web_window.cpp b/main/gui/source/window/web_window.cpp index ed16bc02b..9f137f812 100644 --- a/main/gui/source/window/web_window.cpp +++ b/main/gui/source/window/web_window.cpp @@ -7,6 +7,9 @@ #include +#include +#include + // Function used by c++ to get the size of the html canvas EM_JS(int, canvas_get_width, (), { return Module.canvas.width; @@ -37,6 +40,27 @@ extern "C" void handleThemeChange() { hex::EventOSThemeChanged::post(); } + +EM_JS(void, setupInputModeListener, (), { + Module.canvas.addEventListener('mousedown', function() { + Module._enterMouseMode(); + }); + + Module.canvas.addEventListener('touchstart', function() { + Module._enterTouchMode(); + }); +}); + +EMSCRIPTEN_KEEPALIVE +extern "C" void enterMouseMode() { + ImGui::GetIO().AddMouseSourceEvent(ImGuiMouseSource_Mouse); +} + +EMSCRIPTEN_KEEPALIVE +extern "C" void enterTouchMode() { + ImGui::GetIO().AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); +} + namespace hex { void nativeErrorMessage(const std::string &message) { @@ -70,6 +94,7 @@ namespace hex { void Window::setupNativeWindow() { resizeCanvas(); setupThemeListener(); + setupInputModeListener(); bool themeFollowSystem = ImHexApi::System::usesSystemThemeDetection(); EventOSThemeChanged::subscribe(this, [themeFollowSystem] {