Explorar o código

Examples, Platform, Viewport: Fixed inconsistent window ownership issues. Added comments. Made Win32/SDL back-ends track ownership.

omar %!s(int64=7) %!d(string=hai) anos
pai
achega
d4dd448511

+ 1 - 0
examples/imgui_impl_dx10.cpp

@@ -554,6 +554,7 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport)
 
 static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport)
 {
+    // The main viewport (owned by the application) will always have RendererUserData == NULL here since we didn't create the data for it.
     if (ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData)
     {
         if (data->SwapChain)

+ 1 - 0
examples/imgui_impl_dx11.cpp

@@ -561,6 +561,7 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport)
 
 static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport)
 {
+    // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
     if (ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData)
     {
         if (data->SwapChain)

+ 1 - 0
examples/imgui_impl_dx12.cpp

@@ -690,6 +690,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
 
 static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
 {
+    // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
     if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData)
     {
         IM_ASSERT(0);

+ 8 - 5
examples/imgui_impl_glfw.cpp

@@ -371,12 +371,14 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport)
 {
     if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData)
     {
+        if (data->WindowOwned)
+        {
 #if GLFW_HAS_GLFW_HOVERED
-        HWND hwnd = glfwGetWin32Window(data->Window);
-        ::RemovePropA(hwnd, "IMGUI_VIEWPORT");
-    #endif
-        if (data->Window && data->WindowOwned)
+            HWND hwnd = glfwGetWin32Window(data->Window);
+            ::RemovePropA(hwnd, "IMGUI_VIEWPORT");
+#endif
             glfwDestroyWindow(data->Window);
+        }
         data->Window = NULL;
         IM_DELETE(data);
     }
@@ -539,12 +541,13 @@ static void ImGui_ImplGlfw_InitPlatformInterface()
     platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface;
 #endif
 
-    // Register main window handle
+    // Register main window handle (which is owned by the main application, not by us)
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)();
     data->Window = g_Window;
     data->WindowOwned = false;
     main_viewport->PlatformUserData = data;
+    main_viewport->PlatformHandle = (void*)g_Window;
 }
 
 static void ImGui_ImplGlfw_ShutdownPlatformInterface()

+ 9 - 7
examples/imgui_impl_sdl2.cpp

@@ -285,9 +285,10 @@ struct ImGuiViewportDataSDL2
 {
     SDL_Window*     Window;
     Uint32          WindowID;
+    bool            WindowOwned;
     SDL_GLContext   GLContext;
 
-    ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; GLContext = NULL; }
+    ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; WindowOwned = false; GLContext = NULL; }
     ~ImGuiViewportDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); }
 };
 
@@ -316,8 +317,8 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport)
     sdl_flags |= SDL_WINDOW_HIDDEN;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
     sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
-    data->Window = SDL_CreateWindow("No Title Yet", 
-        (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
+    data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
+    data->WindowOwned = true; 
     if (use_opengl)
         data->GLContext = SDL_GL_CreateContext(data->Window);
     if (use_opengl && backup_context)
@@ -329,11 +330,11 @@ static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport)
 {
     if (ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData)
     {
-        if (data->GLContext)
+        if (data->GLContext && data->WindowOwned)
             SDL_GL_DeleteContext(data->GLContext);
-        data->GLContext = NULL;
-        if (data->Window)
+        if (data->Window && data->WindowOwned)
             SDL_DestroyWindow(data->Window);
+        data->GLContext = NULL;
         data->Window = NULL;
         IM_DELETE(data);
     }
@@ -455,11 +456,12 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g
     platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface;
 #endif
 
-    // Register main window handle
+    // Register main window handle (which is owned by the main application, not by us)
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)();
     data->Window = window;
     data->WindowID = SDL_GetWindowID(window);
+    data->WindowOwned = false;
     data->GLContext = sdl_gl_context;
     main_viewport->PlatformUserData = data;
     main_viewport->PlatformHandle = data->Window;

+ 1 - 0
examples/imgui_impl_vulkan.cpp

@@ -1108,6 +1108,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
 
 static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport)
 {
+    // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
     if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData)
     {
         ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator);

+ 11 - 8
examples/imgui_impl_win32.cpp

@@ -364,10 +364,11 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2)
 struct ImGuiViewportDataWin32
 {
     HWND    Hwnd;
+    bool    HwndOwned;
     DWORD   DwStyle;
     DWORD   DwExStyle;
 
-    ImGuiViewportDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; }
+    ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false;  DwStyle = DwExStyle = 0; }
     ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); }
 };
 
@@ -393,10 +394,11 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
     // Create window
     RECT rect = { (LONG)viewport->PlatformPos.x, (LONG)viewport->PlatformPos.y, (LONG)(viewport->PlatformPos.x + viewport->Size.x), (LONG)(viewport->PlatformPos.y + viewport->Size.y) };
     ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
-    data->Hwnd = ::CreateWindowExA(
-        data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle,       // Style, class name, window name
-        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,    // Window area
-        g_hWnd, NULL, ::GetModuleHandle(NULL), NULL);                           // Parent window, Menu, Instance, Param
+    data->Hwnd = ::CreateWindowEx(
+        data->DwExStyle, _T("ImGui Platform"), _T("No Title Yet"), data->DwStyle,   // Style, class name, window name
+        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,        // Window area
+        g_hWnd, NULL, ::GetModuleHandle(NULL), NULL);                               // Parent window, Menu, Instance, Param
+    data->HwndOwned = true;
     viewport->PlatformRequestResize = false;
     viewport->PlatformHandle = data->Hwnd;
 }
