diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 4633e24d92d2..d426367ade0a 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -825,6 +825,11 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) { + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + if (platform_io.Platform_PushOglContext) + platform_io.Platform_PushOglContext(viewport); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); @@ -832,6 +837,9 @@ static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) glClear(GL_COLOR_BUFFER_BIT); } ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); + + if (platform_io.Platform_PopOglContext) + platform_io.Platform_PopOglContext(viewport); } static void ImGui_ImplOpenGL3_InitPlatformInterface() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 89ef37a2780c..5865b94d3dca 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -23,6 +23,14 @@ #include #include +// Stuff for loading WGL functions explicitly +static HGLRC (WINAPI* _wglCreateContext)(HDC); +static BOOL (WINAPI* _wglDeleteContext)(HGLRC); +static BOOL (WINAPI* _wglMakeCurrent)(HDC, HGLRC); +static BOOL (WINAPI* _wglShareLists)(HGLRC, HGLRC); +static HGLRC (WINAPI* _wglGetCurrentContext)(VOID); +static HDC (WINAPI* _wglGetCurrentDC)(VOID); + // Configuration flags to add in your imconfig.h file: //#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. @@ -86,6 +94,7 @@ static void ImGui_ImplWin32_UpdateMonitors(); struct ImGui_ImplWin32_Data { HWND hWnd; + HGLRC OpenGlContext; HWND MouseHwnd; bool MouseTracked; int MouseButtonsDown; @@ -96,6 +105,11 @@ struct ImGui_ImplWin32_Data bool WantUpdateHasGamepad; bool WantUpdateMonitors; + // OpenGL + HMODULE OglDLL; + HDC stashedDeviceContext; + HGLRC stashedOpenGlContext; + #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; @@ -116,6 +130,11 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() // Functions bool ImGui_ImplWin32_Init(void* hwnd) +{ + return ImGui_ImplWin32_InitForOpenGL(hwnd, NULL); +} + +bool ImGui_ImplWin32_InitForOpenGL(void* hwnd, void* hglrc) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); @@ -136,6 +155,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; + bd->OpenGlContext = (HGLRC)hglrc; bd->WantUpdateHasGamepad = true; bd->WantUpdateMonitors = true; bd->TicksPerSecond = perf_frequency; @@ -148,6 +168,28 @@ bool ImGui_ImplWin32_Init(void* hwnd) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplWin32_InitPlatformInterface(); + // Dynamically load WGL symbols + // This is needed to allow compilation if and when non-OpenGL APIs are used with Win32. + // The function pointer names are preceded with an underscore ("_") to prevent duplicate + // symbols in projects where OpenGL32.dll is linked implicitly. + if (hglrc && !(bd->OglDLL)) + { + bd->OglDLL = ::LoadLibraryA("opengl32.dll"); + IM_ASSERT(bd->OglDLL); + _wglCreateContext = (HGLRC(WINAPI*)(HDC))GetProcAddress(bd->OglDLL, "wglCreateContext"); + _wglDeleteContext = (BOOL(WINAPI*)(HGLRC))GetProcAddress(bd->OglDLL, "wglDeleteContext"); + _wglMakeCurrent = (BOOL(WINAPI*)(HDC, HGLRC))GetProcAddress(bd->OglDLL, "wglMakeCurrent"); + _wglShareLists = (BOOL(WINAPI*)(HGLRC, HGLRC))GetProcAddress(bd->OglDLL, "wglShareLists"); + _wglGetCurrentContext = (HGLRC(WINAPI*)(VOID))GetProcAddress(bd->OglDLL, "wglGetCurrentContext"); + _wglGetCurrentDC = (HDC(WINAPI*)(VOID))GetProcAddress(bd->OglDLL, "wglGetCurrentDC"); + IM_ASSERT(_wglCreateContext); + IM_ASSERT(_wglDeleteContext); + IM_ASSERT(_wglMakeCurrent); + IM_ASSERT(_wglShareLists); + IM_ASSERT(_wglGetCurrentContext); + IM_ASSERT(_wglGetCurrentDC); + } + // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD const char* xinput_dll_names[] = @@ -179,6 +221,10 @@ void ImGui_ImplWin32_Shutdown() ImGui_ImplWin32_ShutdownPlatformInterface(); + // Unload OpenGL library + if (bd->OglDLL) + ::FreeLibrary(bd->OglDLL); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD if (bd->XInputDLL) @@ -843,10 +889,18 @@ struct ImGui_ImplWin32_ViewportData { HWND Hwnd; bool HwndOwned; + HGLRC OpenGlContext; DWORD DwStyle; DWORD DwExStyle; - ImGui_ImplWin32_ViewportData() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; } + ImGui_ImplWin32_ViewportData() + { + Hwnd = NULL; + HwndOwned = false; + OpenGlContext = NULL; + DwStyle = 0; + DwExStyle = 0; + } ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == NULL); } }; @@ -888,6 +942,38 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) vd->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; + + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + if (bd->OpenGlContext) + { + HDC previousDC = _wglGetCurrentDC(); + HGLRC previousRC = _wglGetCurrentContext(); + HDC newDC = ::GetDC(vd->Hwnd); + + // Use the main window's pixel format + int pf; + PIXELFORMATDESCRIPTOR pfd; + if (previousDC) + { + pf = ::GetPixelFormat(previousDC); + ::DescribePixelFormat(previousDC, pf, sizeof(pfd), &pfd); + } + else + { + HDC mainDC = ::GetDC(bd->hWnd); + pf = ::GetPixelFormat(mainDC); + ::DescribePixelFormat(mainDC, pf, sizeof(pfd), &pfd); + ::ReleaseDC(bd->hWnd, mainDC); + } + ::SetPixelFormat(newDC, pf, &pfd); + + // Create OpenGL context and share main context's lists with it + vd->OpenGlContext = _wglCreateContext(newDC); + _wglMakeCurrent(newDC, vd->OpenGlContext); + _wglShareLists(bd->OpenGlContext, vd->OpenGlContext); + _wglMakeCurrent(previousDC, previousRC); + ::ReleaseDC(vd->Hwnd, newDC); + } } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) @@ -902,7 +988,12 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) ::SetCapture(bd->hWnd); } if (vd->Hwnd && vd->HwndOwned) + { + if (vd->OpenGlContext) + _wglDeleteContext(vd->OpenGlContext); + ::DestroyWindow(vd->Hwnd); + } vd->Hwnd = NULL; IM_DELETE(vd); } @@ -1062,6 +1153,39 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) #endif } +static void ImGui_ImplWin32_PushOglContext(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_PushOglContext() is only registered if API is OpenGL + + bd->stashedDeviceContext = _wglGetCurrentDC(); + bd->stashedOpenGlContext = _wglGetCurrentContext(); + _wglMakeCurrent(::GetDC(vd->Hwnd), vd->OpenGlContext); +} + +static void ImGui_ImplWin32_PopOglContext(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_PopOglContext() is only registered if API is OpenGL + + IM_ASSERT(_wglGetCurrentContext() == vd->OpenGlContext); + HDC vd_dc = _wglGetCurrentDC(); + _wglMakeCurrent(bd->stashedDeviceContext, bd->stashedOpenGlContext); + ::ReleaseDC(vd->Hwnd, vd_dc); +} + +static void ImGui_ImplWin32_OGL_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_OGL_SwapBuffers() is only registered if API is OpenGL + + ImGui_ImplWin32_PushOglContext(viewport); + ::SwapBuffers(_wglGetCurrentDC()); + ImGui_ImplWin32_PopOglContext(viewport); +} + static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) @@ -1071,6 +1195,9 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, { switch (msg) { + case WM_ERASEBKGND: + // Prevent flickering on window invalidation + return TRUE; case WM_CLOSE: viewport->PlatformRequestClose = true; return 0; @@ -1134,14 +1261,22 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI + + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + if (bd->OpenGlContext) // OpenGL + { + platform_io.Platform_PushOglContext = ImGui_ImplWin32_PushOglContext; + platform_io.Platform_PopOglContext = ImGui_ImplWin32_PopOglContext; + platform_io.Platform_SwapBuffers = ImGui_ImplWin32_OGL_SwapBuffers; + } // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); vd->Hwnd = bd->hWnd; vd->HwndOwned = false; + vd->OpenGlContext = bd->OpenGlContext; main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->hWnd; } diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 3778c3220191..05409853b9e7 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -17,6 +17,7 @@ #include "imgui.h" // IMGUI_IMPL_API IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd, void* hglrc); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); diff --git a/imgui.h b/imgui.h index 184ca324ddaf..168a8d95f97e 100644 --- a/imgui.h +++ b/imgui.h @@ -3154,6 +3154,8 @@ struct ImGuiPlatformIO float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). + void (*Platform_PushOglContext)(ImGuiViewport* vp); // . . . R . // (Optional) For an OpenGL Renderer to make a platform window's rendering context current for rendering + void (*Platform_PopOglContext)(ImGuiViewport* vp); // . . . R . // (Optional) For an OpenGL Renderer to revert to the previous rendering context after rendering a platform window // (Optional) Renderer functions (e.g. DirectX, OpenGL, Vulkan) void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow)