Browse Source

Examples: DX11 + Win32: Initial attempt at implementing the viewport/platform api. (WIP/test API) (#1542)

omar 7 years ago
parent
commit
25349b31d7
3 changed files with 400 additions and 3 deletions
  1. 7 2
      examples/directx11_example/main.cpp
  2. 140 0
      examples/imgui_impl_dx11.cpp
  3. 253 1
      examples/imgui_impl_win32.cpp

+ 7 - 2
examples/directx11_example/main.cpp

@@ -117,10 +117,12 @@ int main(int, char**)
 
 
     // Setup ImGui binding
     // Setup ImGui binding
     ImGui::CreateContext();
     ImGui::CreateContext();
-    ImGuiIO& io = ImGui::GetIO(); (void)io;
+    ImGuiIO& io = ImGui::GetIO();
+    io.NavFlags |= ImGuiNavFlags_EnableKeyboard;
+    io.ConfigFlags |= ImGuiConfigFlags_MultiViewports;
+
     ImGui_ImplWin32_Init(hwnd);
     ImGui_ImplWin32_Init(hwnd);
     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
-    //io.NavFlags |= ImGuiNavFlags_EnableKeyboard;  // Enable Keyboard Controls
 
 
     // Setup style
     // Setup style
     ImGui::StyleColorsDark();
     ImGui::StyleColorsDark();
@@ -206,6 +208,9 @@ int main(int, char**)
         ImGui::Render();
         ImGui::Render();
         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
 
 
+        ImGui::UpdatePlatformWindows();
+        ImGui::RenderPlatformWindows();
+
         g_pSwapChain->Present(1, 0); // Present with vsync
         g_pSwapChain->Present(1, 0); // Present with vsync
         //g_pSwapChain->Present(0, 0); // Present without vsync
         //g_pSwapChain->Present(0, 0); // Present without vsync
     }
     }

+ 140 - 0
examples/imgui_impl_dx11.cpp

@@ -11,6 +11,7 @@
 
 
 // CHANGELOG
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 // (minor and older changes stripped away, please see git history for details)
+//  2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface
 //  2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications).
 //  2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications).
 //  2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
 //  2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
 //  2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
 //  2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
@@ -26,6 +27,7 @@
 // DirectX data
 // DirectX data
 static ID3D11Device*            g_pd3dDevice = NULL;
 static ID3D11Device*            g_pd3dDevice = NULL;
 static ID3D11DeviceContext*     g_pd3dDeviceContext = NULL;
 static ID3D11DeviceContext*     g_pd3dDeviceContext = NULL;
+static IDXGIFactory1*           g_pFactory = NULL;
 static ID3D11Buffer*            g_pVB = NULL;
 static ID3D11Buffer*            g_pVB = NULL;
 static ID3D11Buffer*            g_pIB = NULL;
 static ID3D11Buffer*            g_pIB = NULL;
 static ID3D10Blob *             g_pVertexShaderBlob = NULL;
 static ID3D10Blob *             g_pVertexShaderBlob = NULL;
@@ -46,6 +48,10 @@ struct VERTEX_CONSTANT_BUFFER
     float        mvp[4][4];
     float        mvp[4][4];
 };
 };
 
 
+// Forward Declarations
+static void ImGui_ImplDX11_InitPlatformInterface();
+static void ImGui_ImplDX11_ShutdownPlatformInterface();
+
 // Render function
 // Render function
 // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
 // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
 void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
 void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
@@ -466,13 +472,29 @@ void    ImGui_ImplDX11_InvalidateDeviceObjects()
 
 
 bool    ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
 bool    ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
 {
 {
+    // Get factory from device
+    IDXGIDevice* pDXGIDevice = NULL;
+    IDXGIAdapter* pDXGIAdapter = NULL;
+    IDXGIFactory1* pFactory = NULL;
+    if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) != S_OK)
+        return false;
+    if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) != S_OK)
+        return false;
+    if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) != S_OK)
+        return false;
+
+    ImGuiIO& io = ImGui::GetIO();
     g_pd3dDevice = device;
     g_pd3dDevice = device;
     g_pd3dDeviceContext = device_context;
     g_pd3dDeviceContext = device_context;
+    g_pFactory = pFactory;
+    if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports)
+        ImGui_ImplDX11_InitPlatformInterface();
     return true;
     return true;
 }
 }
 
 
 void ImGui_ImplDX11_Shutdown()
 void ImGui_ImplDX11_Shutdown()
 {
 {
+    ImGui_ImplDX11_ShutdownPlatformInterface();
     ImGui_ImplDX11_InvalidateDeviceObjects();
     ImGui_ImplDX11_InvalidateDeviceObjects();
     g_pd3dDevice = NULL;
     g_pd3dDevice = NULL;
     g_pd3dDeviceContext = NULL;
     g_pd3dDeviceContext = NULL;
@@ -483,3 +505,121 @@ void ImGui_ImplDX11_NewFrame()
     if (!g_pFontSampler)
     if (!g_pFontSampler)
         ImGui_ImplDX11_CreateDeviceObjects();
         ImGui_ImplDX11_CreateDeviceObjects();
 }
 }
+
+// --------------------------------------------------------------------------------------------------------
+// Platform Windows
+// --------------------------------------------------------------------------------------------------------
+
+#include "imgui_internal.h"     // ImGuiViewport
+
+struct ImGuiPlatformDataDx11
+{
+    IDXGISwapChain*             SwapChain;
+    ID3D11RenderTargetView*     RTView;
+
+    ImGuiPlatformDataDx11()     { SwapChain = NULL; RTView = NULL; }
+    ~ImGuiPlatformDataDx11()    { IM_ASSERT(SwapChain == NULL && RTView == NULL); }
+};
+
+static void ImGui_ImplDX11_CreateViewport(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataDx11* data = IM_NEW(ImGuiPlatformDataDx11)();
+    viewport->RendererUserData = data;
+
+    // FIXME-PLATFORM
+    HWND hwnd = (HWND)viewport->PlatformHandle;
+    IM_ASSERT(hwnd != 0);
+
+    // Create swap chain
+    DXGI_SWAP_CHAIN_DESC sd;
+    ZeroMemory(&sd, sizeof(sd));
+    sd.BufferDesc.Width = (UINT)viewport->Size.x;
+    sd.BufferDesc.Height = (UINT)viewport->Size.y;
+    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    sd.SampleDesc.Count = 1;
+    sd.SampleDesc.Quality = 0;
+    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    sd.BufferCount = 1;
+    sd.OutputWindow = hwnd;
+    sd.Windowed = TRUE;
+    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+    sd.Flags = 0;
+
+    IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL);
+    g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain);
+
+    // Create the render target
+    if (data->SwapChain)
+    {
+        ID3D11Texture2D* pBackBuffer;
+        data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
+        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
+        pBackBuffer->Release();
+    }
+}
+
+static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport)
+{
+    if (ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData)
+    {
+        if (data->SwapChain)
+            data->SwapChain->Release();
+        data->SwapChain = NULL;
+        if (data->RTView)
+            data->RTView->Release();
+        data->RTView = NULL;
+        IM_DELETE(data);
+    }
+    viewport->RendererUserData = NULL;
+}
+
+static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, int w, int h)
+{
+    ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
+    if (data->RTView)
+    {
+        data->RTView->Release();
+        data->RTView = NULL;
+    }
+    if (data->SwapChain)
+    {
+        ID3D11Texture2D* pBackBuffer = NULL;
+        data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0);
+        data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
+        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
+        pBackBuffer->Release();
+    }
+}
+
+static void ImGui_ImplDX11_RenderViewport(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
+    ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM
+    clear_color.w = 1.0f;
+    g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL);
+    g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color);
+    ImGui_ImplDX11_RenderDrawData(&viewport->DrawData);
+}
+
+static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
+    data->SwapChain->Present(0, 0); // Present without vsync
+}
+
+void ImGui_ImplDX11_InitPlatformInterface()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    io.RendererInterface.CreateViewport = ImGui_ImplDX11_CreateViewport;
+    io.RendererInterface.DestroyViewport = ImGui_ImplDX11_DestroyViewport;
+    io.RendererInterface.ResizeViewport = ImGui_ImplDX11_ResizeViewport;
+    io.RendererInterface.RenderViewport = ImGui_ImplDX11_RenderViewport;
+    io.RendererInterface.SwapBuffers = ImGui_ImplDX11_SwapBuffers;
+}
+
+void ImGui_ImplDX11_ShutdownPlatformInterface()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    memset(&io.RendererInterface, 0, sizeof(io.RendererInterface));
+}
+

+ 253 - 1
examples/imgui_impl_win32.cpp

@@ -5,8 +5,12 @@
 #include "imgui_impl_win32.h"
 #include "imgui_impl_win32.h"
 #define WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <windows.h>
+#include <tchar.h>
+
+#include "imgui_internal.h" // FIXME-PLATFORM
 
 
 // CHANGELOG
 // CHANGELOG
+//  2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface
 //  2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
 //  2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
 //  2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
 //  2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
 //  2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set.
 //  2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set.
@@ -24,6 +28,10 @@ static INT64                g_Time = 0;
 static INT64                g_TicksPerSecond = 0;
 static INT64                g_TicksPerSecond = 0;
 static ImGuiMouseCursor     g_LastMouseCursor = ImGuiMouseCursor_Count_;
 static ImGuiMouseCursor     g_LastMouseCursor = ImGuiMouseCursor_Count_;
 
 
+// Forward Declarations
+static void ImGui_ImplWin32_InitPlatformInterface();
+static void ImGui_ImplWin32_ShutdownPlatformInterface();
+
 // Functions
 // Functions
 bool    ImGui_ImplWin32_Init(void* hwnd)
 bool    ImGui_ImplWin32_Init(void* hwnd)
 {
 {
@@ -57,11 +65,21 @@ bool    ImGui_ImplWin32_Init(void* hwnd)
     io.KeyMap[ImGuiKey_Y] = 'Y';
     io.KeyMap[ImGuiKey_Y] = 'Y';
     io.KeyMap[ImGuiKey_Z] = 'Z';
     io.KeyMap[ImGuiKey_Z] = 'Z';
 
 
-    io.ImeWindowHandle = g_hWnd;    return true;
+    io.ImeWindowHandle = g_hWnd;    
+
+    // Our mouse update function expect PlatformHandle to be filled for the main viewport
+    ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+    main_viewport->PlatformHandle = (void*)g_hWnd;
+
+    if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports)
+        ImGui_ImplWin32_InitPlatformInterface();
+
+    return true;
 }
 }
 
 
 void    ImGui_ImplWin32_Shutdown()
 void    ImGui_ImplWin32_Shutdown()
 {
 {
+    ImGui_ImplWin32_ShutdownPlatformInterface();
     g_hWnd = (HWND)0;
     g_hWnd = (HWND)0;
 }
 }
 
 
@@ -92,6 +110,42 @@ static void ImGui_ImplWin32_UpdateMouseCursor()
     }
     }
 }
 }
 
 
+// This code supports multiple OS Windows mapped into different ImGui viewports, 
+// So it is a little more complicated than your typical binding code (which only needs to set io.MousePos in your WM_MOUSEMOVE handler)
+// This is what imgui needs from the back-end to support multiple windows:
+// - io.MousePos               = mouse position (e.g. io.MousePos == viewport->Pos when we are on the upper-left of our viewport)
+// - io.MousePosViewport       = viewport which mouse position is based from (generally the focused/active/capturing viewport)
+// - io.MouseHoveredWindow     = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional]
+// This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler. 
+// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multiple OS windows support.
+static void ImGui_ImplWin32_UpdateMousePos()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
+    io.MousePosViewport = 0;
+    io.MouseHoveredViewport = 0;
+
+    POINT pos;
+    if (!::GetCursorPos(&pos))
+        return;
+
+    // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui
+    io.ConfigFlags |= ImGuiConfigFlags_PlatformHasMouseHoveredViewport;
+    HWND hovered_hwnd = ::WindowFromPoint(pos);
+    if (hovered_hwnd)
+        if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
+            io.MouseHoveredViewport = viewport->ID;
+
+    // Convert mouse from screen position to window client position
+    HWND focused_hwnd = ::GetActiveWindow();
+    if (focused_hwnd != 0 && ::ScreenToClient(focused_hwnd, &pos))
+        if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd))
+        {
+            io.MousePos = ImVec2(viewport->Pos.x + (float)pos.x, viewport->Pos.y + (float)pos.y);
+            io.MousePosViewport = viewport->ID;
+        }
+}
+
 void    ImGui_ImplWin32_NewFrame()
 void    ImGui_ImplWin32_NewFrame()
 {
 {
     ImGuiIO& io = ImGui::GetIO();
     ImGuiIO& io = ImGui::GetIO();
@@ -133,6 +187,8 @@ void    ImGui_ImplWin32_NewFrame()
         ImGui_ImplWin32_UpdateMouseCursor();
         ImGui_ImplWin32_UpdateMouseCursor();
     }
     }
 
 
+    ImGui_ImplWin32_UpdateMousePos();
+
     // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application.
     // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application.
     ImGui::NewFrame();
     ImGui::NewFrame();
 }
 }
@@ -213,3 +269,199 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa
     }
     }
     return 0;
     return 0;
 }
 }
+
+// --------------------------------------------------------------------------------------------------------
+// Platform Windows
+// --------------------------------------------------------------------------------------------------------
+
+struct ImGuiPlatformDataWin32
+{
+    HWND    Hwnd;
+    bool    ExternalResize;
+    DWORD   DwStyle;
+    DWORD   DwExStyle;
+
+    ImGuiPlatformDataWin32() { Hwnd = NULL; ExternalResize = false; DwStyle = DwExStyle = 0; }
+    ~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); }
+};
+
+static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)();
+    viewport->PlatformUserData = data;
+
+    if (viewport->Flags & ImGuiViewportFlags_NoDecoration)
+    {
+        data->DwStyle = WS_POPUP;
+        data->DwExStyle = 0;
+    }
+    else
+    {
+        data->DwStyle = WS_OVERLAPPEDWINDOW;
+        data->DwExStyle = WS_EX_TOOLWINDOW;
+    }
+
+    // Create window
+    RECT rect = { (LONG)viewport->PlatformOsDesktopPos.x, (LONG)viewport->PlatformOsDesktopPos.y, (LONG)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (LONG)(viewport->PlatformOsDesktopPos.y + viewport->Size.y) };
+    ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
+    data->ExternalResize = true;
+    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->ExternalResize = false;
+    viewport->PlatformHandle = data->Hwnd;
+}
+
+static void ImGui_ImplWin32_DestroyViewport(ImGuiViewport* viewport)
+{
+    if (ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData)
+    {
+        if (::GetCapture() == data->Hwnd)
+        {
+            // Transfer capture so if we started dragging from a window that later disappears, we'll still release the MOUSEUP event.
+            ::ReleaseCapture();
+            ::SetCapture(g_hWnd);
+        }
+        if (data->Hwnd)
+            ::DestroyWindow(data->Hwnd);
+        data->Hwnd = NULL;
+        IM_DELETE(data);
+    }
+    viewport->PlatformUserData = viewport->PlatformHandle = NULL;
+}
+
+static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    data->ExternalResize = true;
+    if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
+        ::ShowWindow(data->Hwnd, SW_SHOWNA);
+    else
+        ::ShowWindow(data->Hwnd, SW_SHOW);
+    data->ExternalResize = false;
+}
+
+static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    POINT pos = { 0, 0 };
+    ::ClientToScreen(data->Hwnd, &pos);
+    return ImVec2((float)pos.x, (float)pos.y);
+}
+
+static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
+    ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
+    ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    RECT rect;
+    ::GetClientRect(data->Hwnd, &rect);
+    return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
+}
+
+static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    data->ExternalResize = true;
+    RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
+    ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen
+    ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+    data->ExternalResize = false;
+}
+
+static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
+{
+    ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+    IM_ASSERT(data->Hwnd != 0);
+    ::SetWindowTextA(data->Hwnd, title);
+}
+
+static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
+        return true;
+
+    if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
+    {
+        ImGuiIO& io = ImGui::GetIO();
+        ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
+        switch (msg)
+        {
+        case WM_CLOSE:
+            viewport->PlatformRequestClose = true;
+            return 0;
+        case WM_MOVE:
+            viewport->PlatformOsDesktopPos = ImVec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam));
+            break;
+        case WM_NCHITTEST:
+            // Let mouse pass-through the window, this is used while e.g. dragging a window, we creates a temporary overlay but want the cursor to aim behind our overlay.
+            if (viewport->Flags & ImGuiViewportFlags_NoInputs)
+                return HTTRANSPARENT;
+            break;
+        case WM_SIZE:
+            if (!data->ExternalResize)
+                viewport->PlatformRequestResize = true;
+            if (io.RendererInterface.ResizeViewport)
+                io.RendererInterface.ResizeViewport(viewport, (int)LOWORD(lParam), (int)HIWORD(lParam));
+            break;
+        }
+    }
+
+    return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+static void ImGui_ImplWin32_InitPlatformInterface()
+{
+    WNDCLASSEX wcex;
+    wcex.cbSize = sizeof(WNDCLASSEX);
+    wcex.style = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
+    wcex.cbClsExtra = 0;
+    wcex.cbWndExtra = 0;
+    wcex.hInstance = ::GetModuleHandle(NULL);
+    wcex.hIcon = NULL;
+    wcex.hCursor = NULL;
+    wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+    wcex.lpszMenuName = NULL;
+    wcex.lpszClassName = _T("ImGui Platform");
+    wcex.hIconSm = NULL;
+    ::RegisterClassEx(&wcex);
+
+    // Register platform interface (will be coupled with a renderer interface)
+    ImGuiIO& io = ImGui::GetIO();
+    io.PlatformInterface.CreateViewport = ImGui_ImplWin32_CreateViewport;
+    io.PlatformInterface.DestroyViewport = ImGui_ImplWin32_DestroyViewport;
+    io.PlatformInterface.ShowWindow = ImGui_ImplWin32_ShowWindow;
+    io.PlatformInterface.SetWindowPos = ImGui_ImplWin32_SetWindowPos;
+    io.PlatformInterface.GetWindowPos = ImGui_ImplWin32_GetWindowPos;
+    io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize;
+    io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize;
+    io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
+
+    // Register main window handle
+    ImGuiViewport* main_viewport = ImGui::GetMainViewport();
+    ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)();
+    data->Hwnd = g_hWnd;
+    main_viewport->PlatformUserData = data;
+    main_viewport->PlatformHandle = (void*)data->Hwnd;
+}
+
+static void ImGui_ImplWin32_ShutdownPlatformInterface()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface));
+
+    ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL));
+}