Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[emscripten + glfw]: Added support for GLFW3 contrib port #7647

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 49 additions & 16 deletions backends/imgui_impl_glfw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -101,6 +103,11 @@
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3
#include <GLFW/emscripten_glfw3.h>
#else
#define EMSCRIPTEN_USE_EMBEDDED_GLFW3
#endif
#endif

// We gather version tests as define in order to easily see which features are version-dependent.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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++)
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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<GLFWwindow *>(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<GLFWwindow *>(EM_ASM_INT({ return Module.glfwGetWindow(UTF8ToString($0)); }, canvas_selector));
IM_ASSERT(window == w);
emscripten_glfw_make_canvas_resizable(window, "window", nullptr);
}
#endif

Expand Down
2 changes: 2 additions & 0 deletions backends/imgui_impl_glfw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/example_glfw_opengl3/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
15 changes: 14 additions & 1 deletion examples/example_glfw_wgpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion examples/example_glfw_wgpu/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down