diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 53139f227fc0..65cf2a27863b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -21,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. +// 2024-06-10: Replaced ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback (now deprecated) with ImGui_ImplGlfw_InstallEmscriptenCallbacks +// 2024-06-02: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) // 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys. // 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609) @@ -101,6 +103,11 @@ #ifdef __EMSCRIPTEN__ #include #include +#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 +#include +#else +#define EMSCRIPTEN_USE_EMBEDDED_GLFW3 +#endif #endif // We gather version tests as define in order to easily see which features are version-dependent. @@ -132,7 +139,7 @@ struct ImGui_ImplGlfw_Data ImVec2 LastValidMousePos; bool InstalledCallbacks; bool CallbacksChainForAllWindows; -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif @@ -336,7 +343,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackScroll(window, xoffset, yoffset); -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 // Ignore GLFW events: will be processed in ImGui_ImplEmscripten_WheelCallback(). return; #endif @@ -347,7 +354,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { -#if GLFW_HAS_GETKEYNAME && !defined(__EMSCRIPTEN__) +#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // See https://github.com/glfw/glfw/issues/1502 for details. @@ -358,7 +365,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); const char* key_name = glfwGetKeyName(key, scancode); glfwSetErrorCallback(prev_error_callback); -#if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) +#if GLFW_HAS_GETERROR && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) // Eat errors (see #5908) (void)glfwGetError(nullptr); #endif if (key_name && key_name[0] != 0 && key_name[1] == 0) @@ -455,7 +462,7 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. } -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) { // Mimic Emscripten_HandleWheel() in SDL. @@ -611,12 +618,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); - // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) - // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. - // FIXME: May break chaining in case user registered their own Emscripten callback? -#ifdef __EMSCRIPTEN__ - emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, ImGui_ImplEmscripten_WheelCallback); -#endif // Set platform dependent data in viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); @@ -663,8 +664,9 @@ void ImGui_ImplGlfw_Shutdown() if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); -#ifdef __EMSCRIPTEN__ - emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr); +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 + if(bd->CanvasSelector) + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, nullptr); #endif for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -691,7 +693,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) { GLFWwindow* window = bd->Window; -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const bool is_window_focused = true; #else const bool is_window_focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; @@ -749,7 +751,7 @@ static void ImGui_ImplGlfw_UpdateGamepads() return; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; -#if GLFW_HAS_GAMEPAD_API && !defined(__EMSCRIPTEN__) +#if GLFW_HAS_GAMEPAD_API && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; @@ -823,7 +825,7 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateGamepads(); } -#ifdef __EMSCRIPTEN__ +#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) { ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; @@ -842,9 +844,18 @@ static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, con return true; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. // STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) +{ + ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); +} +#endif + +// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query. +// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID. +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* /* window */, const char* canvas_selector) { IM_ASSERT(canvas_selector != nullptr); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); @@ -856,6 +867,28 @@ void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_sel // Change the size of the GLFW window according to the size of the canvas ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd); + + // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) + // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. + // FIXME: May break chaining in case user registered their own Emscripten callback? + emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); +} +#else +void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) +{ + auto window = reinterpret_cast(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, canvas_selector); +} + +// When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call +// by invoking emscripten_glfw_make_canvas_resizable afterward. +// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Usage.md#how-to-make-the-canvas-resizable-by-the-user for an explanation +void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector) +{ + // sanity check... + auto w = reinterpret_cast(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector)); + IM_ASSERT(window == w); + emscripten_glfw_make_canvas_resizable(window, "window", nullptr); } #endif diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 6a9acd05b27d..a065cd6d0a0e 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -32,7 +32,9 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); // Emscripten related initialization phase methods #ifdef __EMSCRIPTEN__ +[[deprecated("Use ImGui_ImplGlfw_InstallEmscriptenCallbacks instead")]] IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector); +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); #endif // GLFW callbacks install diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 4438fa5bb6c7..22ef79eb7f3d 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -85,7 +85,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); #endif ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index e682836ddb58..77b492fdc278 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -27,6 +27,12 @@ set(IMGUI_DIR ../../) # Libraries if(EMSCRIPTEN) + if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57") + set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)") + else() + # cannot use contrib.glfw3 prior to 3.1.57 + set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE) + endif() set(LIBRARIES glfw) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) else() @@ -82,9 +88,16 @@ target_link_libraries(example_glfw_wgpu PUBLIC ${LIBRARIES}) # Emscripten settings if(EMSCRIPTEN) + if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") + target_compile_options(example_glfw_wgpu PUBLIC + "${IMGUI_EMSCRIPTEN_GLFW3}" + "-DEMSCRIPTEN_USE_PORT_CONTRIB_GLFW3" # unnecessary beyond emscripten 3.1.59 + ) + endif() + message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") target_link_options(example_glfw_wgpu PRIVATE "-sUSE_WEBGPU=1" - "-sUSE_GLFW=3" + "${IMGUI_EMSCRIPTEN_GLFW3}" "-sWASM=1" "-sALLOW_MEMORY_GROWTH=1" "-sNO_EXIT_RUNTIME=0" diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 4e47b8323bd6..c5872bd85c7f 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -101,7 +101,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); #ifdef __EMSCRIPTEN__ - ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas"); + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); #endif ImGui_ImplWGPU_InitInfo init_info; init_info.Device = wgpu_device;