@@ -411,7 +413,7 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
             ::ReleaseCapture();
             ::SetCapture(g_hWnd);
         }
-        if (data->Hwnd)
+        if (data->Hwnd && data->HwndOwned)
             ::DestroyWindow(data->Hwnd);
         data->Hwnd = NULL;
         IM_DELETE(data);
@@ -564,12 +566,13 @@ static void ImGui_ImplWin32_InitPlatformInterface()
     platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
     platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale;
 
-    // Register main window handle
+    // Register main window handle (which is owned by the main application, not by us)
     ImGuiViewport* main_viewport = ImGui::GetMainViewport();
     ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
     data->Hwnd = g_hWnd;
+    data->HwndOwned = false;
     main_viewport->PlatformUserData = data;
-    main_viewport->PlatformHandle = (void*)data->Hwnd;
+    main_viewport->PlatformHandle = (void*)g_hWnd;
 }
 
 static void ImGui_ImplWin32_ShutdownPlatformInterface()

+ 8 - 4
imgui.cpp

@@ -762,7 +762,7 @@ static void             UpdateManualResize(ImGuiWindow* window, const ImVec2& si
 static void             FocusFrontMostActiveWindow(ImGuiWindow* ignore_window);
 
 // Viewports
-const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
+const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
 static inline ImRect    GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); }
 static inline ImVec2    ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport)    { return imgui_pos - viewport->Pos + viewport->PlatformPos; }
 static inline ImVec2    ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; }
@@ -3530,7 +3530,8 @@ void ImGui::UpdatePlatformWindows()
     if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports))
         return;
 
-    // Create/resize/destroy platform windows and update the list that the user can process
+    // Create/resize/destroy platform windows to match each active viewport. Update the user-facing list.
+    // Skip the main viewport (index 0), which is always fully handled by the application!
     for (int i = 1; i < g.Viewports.Size; i++)
     {
         ImGuiViewportP* viewport = g.Viewports[i];
@@ -3609,12 +3610,12 @@ void ImGui::UpdatePlatformWindows()
 //    for (int i = 1; i < data->Viewports.Size; i++)
 //        MySwapBufferFunction(data->Viewports[i], my_args);
 //
-// Note how we intentionally skip the main viewport (index 0) which is generally rendered as part of our main application.
 void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
 {
     if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports))
         return;
 
+    // Skip the main viewport (index 0), which is always fully handled by the application!
     ImGuiPlatformData* data = ImGui::GetPlatformData();
     ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
     for (int i = 1; i < data->Viewports.Size; i++)
@@ -3779,7 +3780,7 @@ void ImGui::NewFrame()
             IM_ASSERT(g.FrameCount == 0 || g.FrameCountPlatformEnded == g.FrameCount && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?");
             IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL  && "Platform init didn't install handlers?");
             IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
-            IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL    && "Platform init didn't setup main viewport.");
+            IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
             IM_ASSERT(g.IO.RenderDrawListsFn == NULL);  // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function!
 #endif
@@ -4059,6 +4060,9 @@ void ImGui::Initialize(ImGuiContext* context)
 
 void ImGui::DestroyPlatformWindows()
 {
+    // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data it may hold on it.
+    // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, 
+    // and won't destroy the underlying platform/renderer data.
     ImGuiContext& g = *GImGui;
     if (g.PlatformIO.Renderer_DestroyWindow)
         for (int i = 0; i < g.Viewports.Size; i++)

+ 4 - 4
imgui.h

@@ -549,10 +549,10 @@ namespace ImGui
     // (Optional) Platform interface for multi-viewport support
     IMGUI_API ImGuiPlatformIO&   GetPlatformIO();                   // Platform/Renderer function, for back-end to setup.
     IMGUI_API ImGuiPlatformData* GetPlatformData();                 // List of viewports. Viewport 0 is always the main viewport, followed by the secondary viewports.
-    IMGUI_API ImGuiViewport*     GetMainViewport();                 // GetPlatformData()->MainViewport
-    IMGUI_API void               UpdatePlatformWindows();           // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport
-    IMGUI_API void               RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user.
-    IMGUI_API void               DestroyPlatformWindows();          // (Optional) Call in back-end shutdown if you need to close Platform Windows before imgui shutdown.
+    IMGUI_API ImGuiViewport*     GetMainViewport();                 // == GetPlatformData()->MainViewport == GetPlatformData()->Viewports[0]
+    IMGUI_API void               UpdatePlatformWindows();           // Call in main loop. Will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport.
+    IMGUI_API void               RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // Call in main loop. Will call RenderWindow/SwapBuffers platform functions for each secondary viewport. May be reimplemented by user for custom rendering needs.
+    IMGUI_API void               DestroyPlatformWindows();          // (Optional) Call DestroyWindow platform functions for all viewports. Call from back-end Shutdown() if you need to close platform windows before imgui shutdown. Otherwise will be called by DestroyContext().
     IMGUI_API ImGuiViewport*     FindViewportByPlatformHandle(void* platform_handle);
 
     // Memory Utilities