diff --git a/dist/web/source/index.html b/dist/web/source/index.html
index 56096005d..e280efa93 100644
--- a/dist/web/source/index.html
+++ b/dist/web/source/index.html
@@ -96,7 +96,9 @@
-
+
+
+
diff --git a/dist/web/source/wasm-config.js b/dist/web/source/wasm-config.js
index 1fdffa5e5..24766d951 100644
--- a/dist/web/source/wasm-config.js
+++ b/dist/web/source/wasm-config.js
@@ -110,75 +110,7 @@ var Module = {
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.
@@ -198,8 +130,6 @@ var Module = {
e.preventDefault();
}, false);
- js_resizeCanvas()
-
// Turn long touches into right-clicks
let timer = null;
canvas.addEventListener('touchstart', event => {
@@ -269,18 +199,6 @@ if (urlParams.has("lang")) {
Module["arguments"].push(urlParams.get("save-editor"));
}
-function js_resizeCanvas() {
- let canvas = document.getElementById('canvas');
-
- canvas.top = canvas.parentElement.clientTop;
- canvas.left = canvas.parentElement.clientLeft;
-
- canvas.style.width = "100%";
- canvas.style.height = "100%";
-}
-let resizeObserver = new ResizeObserver(js_resizeCanvas);
-resizeObserver.observe(document.getElementById("canvas"))
-
// Prevent some default browser shortcuts from preventing ImHex ones to work
document.addEventListener('keydown', e => {
if (e.ctrlKey) {
diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp
index 179b04cba..16f068bfd 100644
--- a/lib/libimhex/source/api/imhex_api.cpp
+++ b/lib/libimhex/source/api/imhex_api.cpp
@@ -680,23 +680,15 @@ namespace hex {
if (!sessionType.has_value() || sessionType == "x11")
return 1.0F;
else {
- static float scaleFactor = -1;
- if (scaleFactor <= 0) {
- int windowW, windowH;
- int displayW, displayH;
- glfwGetWindowSize(getMainWindowHandle(), &windowW, &windowH);
- glfwGetFramebufferSize(getMainWindowHandle(), &displayW, &displayH);
+ int windowW, windowH;
+ int displayW, displayH;
+ glfwGetWindowSize(getMainWindowHandle(), &windowW, &windowH);
+ glfwGetFramebufferSize(getMainWindowHandle(), &displayW, &displayH);
- float xScale = (windowW > 0) ? float(displayW) / windowW : 1.0f;
- float yScale = (windowH > 0) ? float(displayH) / windowH : 1.0f;
-
- scaleFactor = std::midpoint(xScale, yScale);
- }
-
- return scaleFactor;
+ return (windowW > 0) ? float(displayW) / windowW : 1.0f;
}
#elif defined(OS_WEB)
- return MAIN_THREAD_EM_ASM_INT({ return window.devicePixelRatio; });
+ return emscripten_get_device_pixel_ratio();
#else
return 1.0F;
#endif
diff --git a/lib/third_party/imgui/backend/CMakeLists.txt b/lib/third_party/imgui/backend/CMakeLists.txt
index c5f54167e..11c603c7d 100644
--- a/lib/third_party/imgui/backend/CMakeLists.txt
+++ b/lib/third_party/imgui/backend/CMakeLists.txt
@@ -25,6 +25,10 @@ if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
target_link_libraries(imgui_backend PUBLIC ${X11_LIBRARIES})
endif()
+ if (EMSCRIPTEN)
+ target_compile_options(imgui_backend PRIVATE --use-port=contrib.glfw3)
+ endif()
+
find_package(GLFW QUIET)
if (NOT GLFW_FOUND OR "${GLFW_LIBRARIES}" STREQUAL "")
find_package(glfw3 QUIET)
diff --git a/main/gui/CMakeLists.txt b/main/gui/CMakeLists.txt
index c53b77ef5..db15315d5 100644
--- a/main/gui/CMakeLists.txt
+++ b/main/gui/CMakeLists.txt
@@ -38,7 +38,8 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/external/libromfs ${CMAKE
add_dependencies(imhex_all main)
if (EMSCRIPTEN)
- target_link_options(main PRIVATE -sUSE_GLFW=3 -sUSE_PTHREADS=1 -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth)
+ target_compile_options(main PRIVATE --use-port=contrib.glfw3)
+ target_link_options(main PRIVATE --use-port=contrib.glfw3 -sUSE_PTHREADS=1 -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth)
target_link_options(main PRIVATE -sTOTAL_MEMORY=134217728)
target_link_options(main PRIVATE -sMAX_WEBGL_VERSION=2)
target_link_options(main PRIVATE -sGL_UNSAFE_OPTS=0)
diff --git a/main/gui/source/init/splash_window.cpp b/main/gui/source/init/splash_window.cpp
index 73681e405..c81a2566f 100644
--- a/main/gui/source/init/splash_window.cpp
+++ b/main/gui/source/init/splash_window.cpp
@@ -67,6 +67,17 @@ namespace hex::init {
log::debug("OpenGL Renderer: '{}'", glRendererString);
log::debug("OpenGL Version String: '{}'", glVersionString);
log::debug("OpenGL Shading Language Version: '{}'", glShadingLanguageVersion);
+ log::debug("GLFW Backend: '{}'", [] {
+ switch (glfwGetPlatform()) {
+ case GLFW_PLATFORM_WIN32: return "Win32";
+ case GLFW_PLATFORM_COCOA: return "Cocoa";
+ case GLFW_PLATFORM_X11: return "X11";
+ case GLFW_PLATFORM_WAYLAND: return "Wayland";
+ case GLFW_PLATFORM_NULL: return "null";
+ case GLFW_PLATFORM_UNAVAILABLE: return "Unavailable";
+ default: return "Unknown";
+ }
+ }());
ImHexApi::System::impl::setGPUVendor(glVendorString);
ImHexApi::System::impl::setGLRenderer(glRendererString);
@@ -462,6 +473,8 @@ namespace hex::init {
log::error("GLFW Error [{:05X}] : {}", errorCode, desc);
});
+ glfwDefaultWindowHints();
+
#if defined(OS_LINUX)
#if defined(GLFW_WAYLAND_APP_ID)
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "imhex");
diff --git a/main/gui/source/window/platform/linux.cpp b/main/gui/source/window/platform/linux.cpp
index d18ddf3e9..c5001bf0c 100644
--- a/main/gui/source/window/platform/linux.cpp
+++ b/main/gui/source/window/platform/linux.cpp
@@ -112,7 +112,7 @@ namespace hex {
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
diff --git a/main/gui/source/window/platform/web.cpp b/main/gui/source/window/platform/web.cpp
index 03ae02f17..07c6ec7f4 100644
--- a/main/gui/source/window/platform/web.cpp
+++ b/main/gui/source/window/platform/web.cpp
@@ -1,9 +1,13 @@
+#include
+
#include "window.hpp"
+#include "hex/api/imhex_api/system.hpp"
#if defined(OS_WEB)
#include
#include
+#include
#include
#include
@@ -16,20 +20,6 @@
#include
#include
-// Function used by c++ to get the size of the html canvas
-EM_JS(int, canvas_get_width, (), {
- return Module.canvas.width;
-});
-
-// Function used by c++ to get the size of the html canvas
-EM_JS(int, canvas_get_height, (), {
- return Module.canvas.height;
-});
-
-// Function called by javascript
-EM_JS(void, resizeCanvas, (), {
- js_resizeCanvas();
-});
EM_JS(bool, isMacOS, (), {
return navigator.userAgent.indexOf('Mac OS X') != -1
@@ -54,6 +44,11 @@ extern "C" void handleThemeChange() {
hex::EventOSThemeChanged::post();
}
+EMSCRIPTEN_KEEPALIVE
+extern "C" void updateFramebufferSize(int width, int height) {
+ glfwSetWindowSize(hex::ImHexApi::System::getMainWindowHandle(), width, height);
+}
+
EM_JS(void, setupInputModeListener, (), {
Module.canvas.addEventListener('mousedown', function() {
@@ -79,9 +74,12 @@ namespace hex {
void Window::configureGLFW() {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_DECORATED, GL_FALSE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);
+ glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
+
+ emscripten::glfw3::SetNextWindowCanvasSelector("#canvas");
}
void Window::initNative() {
@@ -101,8 +99,23 @@ namespace hex {
});
}
+ static float calculateNativeScale(GLFWwindow *window) {
+ int windowW, windowH;
+ int displayW, displayH;
+ glfwGetWindowSize(window, &windowW, &windowH);
+ glfwGetFramebufferSize(window, &displayW, &displayH);
+
+ const auto xScale = (windowW > 0) ? float(displayW) / windowW : 1.0f;
+ const auto yScale = (windowH > 0) ? float(displayH) / windowH : 1.0f;
+
+ auto scaleFactor = std::midpoint(xScale, yScale);
+ if (scaleFactor <= 0.0F)
+ scaleFactor = 1.0F;
+
+ return scaleFactor;
+ }
+
void Window::setupNativeWindow() {
- resizeCanvas();
setupThemeListener();
setupInputModeListener();
fixCanvasInPlace();
@@ -123,15 +136,20 @@ namespace hex {
glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) {
auto win = static_cast(glfwGetWindowUserPointer(window));
- resizeCanvas();
win->fullFrame();
});
if (themeFollowSystem)
EventOSThemeChanged::post();
- if (isMacOS())
+ if (emscripten::glfw3::IsRuntimePlatformApple())
ShortcutManager::enableMacOSMode();
+
+ glfwSetWindowAttrib(m_window, GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
+ glfwShowWindow(m_window);
+ emscripten::glfw3::MakeCanvasResizable(m_window, "#canvas-wrapper");
+ ImHexApi::System::impl::setNativeScale(calculateNativeScale(m_window));
+ EventDPIChanged::post(1.0, ImHexApi::System::getBackingScaleFactor());
}
void Window::beginNativeWindowFrame() {
@@ -140,40 +158,18 @@ namespace hex {
void Window::endNativeWindowFrame() {
static float prevScaleFactor = 0;
-
- const float currScaleFactor = MAIN_THREAD_EM_ASM_DOUBLE({
- try {
- return window.devicePixelRatio;
- } catch (e) {
- return 1.0;
- }
- });
+ const float currScaleFactor = ImHexApi::System::getBackingScaleFactor();
if (prevScaleFactor != 0 && prevScaleFactor != currScaleFactor) {
EventDPIChanged::post(prevScaleFactor, currScaleFactor);
- resizeCanvas();
- ImHexApi::System::impl::setNativeScale(currScaleFactor);
+ ImHexApi::System::impl::setNativeScale(calculateNativeScale(m_window));
ThemeManager::reapplyCurrentTheme();
}
-
- static i32 prevWidth = 0;
- static i32 prevHeight = 0;
-
- auto width = canvas_get_width();
- auto height = canvas_get_height();
-
- if (prevWidth != width || prevHeight != height) {
- // Size has changed
-
- prevWidth = width;
- prevHeight = height;
- this->resize(width, height);
- resizeCanvas();
- }
-
prevScaleFactor = currScaleFactor;
+
+ glfwSetWindowSize(m_window, EM_ASM_INT({ return document.getElementById("canvas-wrapper").clientWidth; }), EM_ASM_INT({ return document.getElementById("canvas-wrapper").clientHeight; }));
}
}
diff --git a/main/gui/source/window/platform/windows.cpp b/main/gui/source/window/platform/windows.cpp
index ab0dbf10a..0473b4a5d 100644
--- a/main/gui/source/window/platform/windows.cpp
+++ b/main/gui/source/window/platform/windows.cpp
@@ -401,7 +401,7 @@ namespace hex {
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
} else {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
}
glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE);
diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp
index da74b51c8..0d3e9655e 100644
--- a/main/gui/source/window/window.cpp
+++ b/main/gui/source/window/window.cpp
@@ -1019,8 +1019,14 @@ namespace hex {
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
- glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+
+ // Don't hide the window on the web build, otherwise the mouse cursor offset will not
+ // be calculated correctly if the canvas is not filling the entire screen
+ #if !defined(OS_WEB)
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+ #endif
+
configureGLFW();
if (initialWindowProperties.has_value()) {