From debd37b0bad80211fb685c0b2f04eb24cf3c1f19 Mon Sep 17 00:00:00 2001 From: Dario Mylonopoulos Date: Tue, 9 Dec 2025 18:13:59 +0100 Subject: [PATCH] Backends: GLFW: load X11 functions dynamically to remove requirement to link with x11 --- backends/imgui_impl_glfw.cpp | 82 +++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index c9dbafa14..30a39f228 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -119,10 +119,15 @@ #endif // GLFW -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) -#define GLFW_HAS_X11_OR_WAYLAND 1 +#if !defined(IMGUI_IMPL_GLFW_DISABLE_X11) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_X11 1 #else -#define GLFW_HAS_X11_OR_WAYLAND 0 +#define GLFW_HAS_X11 0 +#endif +#if !defined(IMGUI_IMPL_GLFW_DISABLE_WAYLAND) && (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)) +#define GLFW_HAS_WAYLAND 1 +#else +#define GLFW_HAS_WAYLAND 0 #endif #include #ifdef _WIN32 @@ -136,10 +141,11 @@ #define GLFW_EXPOSE_NATIVE_COCOA #endif #include -#elif GLFW_HAS_X11_OR_WAYLAND +#elif GLFW_HAS_X11 #ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) #define GLFW_EXPOSE_NATIVE_X11 #include +#include #endif #include #undef Status // X11 headers are leaking this. @@ -208,6 +214,13 @@ enum GlfwClientApi GlfwClientApi_Unknown, // Anything else fits here. }; +#if GLFW_HAS_X11 +typedef Atom (* PFN_XInternAtom)(Display*,const char*,Bool); +typedef int (* PFN_XChangeProperty)(Display*,Window,Atom,Atom,int,int,const unsigned char*,int); +typedef int (* PFN_XChangeWindowAttributes)(Display*,Window,unsigned long,XSetWindowAttributes*); +typedef int (* PFN_XFlush)(Display*); +#endif + // GLFW data struct ImGui_ImplGlfw_Data { @@ -244,6 +257,15 @@ struct ImGui_ImplGlfw_Data WNDPROC PrevWndProc; #endif +#if GLFW_HAS_X11 + // Module and function pointers loaded at initialization to avoid linking statically with X11. + void* X11Module; + PFN_XInternAtom XInternAtom; + PFN_XChangeProperty XChangeProperty; + PFN_XChangeWindowAttributes XChangeWindowAttributes; + PFN_XFlush XFlush; +#endif + ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -275,7 +297,7 @@ static void ImGui_ImplGlfw_ShutdownMultiViewportSupport(); // Functions static bool ImGui_ImplGlfw_IsWayland() { -#if !GLFW_HAS_X11_OR_WAYLAND +#if !GLFW_HAS_WAYLAND return false; #elif GLFW_HAS_GETPLATFORM return glfwGetPlatform() == GLFW_PLATFORM_WAYLAND; @@ -784,6 +806,30 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); #endif +#if GLFW_HAS_X11 + if (!bd->IsWayland) { + // Load X11 module dynamically. Copied from the way that GLFW does it in x11_init.c + #if defined(__CYGWIN__) + const char* x11_module_path = "libX11-6.so"; + #elif defined(__OpenBSD__) || defined(__NetBSD__) + const char* x11_module_path = "libX11.so"; + #else + const char* x11_module_path = "libX11.so.6"; + #endif + bd->X11Module = dlopen(x11_module_path, RTLD_LAZY | RTLD_LOCAL); + + bd->XInternAtom = (PFN_XInternAtom)dlsym(bd->X11Module, "XInternAtom"); + bd->XChangeProperty = (PFN_XChangeProperty)dlsym(bd->X11Module, "XChangeProperty"); + bd->XChangeWindowAttributes = (PFN_XChangeWindowAttributes)dlsym(bd->X11Module, "XChangeWindowAttributes"); + bd->XFlush = (PFN_XFlush)dlsym(bd->X11Module, "XFlush"); + + IM_ASSERT(bd->XInternAtom != nullptr); + IM_ASSERT(bd->XChangeProperty != nullptr); + IM_ASSERT(bd->XChangeWindowAttributes != nullptr); + IM_ASSERT(bd->XFlush != nullptr); + } +#endif + // Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime // to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten). #ifdef __EMSCRIPTEN__ @@ -847,6 +893,12 @@ void ImGui_ImplGlfw_Shutdown() bd->PrevWndProc = nullptr; #endif +#if GLFW_HAS_X11 + if (!bd->IsWayland) { + dlclose(bd->X11Module); + } +#endif + io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); @@ -1051,7 +1103,7 @@ static void ImGui_ImplGlfw_UpdateMonitors() // - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle. float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) { -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window)) if (bd->IsWayland) return 1.0f; @@ -1068,7 +1120,7 @@ float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) { -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND if (ImGui_ImplGlfw_IsWayland()) // We can't access our bd->IsWayland cache for a monitor. return 1.0f; #endif @@ -1090,7 +1142,7 @@ static void ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(GLFWwindow* window, glfwGetFramebufferSize(window, &display_w, &display_h); float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f; float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f; -#if GLFW_HAS_X11_OR_WAYLAND +#if GLFW_HAS_WAYLAND ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); if (!bd->IsWayland) fb_scale_x = fb_scale_y = 1.0f; @@ -1255,20 +1307,20 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) #if !defined(__APPLE__) && !defined(_WIN32) && !defined(__EMSCRIPTEN__) && GLFW_HAS_GETPLATFORM #define IMGUI_GLFW_HAS_SETWINDOWFLOATING -static void ImGui_ImplGlfw_SetWindowFloating(GLFWwindow* window) +static void ImGui_ImplGlfw_SetWindowFloating(ImGui_ImplGlfw_Data* bd, GLFWwindow* window) { #ifdef GLFW_EXPOSE_NATIVE_X11 if (glfwGetPlatform() == GLFW_PLATFORM_X11) { Display* display = glfwGetX11Display(); Window xwindow = glfwGetX11Window(window); - Atom wm_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); - Atom wm_type_dialog = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - XChangeProperty(display, xwindow, wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_type_dialog, 1); + Atom wm_type = bd->XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + Atom wm_type_dialog = bd->XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + bd->XChangeProperty(display, xwindow, wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_type_dialog, 1); XSetWindowAttributes attrs; attrs.override_redirect = False; - XChangeWindowAttributes(display, xwindow, CWOverrideRedirect, &attrs); - XFlush(display); + bd->XChangeWindowAttributes(display, xwindow, CWOverrideRedirect, &attrs); + bd->XFlush(display); } #endif // GLFW_EXPOSE_NATIVE_X11 #ifdef GLFW_EXPOSE_NATIVE_WAYLAND @@ -1305,7 +1357,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) ImGui_ImplGlfw_ContextMap_Add(vd->Window, bd->Context); viewport->PlatformHandle = (void*)vd->Window; #ifdef IMGUI_GLFW_HAS_SETWINDOWFLOATING - ImGui_ImplGlfw_SetWindowFloating(vd->Window); + ImGui_ImplGlfw_SetWindowFloating(bd, vd->Window); #endif #ifdef _WIN32 viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window);