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

This commit is contained in:
omar 2018-04-09 22:01:24 +02:00
parent 56ad2a2d74
commit d4dd448511
9 changed files with 44 additions and 28 deletions

View File

@ -554,6 +554,7 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport)
static void ImGui_ImplDX10_DestroyWindow(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 (ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData)
{ {
if (data->SwapChain) if (data->SwapChain)

View File

@ -561,6 +561,7 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport)
static void ImGui_ImplDX11_DestroyWindow(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 (ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData)
{ {
if (data->SwapChain) if (data->SwapChain)

View File

@ -690,6 +690,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
static void ImGui_ImplDX12_DestroyWindow(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) if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData)
{ {
IM_ASSERT(0); IM_ASSERT(0);

View File

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

View File

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

View File

@ -1108,6 +1108,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
static void ImGui_ImplVulkan_DestroyWindow(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) if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData)
{ {
ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator); ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator);

View File

@ -364,10 +364,11 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2)
struct ImGuiViewportDataWin32 struct ImGuiViewportDataWin32
{ {
HWND Hwnd; HWND Hwnd;
bool HwndOwned;
DWORD DwStyle; DWORD DwStyle;
DWORD DwExStyle; DWORD DwExStyle;
ImGuiViewportDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; } ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; }
~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); } ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); }
}; };
@ -393,10 +394,11 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
// Create window // 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) }; 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); ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
data->Hwnd = ::CreateWindowExA( data->Hwnd = ::CreateWindowEx(
data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name 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 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 g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param
data->HwndOwned = true;
viewport->PlatformRequestResize = false; viewport->PlatformRequestResize = false;
viewport->PlatformHandle = data->Hwnd; viewport->PlatformHandle = data->Hwnd;
} }
@ -411,7 +413,7 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
::ReleaseCapture(); ::ReleaseCapture();
::SetCapture(g_hWnd); ::SetCapture(g_hWnd);
} }
if (data->Hwnd) if (data->Hwnd && data->HwndOwned)
::DestroyWindow(data->Hwnd); ::DestroyWindow(data->Hwnd);
data->Hwnd = NULL; data->Hwnd = NULL;
IM_DELETE(data); IM_DELETE(data);
@ -564,12 +566,13 @@ static void ImGui_ImplWin32_InitPlatformInterface()
platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; 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(); ImGuiViewport* main_viewport = ImGui::GetMainViewport();
ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
data->Hwnd = g_hWnd; data->Hwnd = g_hWnd;
data->HwndOwned = false;
main_viewport->PlatformUserData = data; main_viewport->PlatformUserData = data;
main_viewport->PlatformHandle = (void*)data->Hwnd; main_viewport->PlatformHandle = (void*)g_hWnd;
} }
static void ImGui_ImplWin32_ShutdownPlatformInterface() static void ImGui_ImplWin32_ShutdownPlatformInterface()

View File

@ -762,7 +762,7 @@ static void UpdateManualResize(ImGuiWindow* window, const ImVec2& si
static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window);
// Viewports // 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 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 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; } 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)) if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports))
return; 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++) for (int i = 1; i < g.Viewports.Size; i++)
{ {
ImGuiViewportP* viewport = g.Viewports[i]; ImGuiViewportP* viewport = g.Viewports[i];
@ -3609,12 +3610,12 @@ void ImGui::UpdatePlatformWindows()
// for (int i = 1; i < data->Viewports.Size; i++) // for (int i = 1; i < data->Viewports.Size; i++)
// MySwapBufferFunction(data->Viewports[i], my_args); // 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) void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
{ {
if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports))
return; return;
// Skip the main viewport (index 0), which is always fully handled by the application!
ImGuiPlatformData* data = ImGui::GetPlatformData(); ImGuiPlatformData* data = ImGui::GetPlatformData();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < data->Viewports.Size; i++) 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.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_CreateWindow != NULL && "Platform init didn't install handlers?");
IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != 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 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function!
#endif #endif
@ -4059,6 +4060,9 @@ void ImGui::Initialize(ImGuiContext* context)
void ImGui::DestroyPlatformWindows() 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; ImGuiContext& g = *GImGui;
if (g.PlatformIO.Renderer_DestroyWindow) if (g.PlatformIO.Renderer_DestroyWindow)
for (int i = 0; i < g.Viewports.Size; i++) for (int i = 0; i < g.Viewports.Size; i++)

View File

@ -549,10 +549,10 @@ namespace ImGui
// (Optional) Platform interface for multi-viewport support // (Optional) Platform interface for multi-viewport support
IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer function, for back-end to setup. 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 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 ImGuiViewport* GetMainViewport(); // == GetPlatformData()->MainViewport == GetPlatformData()->Viewports[0]
IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport 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_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 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 in back-end shutdown if you need to close Platform Windows before imgui shutdown. 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); IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle);
// Memory Utilities // Memory Utilities