From f77fc2dbbbc7f59bf75538de053568215d016fbb Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Wed, 9 Jul 2025 22:57:37 +0200 Subject: [PATCH 01/11] switched to imgui 1.92.1 --- .gitmodules | 4 ++-- Imgui/src/ImGuiImplSDL.cpp | 2 +- ThirdParty/imgui | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 5047b264..3621e5fa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "ThirdParty/imgui"] path = ThirdParty/imgui - url = https://github.com/DiligentGraphics/imgui +url=https://github.com/ocornut/imgui.git [submodule "ThirdParty/args"] path = ThirdParty/args url = https://github.com/DiligentGraphics/args @@ -15,4 +15,4 @@ url = https://github.com/DiligentGraphics/stb.git [submodule "ThirdParty/json"] path = ThirdParty/json - url = https://github.com/DiligentGraphics/json.git + url = https://github.com/DiligentGraphics/json.git \ No newline at end of file diff --git a/Imgui/src/ImGuiImplSDL.cpp b/Imgui/src/ImGuiImplSDL.cpp index b219c3d9..4c4051ee 100644 --- a/Imgui/src/ImGuiImplSDL.cpp +++ b/Imgui/src/ImGuiImplSDL.cpp @@ -28,7 +28,7 @@ #include "Errors.hpp" #include "RenderDevice.h" -#include "backends/imgui_impl_sdl.h" +#include "backends/imgui_impl_sdl2.h" namespace Diligent { diff --git a/ThirdParty/imgui b/ThirdParty/imgui index 66ad2ad5..5d412687 160000 --- a/ThirdParty/imgui +++ b/ThirdParty/imgui @@ -1 +1 @@ -Subproject commit 66ad2ad5398cb61433009553e10fd326d13acb84 +Subproject commit 5d4126876bc10396d4c6511853ff10964414c776 From f639866ebfc35a2b0fa9d36b45ff2963c5881052 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Fri, 11 Jul 2025 20:49:41 +0200 Subject: [PATCH 02/11] imgui renderer compiling, but texture updates not handled yet --- Imgui/interface/ImGuiDiligentRenderer.hpp | 11 ++- Imgui/interface/ImGuiImplDiligent.hpp | 2 - Imgui/src/ImGuiDiligentRenderer.cpp | 104 +++++++++++++--------- Imgui/src/ImGuiImplDiligent.cpp | 5 -- 4 files changed, 70 insertions(+), 52 deletions(-) diff --git a/Imgui/interface/ImGuiDiligentRenderer.hpp b/Imgui/interface/ImGuiDiligentRenderer.hpp index f3578fce..ee2845be 100644 --- a/Imgui/interface/ImGuiDiligentRenderer.hpp +++ b/Imgui/interface/ImGuiDiligentRenderer.hpp @@ -28,6 +28,7 @@ #pragma once #include +#include #include "../../../DiligentCore/Primitives/interface/BasicTypes.h" #include "../../../DiligentCore/Common/interface/BasicMath.hpp" #include "../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp" @@ -64,9 +65,9 @@ class ImGuiDiligentRenderer void RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDrawData); void InvalidateDeviceObjects(); void CreateDeviceObjects(); - void CreateFontsTexture(); private: + void UpdateTextures(IDeviceContext* pCtx, ImDrawData* pDrawData); inline float4 TransformClipRect(const ImVec2& DisplaySize, const float4& rect) const; private: @@ -75,10 +76,16 @@ class ImGuiDiligentRenderer RefCntAutoPtr m_pIB; RefCntAutoPtr m_pVertexConstantBuffer; RefCntAutoPtr m_pPSO; - RefCntAutoPtr m_pFontSRV; RefCntAutoPtr m_pSRB; IShaderResourceVariable* m_pTextureVar = nullptr; + struct TextureInfo + { + RefCntAutoPtr pTexture; + RefCntAutoPtr pSRV; + }; + std::vector m_Textures; + const TEXTURE_FORMAT m_BackBufferFmt; const TEXTURE_FORMAT m_DepthBufferFmt; Uint32 m_VertexBufferSize = 0; diff --git a/Imgui/interface/ImGuiImplDiligent.hpp b/Imgui/interface/ImGuiImplDiligent.hpp index 31e27005..194c8540 100644 --- a/Imgui/interface/ImGuiImplDiligent.hpp +++ b/Imgui/interface/ImGuiImplDiligent.hpp @@ -120,8 +120,6 @@ class ImGuiImplDiligent void InvalidateDeviceObjects(); void CreateDeviceObjects(); - void UpdateFontsTexture(); - protected: std::unique_ptr m_pRenderer; }; diff --git a/Imgui/src/ImGuiDiligentRenderer.cpp b/Imgui/src/ImGuiDiligentRenderer.cpp index 22d2f191..e9d8b8e5 100644 --- a/Imgui/src/ImGuiDiligentRenderer.cpp +++ b/Imgui/src/ImGuiDiligentRenderer.cpp @@ -508,6 +508,7 @@ ImGuiDiligentRenderer::ImGuiDiligentRenderer(const ImGuiDiligentCreateInfo& CI) IO.BackendRendererName = "ImGuiDiligentRenderer"; if (m_BaseVertexSupported) IO.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + IO.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; CreateDeviceObjects(); } @@ -537,8 +538,8 @@ void ImGuiDiligentRenderer::InvalidateDeviceObjects() m_pIB.Release(); m_pVertexConstantBuffer.Release(); m_pPSO.Release(); - m_pFontSRV.Release(); m_pSRB.Release(); + m_Textures.clear(); } void ImGuiDiligentRenderer::CreateDeviceObjects() @@ -718,42 +719,10 @@ void ImGuiDiligentRenderer::CreateDeviceObjects() } m_pPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, "Constants")->Set(m_pVertexConstantBuffer); - CreateFontsTexture(); -} - -void ImGuiDiligentRenderer::CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& IO = ImGui::GetIO(); - - unsigned char* pData = nullptr; - int Width = 0; - int Weight = 0; - IO.Fonts->GetTexDataAsRGBA32(&pData, &Width, &Weight); - - TextureDesc FontTexDesc; - FontTexDesc.Name = "Imgui font texture"; - FontTexDesc.Type = RESOURCE_DIM_TEX_2D; - FontTexDesc.Width = static_cast(Width); - FontTexDesc.Height = static_cast(Weight); - FontTexDesc.Format = TEX_FORMAT_RGBA8_UNORM; - FontTexDesc.BindFlags = BIND_SHADER_RESOURCE; - FontTexDesc.Usage = USAGE_IMMUTABLE; - - TextureSubResData Mip0Data[] = {{pData, 4 * Uint64{FontTexDesc.Width}}}; - TextureData InitData(Mip0Data, _countof(Mip0Data)); - - RefCntAutoPtr pFontTex; - m_pDevice->CreateTexture(FontTexDesc, &InitData, &pFontTex); - m_pFontSRV = pFontTex->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); - m_pSRB.Release(); m_pPSO->CreateShaderResourceBinding(&m_pSRB, true); m_pTextureVar = m_pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "Texture"); VERIFY_EXPR(m_pTextureVar != nullptr); - - // Store our identifier - IO.Fonts->TexID = reinterpret_cast(m_pFontSRV.RawPtr()); } float4 ImGuiDiligentRenderer::TransformClipRect(const ImVec2& DisplaySize, const float4& rect) const @@ -869,12 +838,63 @@ float4 ImGuiDiligentRenderer::TransformClipRect(const ImVec2& DisplaySize, const } } +void ImGuiDiligentRenderer::UpdateTextures(IDeviceContext* pCtx, ImDrawData* pDrawData) +{ + if (pDrawData->Textures == nullptr) + return; + + for (ImTextureData* pTexData : *pDrawData->Textures) + { + if (pTexData->Status != ImTextureStatus_OK) + { + if (pTexData->Status == ImTextureStatus_WantCreate) + { + TextureDesc TexDesc; + TexDesc.Name = "ImGui Texture"; + TexDesc.Type = RESOURCE_DIM_TEX_2D; + TexDesc.Width = pTexData->Width; + TexDesc.Height = pTexData->Height; + TexDesc.Format = pTexData->Format == ImTextureFormat_Alpha8 ? TEX_FORMAT_R8_UNORM : TEX_FORMAT_RGBA8_UNORM; + TexDesc.BindFlags = BIND_SHADER_RESOURCE; + TexDesc.Usage = USAGE_DEFAULT; + + TextureInfo NewTex; + m_pDevice->CreateTexture(TexDesc, nullptr, &NewTex.pTexture); + NewTex.pSRV = NewTex.pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + + pTexData->SetTexID(reinterpret_cast(NewTex.pSRV.RawPtr())); + pTexData->BackendUserData = reinterpret_cast(m_Textures.size()); + m_Textures.push_back(std::move(NewTex)); + } + + if (pTexData->Status == ImTextureStatus_WantCreate || pTexData->Status == ImTextureStatus_WantUpdates) + { + const auto& Tex = m_Textures[reinterpret_cast(pTexData->BackendUserData)]; + + Box UpdateBox; + UpdateBox.MaxX = pTexData->UpdateRect.w; + UpdateBox.MaxY = pTexData->UpdateRect.h; + + TextureSubResData SubresData; + SubresData.pData = pTexData->GetPixels(); + SubresData.Stride = pTexData->GetPitch(); + + pCtx->UpdateTexture(Tex.pTexture, 0, 0, UpdateBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_NONE, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + } + + pTexData->SetStatus(ImTextureStatus_OK); + } + } +} + void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDrawData) { ScopedDebugGroup DebugGroup{pCtx, "ImGui"}; + UpdateTextures(pCtx, pDrawData); + // Avoid rendering when minimized - if (pDrawData->DisplaySize.x <= 0.0f || pDrawData->DisplaySize.y <= 0.0f || pDrawData->CmdListsCount == 0) + if (pDrawData->DisplaySize.x <= 0.0f || pDrawData->DisplaySize.y <= 0.0f || pDrawData->CmdLists.empty()) return; // Create and grow vertex/index buffers if needed @@ -916,9 +936,8 @@ void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDr ImDrawVert* pVtxDst = Vertices; ImDrawIdx* pIdxDst = Indices; - for (Int32 CmdListID = 0; CmdListID < pDrawData->CmdListsCount; CmdListID++) + for (const ImDrawList* pCmdList : pDrawData->CmdLists) { - const ImDrawList* pCmdList = pDrawData->CmdLists[CmdListID]; memcpy(pVtxDst, pCmdList->VtxBuffer.Data, pCmdList->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(pIdxDst, pCmdList->IdxBuffer.Data, pCmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); pVtxDst += pCmdList->VtxBuffer.Size; @@ -1020,13 +1039,12 @@ void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDr Uint32 GlobalVtxOffset = 0; ITextureView* pLastTextureView = nullptr; - for (Int32 CmdListID = 0; CmdListID < pDrawData->CmdListsCount; CmdListID++) + for (const ImDrawList* pCmdList : pDrawData->CmdLists) { - const ImDrawList* pCmdList = pDrawData->CmdLists[CmdListID]; - for (Int32 CmdID = 0; CmdID < pCmdList->CmdBuffer.Size; CmdID++) + for (const ImDrawCmd& Cmd : pCmdList->CmdBuffer) { - const ImDrawCmd* pCmd = &pCmdList->CmdBuffer[CmdID]; - if (pCmd->UserCallback != NULL) + const ImDrawCmd* pCmd = &Cmd; + if (pCmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) @@ -1067,14 +1085,14 @@ void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDr pCtx->SetScissorRects(1, &Scissor, m_RenderSurfaceWidth, m_RenderSurfaceHeight); // Bind texture - ITextureView* pTextureView = reinterpret_cast(pCmd->TextureId); + ITextureView* pTextureView = reinterpret_cast(pCmd->GetTexID()); VERIFY_EXPR(pTextureView); if (pTextureView != pLastTextureView) { pLastTextureView = pTextureView; m_pTextureVar->Set(pTextureView); - pCtx->CommitShaderResources(m_pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); } + pCtx->CommitShaderResources(m_pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); DrawIndexedAttribs DrawAttrs{pCmd->ElemCount, sizeof(ImDrawIdx) == sizeof(Uint16) ? VT_UINT16 : VT_UINT32, DRAW_FLAG_VERIFY_STATES}; DrawAttrs.FirstIndexLocation = pCmd->IdxOffset + GlobalIdxOffset; diff --git a/Imgui/src/ImGuiImplDiligent.cpp b/Imgui/src/ImGuiImplDiligent.cpp index 7ec75601..e0f1bc4e 100644 --- a/Imgui/src/ImGuiImplDiligent.cpp +++ b/Imgui/src/ImGuiImplDiligent.cpp @@ -95,9 +95,4 @@ void ImGuiImplDiligent::CreateDeviceObjects() m_pRenderer->CreateDeviceObjects(); } -void ImGuiImplDiligent::UpdateFontsTexture() -{ - m_pRenderer->CreateFontsTexture(); -} - } // namespace Diligent From e63fa2bb114476db65f986854e9f594cac8f9662 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Fri, 11 Jul 2025 21:27:45 +0200 Subject: [PATCH 03/11] fonts scaling (wip) --- Imgui/src/ImGuiDiligentRenderer.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Imgui/src/ImGuiDiligentRenderer.cpp b/Imgui/src/ImGuiDiligentRenderer.cpp index e9d8b8e5..ea949889 100644 --- a/Imgui/src/ImGuiDiligentRenderer.cpp +++ b/Imgui/src/ImGuiDiligentRenderer.cpp @@ -530,6 +530,18 @@ void ImGuiDiligentRenderer::NewFrame(Uint32 RenderSurfaceWidth, void ImGuiDiligentRenderer::EndFrame() { + auto& Textures = ImGui::GetPlatformIO().Textures; + for (int i = 0; i < Textures.Size; ++i) + { + if (Textures.Data[i]->Status == ImTextureStatus_WantDestroy) + { + // The texture is not used by ImGui anymore. + // We don't destroy it immediately to avoid issues with in-flight commands. + // The texture will be destroyed when InvalidateDeviceObjects() is called. + // This is not ideal, but should be safe. + Textures.Data[i]->SetStatus(ImTextureStatus_Destroyed); + } + } } void ImGuiDiligentRenderer::InvalidateDeviceObjects() @@ -700,6 +712,7 @@ void ImGuiDiligentRenderer::CreateDeviceObjects() SamLinearWrap.AddressU = TEXTURE_ADDRESS_WRAP; SamLinearWrap.AddressV = TEXTURE_ADDRESS_WRAP; SamLinearWrap.AddressW = TEXTURE_ADDRESS_WRAP; + SamLinearWrap.MagFilter = FILTER_TYPE_POINT; ImmutableSamplerDesc ImtblSamplers[] = { {SHADER_TYPE_PIXEL, "Texture", SamLinearWrap} // @@ -872,16 +885,24 @@ void ImGuiDiligentRenderer::UpdateTextures(IDeviceContext* pCtx, ImDrawData* pDr const auto& Tex = m_Textures[reinterpret_cast(pTexData->BackendUserData)]; Box UpdateBox; - UpdateBox.MaxX = pTexData->UpdateRect.w; - UpdateBox.MaxY = pTexData->UpdateRect.h; + UpdateBox.MinX = pTexData->UpdateRect.x; + UpdateBox.MinY = pTexData->UpdateRect.y; + UpdateBox.MaxX = pTexData->UpdateRect.x + pTexData->UpdateRect.w; + UpdateBox.MaxY = pTexData->UpdateRect.y + pTexData->UpdateRect.h; TextureSubResData SubresData; - SubresData.pData = pTexData->GetPixels(); + SubresData.pData = pTexData->GetPixelsAt(pTexData->UpdateRect.x, pTexData->UpdateRect.y); SubresData.Stride = pTexData->GetPitch(); pCtx->UpdateTexture(Tex.pTexture, 0, 0, UpdateBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_NONE, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); } + if (pTexData->Status == ImTextureStatus_WantDestroy) + { + // The texture is not used by ImGui anymore. + // We don't destroy it immediately to avoid issues with in-flight commands. + } + pTexData->SetStatus(ImTextureStatus_OK); } } From 3d60f2818b5cb58493ef72ca4555c8d4b07513c5 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Sat, 12 Jul 2025 12:33:34 +0200 Subject: [PATCH 04/11] finalized and code cleanup --- Imgui/CMakeLists.txt | 2 + Imgui/interface/ImGuiDiligentRenderer.hpp | 11 +- Imgui/src/ImGuiDiligentRenderer.cpp | 166 ++++++++++++---------- 3 files changed, 98 insertions(+), 81 deletions(-) diff --git a/Imgui/CMakeLists.txt b/Imgui/CMakeLists.txt index d4eab65f..65ac1eb0 100644 --- a/Imgui/CMakeLists.txt +++ b/Imgui/CMakeLists.txt @@ -124,6 +124,8 @@ if(PLATFORM_UNIVERSAL_WINDOWS) target_compile_definitions(Diligent-Imgui PRIVATE IMGUI_DISABLE_WIN32_FUNCTIONS) endif() +target_compile_definitions(Diligent-Imgui PUBLIC IMGUI_DEFINE_MATH_OPERATORS) + if(PLATFORM_WIN32 AND MINGW_BUILD) # Link with dwmapi.lib as imgui_impl_win32.cpp skips # '#pragma comment(lib, "dwmapi")' diff --git a/Imgui/interface/ImGuiDiligentRenderer.hpp b/Imgui/interface/ImGuiDiligentRenderer.hpp index ee2845be..2cd07c7a 100644 --- a/Imgui/interface/ImGuiDiligentRenderer.hpp +++ b/Imgui/interface/ImGuiDiligentRenderer.hpp @@ -28,7 +28,6 @@ #pragma once #include -#include #include "../../../DiligentCore/Primitives/interface/BasicTypes.h" #include "../../../DiligentCore/Common/interface/BasicMath.hpp" #include "../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp" @@ -67,8 +66,9 @@ class ImGuiDiligentRenderer void CreateDeviceObjects(); private: - void UpdateTextures(IDeviceContext* pCtx, ImDrawData* pDrawData); inline float4 TransformClipRect(const ImVec2& DisplaySize, const float4& rect) const; + void UpdateTexture(IDeviceContext *pCtx, ImTextureData *tex); + void DestroyTexture(ImTextureData *tex); private: RefCntAutoPtr m_pDevice; @@ -79,13 +79,6 @@ class ImGuiDiligentRenderer RefCntAutoPtr m_pSRB; IShaderResourceVariable* m_pTextureVar = nullptr; - struct TextureInfo - { - RefCntAutoPtr pTexture; - RefCntAutoPtr pSRV; - }; - std::vector m_Textures; - const TEXTURE_FORMAT m_BackBufferFmt; const TEXTURE_FORMAT m_DepthBufferFmt; Uint32 m_VertexBufferSize = 0; diff --git a/Imgui/src/ImGuiDiligentRenderer.cpp b/Imgui/src/ImGuiDiligentRenderer.cpp index ea949889..c60decc9 100644 --- a/Imgui/src/ImGuiDiligentRenderer.cpp +++ b/Imgui/src/ImGuiDiligentRenderer.cpp @@ -530,28 +530,22 @@ void ImGuiDiligentRenderer::NewFrame(Uint32 RenderSurfaceWidth, void ImGuiDiligentRenderer::EndFrame() { - auto& Textures = ImGui::GetPlatformIO().Textures; - for (int i = 0; i < Textures.Size; ++i) - { - if (Textures.Data[i]->Status == ImTextureStatus_WantDestroy) - { - // The texture is not used by ImGui anymore. - // We don't destroy it immediately to avoid issues with in-flight commands. - // The texture will be destroyed when InvalidateDeviceObjects() is called. - // This is not ideal, but should be safe. - Textures.Data[i]->SetStatus(ImTextureStatus_Destroyed); - } - } } void ImGuiDiligentRenderer::InvalidateDeviceObjects() { - m_pVB.Release(); - m_pIB.Release(); - m_pVertexConstantBuffer.Release(); - m_pPSO.Release(); - m_pSRB.Release(); - m_Textures.clear(); + // Destroy ImGui textures that may still exist + ImGuiPlatformIO &io = ImGui::GetPlatformIO(); + for (int n = 0; n < io.Textures.Size; ++n) + { + DestroyTexture(io.Textures[n]); + } + + m_pVB.Release(); + m_pIB.Release(); + m_pVertexConstantBuffer.Release(); + m_pPSO.Release(); + m_pSRB.Release(); } void ImGuiDiligentRenderer::CreateDeviceObjects() @@ -712,7 +706,6 @@ void ImGuiDiligentRenderer::CreateDeviceObjects() SamLinearWrap.AddressU = TEXTURE_ADDRESS_WRAP; SamLinearWrap.AddressV = TEXTURE_ADDRESS_WRAP; SamLinearWrap.AddressW = TEXTURE_ADDRESS_WRAP; - SamLinearWrap.MagFilter = FILTER_TYPE_POINT; ImmutableSamplerDesc ImtblSamplers[] = { {SHADER_TYPE_PIXEL, "Texture", SamLinearWrap} // @@ -851,69 +844,98 @@ float4 ImGuiDiligentRenderer::TransformClipRect(const ImVec2& DisplaySize, const } } -void ImGuiDiligentRenderer::UpdateTextures(IDeviceContext* pCtx, ImDrawData* pDrawData) +void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext *pCtx, ImTextureData *tex) { - if (pDrawData->Textures == nullptr) - return; + auto *backend = static_cast(tex->BackendUserData); + + // ---------------------------------------------------------------- + // 1) CREATE + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantCreate) + { + IM_ASSERT(backend == nullptr && tex->TexID == ImTextureID_Invalid); + + TextureDesc desc; + desc.Name = "ImGuiTexture"; + desc.Type = RESOURCE_DIM_TEX_2D; + desc.Width = static_cast(tex->Width); + desc.Height = static_cast(tex->Height); + desc.Format = tex->Format == ImTextureFormat_Alpha8 ? TEX_FORMAT_R8_UNORM : TEX_FORMAT_RGBA8_UNORM; + desc.Usage = USAGE_DEFAULT; // allow future UpdateTexture() + desc.BindFlags = BIND_SHADER_RESOURCE; + + TextureSubResData mip0; + mip0.pData = tex->GetPixels(); + mip0.Stride = tex->GetPitch(); + TextureData init(&mip0, 1); + + ITexture *pTexture = nullptr; + m_pDevice->CreateTexture(desc, &init, &pTexture); + pTexture->AddRef(); + ITextureView *ptexView = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + ptexView->AddRef(); + + // store texture view and texture pointers inside imgui and set texture state to ok + tex->SetTexID(reinterpret_cast(ptexView)); + tex->BackendUserData = reinterpret_cast(pTexture); + tex->SetStatus(ImTextureStatus_OK); + return; + } + + // ---------------------------------------------------------------- + // 2) UPDATE + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantUpdates && backend != nullptr) + { + Box dstBox{ Uint32(tex->UpdateRect.x), Uint32(tex->UpdateRect.x + tex->UpdateRect.w), + Uint32(tex->UpdateRect.y), Uint32(tex->UpdateRect.y + tex->UpdateRect.h), + 0, 1 }; // Z range + + TextureSubResData SubresData; + SubresData.pData = tex->GetPixelsAt(tex->UpdateRect.x, tex->UpdateRect.y); + SubresData.Stride = tex->GetPitch(); + + pCtx->UpdateTexture(backend, 0, 0, dstBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_VERIFY, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + tex->SetStatus(ImTextureStatus_OK); + return; + } + + // ---------------------------------------------------------------- + // 3) DESTROY + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + { + DestroyTexture(tex); + } +} - for (ImTextureData* pTexData : *pDrawData->Textures) +void ImGuiDiligentRenderer::DestroyTexture(ImTextureData *tex) +{ + if (ITextureView *pTextureView = reinterpret_cast(tex->GetTexID())) { - if (pTexData->Status != ImTextureStatus_OK) - { - if (pTexData->Status == ImTextureStatus_WantCreate) - { - TextureDesc TexDesc; - TexDesc.Name = "ImGui Texture"; - TexDesc.Type = RESOURCE_DIM_TEX_2D; - TexDesc.Width = pTexData->Width; - TexDesc.Height = pTexData->Height; - TexDesc.Format = pTexData->Format == ImTextureFormat_Alpha8 ? TEX_FORMAT_R8_UNORM : TEX_FORMAT_RGBA8_UNORM; - TexDesc.BindFlags = BIND_SHADER_RESOURCE; - TexDesc.Usage = USAGE_DEFAULT; - - TextureInfo NewTex; - m_pDevice->CreateTexture(TexDesc, nullptr, &NewTex.pTexture); - NewTex.pSRV = NewTex.pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); - - pTexData->SetTexID(reinterpret_cast(NewTex.pSRV.RawPtr())); - pTexData->BackendUserData = reinterpret_cast(m_Textures.size()); - m_Textures.push_back(std::move(NewTex)); - } - - if (pTexData->Status == ImTextureStatus_WantCreate || pTexData->Status == ImTextureStatus_WantUpdates) - { - const auto& Tex = m_Textures[reinterpret_cast(pTexData->BackendUserData)]; - - Box UpdateBox; - UpdateBox.MinX = pTexData->UpdateRect.x; - UpdateBox.MinY = pTexData->UpdateRect.y; - UpdateBox.MaxX = pTexData->UpdateRect.x + pTexData->UpdateRect.w; - UpdateBox.MaxY = pTexData->UpdateRect.y + pTexData->UpdateRect.h; - - TextureSubResData SubresData; - SubresData.pData = pTexData->GetPixelsAt(pTexData->UpdateRect.x, pTexData->UpdateRect.y); - SubresData.Stride = pTexData->GetPitch(); - - pCtx->UpdateTexture(Tex.pTexture, 0, 0, UpdateBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_NONE, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - } - - if (pTexData->Status == ImTextureStatus_WantDestroy) - { - // The texture is not used by ImGui anymore. - // We don't destroy it immediately to avoid issues with in-flight commands. - } + pTextureView->Release(); + } - pTexData->SetStatus(ImTextureStatus_OK); - } + if (auto *pTexture = static_cast(tex->BackendUserData)) + { + pTexture->Release(); } + + tex->BackendUserData = nullptr; + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDrawData) { ScopedDebugGroup DebugGroup{pCtx, "ImGui"}; - UpdateTextures(pCtx, pDrawData); - + // Handle requested texture creates/updates/destroys ----------------- + if (pDrawData->Textures != nullptr) + for (ImTextureData *tex : *pDrawData->Textures) + if (tex->Status != ImTextureStatus_OK) + UpdateTexture(pCtx, tex); + // Avoid rendering when minimized if (pDrawData->DisplaySize.x <= 0.0f || pDrawData->DisplaySize.y <= 0.0f || pDrawData->CmdLists.empty()) return; @@ -1112,8 +1134,8 @@ void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDr { pLastTextureView = pTextureView; m_pTextureVar->Set(pTextureView); + pCtx->CommitShaderResources(m_pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); } - pCtx->CommitShaderResources(m_pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); DrawIndexedAttribs DrawAttrs{pCmd->ElemCount, sizeof(ImDrawIdx) == sizeof(Uint16) ? VT_UINT16 : VT_UINT32, DRAW_FLAG_VERIFY_STATES}; DrawAttrs.FirstIndexLocation = pCmd->IdxOffset + GlobalIdxOffset; From 69dc74604eda3d09fbd06d8ac101164dd5205ca8 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Sat, 12 Jul 2025 16:30:43 +0200 Subject: [PATCH 05/11] correct formatting --- Imgui/interface/ImGuiDiligentRenderer.hpp | 4 +- Imgui/src/ImGuiDiligentRenderer.cpp | 150 +++++++++++----------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/Imgui/interface/ImGuiDiligentRenderer.hpp b/Imgui/interface/ImGuiDiligentRenderer.hpp index 2cd07c7a..d99f0509 100644 --- a/Imgui/interface/ImGuiDiligentRenderer.hpp +++ b/Imgui/interface/ImGuiDiligentRenderer.hpp @@ -67,8 +67,8 @@ class ImGuiDiligentRenderer private: inline float4 TransformClipRect(const ImVec2& DisplaySize, const float4& rect) const; - void UpdateTexture(IDeviceContext *pCtx, ImTextureData *tex); - void DestroyTexture(ImTextureData *tex); + void UpdateTexture(IDeviceContext* pCtx, ImTextureData* tex); + void DestroyTexture(ImTextureData* tex); private: RefCntAutoPtr m_pDevice; diff --git a/Imgui/src/ImGuiDiligentRenderer.cpp b/Imgui/src/ImGuiDiligentRenderer.cpp index c60decc9..2afbef8f 100644 --- a/Imgui/src/ImGuiDiligentRenderer.cpp +++ b/Imgui/src/ImGuiDiligentRenderer.cpp @@ -534,18 +534,18 @@ void ImGuiDiligentRenderer::EndFrame() void ImGuiDiligentRenderer::InvalidateDeviceObjects() { - // Destroy ImGui textures that may still exist - ImGuiPlatformIO &io = ImGui::GetPlatformIO(); + // Destroy ImGui textures that may still exist + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); for (int n = 0; n < io.Textures.Size; ++n) { DestroyTexture(io.Textures[n]); } - m_pVB.Release(); - m_pIB.Release(); - m_pVertexConstantBuffer.Release(); - m_pPSO.Release(); - m_pSRB.Release(); + m_pVB.Release(); + m_pIB.Release(); + m_pVertexConstantBuffer.Release(); + m_pPSO.Release(); + m_pSRB.Release(); } void ImGuiDiligentRenderer::CreateDeviceObjects() @@ -844,98 +844,98 @@ float4 ImGuiDiligentRenderer::TransformClipRect(const ImVec2& DisplaySize, const } } -void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext *pCtx, ImTextureData *tex) +void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* tex) { - auto *backend = static_cast(tex->BackendUserData); - - // ---------------------------------------------------------------- - // 1) CREATE - // ---------------------------------------------------------------- - if (tex->Status == ImTextureStatus_WantCreate) - { - IM_ASSERT(backend == nullptr && tex->TexID == ImTextureID_Invalid); - - TextureDesc desc; - desc.Name = "ImGuiTexture"; - desc.Type = RESOURCE_DIM_TEX_2D; - desc.Width = static_cast(tex->Width); - desc.Height = static_cast(tex->Height); - desc.Format = tex->Format == ImTextureFormat_Alpha8 ? TEX_FORMAT_R8_UNORM : TEX_FORMAT_RGBA8_UNORM; - desc.Usage = USAGE_DEFAULT; // allow future UpdateTexture() - desc.BindFlags = BIND_SHADER_RESOURCE; - - TextureSubResData mip0; - mip0.pData = tex->GetPixels(); - mip0.Stride = tex->GetPitch(); - TextureData init(&mip0, 1); - - ITexture *pTexture = nullptr; - m_pDevice->CreateTexture(desc, &init, &pTexture); + auto* backend = static_cast(tex->BackendUserData); + + // ---------------------------------------------------------------- + // 1) CREATE + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantCreate) + { + IM_ASSERT(backend == nullptr && tex->TexID == ImTextureID_Invalid); + + TextureDesc desc; + desc.Name = "ImGuiTexture"; + desc.Type = RESOURCE_DIM_TEX_2D; + desc.Width = static_cast(tex->Width); + desc.Height = static_cast(tex->Height); + desc.Format = tex->Format == ImTextureFormat_Alpha8 ? TEX_FORMAT_R8_UNORM : TEX_FORMAT_RGBA8_UNORM; + desc.Usage = USAGE_DEFAULT; // allow future UpdateTexture() + desc.BindFlags = BIND_SHADER_RESOURCE; + + TextureSubResData mip0; + mip0.pData = tex->GetPixels(); + mip0.Stride = tex->GetPitch(); + TextureData init(&mip0, 1); + + ITexture* pTexture = nullptr; + m_pDevice->CreateTexture(desc, &init, &pTexture); pTexture->AddRef(); - ITextureView *ptexView = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); - ptexView->AddRef(); + ITextureView* ptexView = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + ptexView->AddRef(); // store texture view and texture pointers inside imgui and set texture state to ok - tex->SetTexID(reinterpret_cast(ptexView)); - tex->BackendUserData = reinterpret_cast(pTexture); - tex->SetStatus(ImTextureStatus_OK); - return; - } - - // ---------------------------------------------------------------- - // 2) UPDATE - // ---------------------------------------------------------------- - if (tex->Status == ImTextureStatus_WantUpdates && backend != nullptr) - { - Box dstBox{ Uint32(tex->UpdateRect.x), Uint32(tex->UpdateRect.x + tex->UpdateRect.w), - Uint32(tex->UpdateRect.y), Uint32(tex->UpdateRect.y + tex->UpdateRect.h), - 0, 1 }; // Z range - - TextureSubResData SubresData; - SubresData.pData = tex->GetPixelsAt(tex->UpdateRect.x, tex->UpdateRect.y); - SubresData.Stride = tex->GetPitch(); - - pCtx->UpdateTexture(backend, 0, 0, dstBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_VERIFY, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - tex->SetStatus(ImTextureStatus_OK); - return; - } - - // ---------------------------------------------------------------- - // 3) DESTROY - // ---------------------------------------------------------------- - if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) - { + tex->SetTexID(reinterpret_cast(ptexView)); + tex->BackendUserData = reinterpret_cast(pTexture); + tex->SetStatus(ImTextureStatus_OK); + return; + } + + // ---------------------------------------------------------------- + // 2) UPDATE + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantUpdates && backend != nullptr) + { + Box dstBox{Uint32(tex->UpdateRect.x), Uint32(tex->UpdateRect.x + tex->UpdateRect.w), + Uint32(tex->UpdateRect.y), Uint32(tex->UpdateRect.y + tex->UpdateRect.h), + 0, 1}; // Z range + + TextureSubResData SubresData; + SubresData.pData = tex->GetPixelsAt(tex->UpdateRect.x, tex->UpdateRect.y); + SubresData.Stride = tex->GetPitch(); + + pCtx->UpdateTexture(backend, 0, 0, dstBox, SubresData, RESOURCE_STATE_TRANSITION_MODE_VERIFY, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + tex->SetStatus(ImTextureStatus_OK); + return; + } + + // ---------------------------------------------------------------- + // 3) DESTROY + // ---------------------------------------------------------------- + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + { DestroyTexture(tex); - } + } } -void ImGuiDiligentRenderer::DestroyTexture(ImTextureData *tex) +void ImGuiDiligentRenderer::DestroyTexture(ImTextureData* tex) { - if (ITextureView *pTextureView = reinterpret_cast(tex->GetTexID())) + if (ITextureView* pTextureView = reinterpret_cast(tex->GetTexID())) { pTextureView->Release(); } - if (auto *pTexture = static_cast(tex->BackendUserData)) + if (auto* pTexture = static_cast(tex->BackendUserData)) { pTexture->Release(); } tex->BackendUserData = nullptr; tex->SetTexID(ImTextureID_Invalid); - tex->SetStatus(ImTextureStatus_Destroyed); + tex->SetStatus(ImTextureStatus_Destroyed); } void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDrawData) { ScopedDebugGroup DebugGroup{pCtx, "ImGui"}; - // Handle requested texture creates/updates/destroys ----------------- - if (pDrawData->Textures != nullptr) - for (ImTextureData *tex : *pDrawData->Textures) - if (tex->Status != ImTextureStatus_OK) - UpdateTexture(pCtx, tex); - + // Handle requested texture creates/updates/destroys ----------------- + if (pDrawData->Textures != nullptr) + for (ImTextureData* tex : *pDrawData->Textures) + if (tex->Status != ImTextureStatus_OK) + UpdateTexture(pCtx, tex); + // Avoid rendering when minimized if (pDrawData->DisplaySize.x <= 0.0f || pDrawData->DisplaySize.y <= 0.0f || pDrawData->CmdLists.empty()) return; From 3ba093c6bd2f5dd4b1f4e8c71edce5bf627c3a11 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Sun, 13 Jul 2025 23:15:40 +0200 Subject: [PATCH 06/11] working on macos --- Imgui/CMakeLists.txt | 11 +-- Imgui/src/ImGuiImplMacOS.mm | 167 +++++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 7 deletions(-) diff --git a/Imgui/CMakeLists.txt b/Imgui/CMakeLists.txt index 65ac1eb0..2d4cb23c 100644 --- a/Imgui/CMakeLists.txt +++ b/Imgui/CMakeLists.txt @@ -84,12 +84,13 @@ else() list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.cpp) list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.h) elseif(PLATFORM_MACOS) - set(DEAR_IMGUI_V185_SOURCE - ../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm - ../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h + set(DEAR_IMGUI_OSX_SOURCE + ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm + ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h ) - target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_V185_SOURCE}) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/imgui_v1.85 PREFIX "dear_imgui_v1.85" FILES ${DEAR_IMGUI_V185_SOURCE}) + target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_OSX_SOURCE}) + source_group(TREE ${DILIGENT_DEAR_IMGUI_PATH}/backends PREFIX "dear_imgui_osx" FILES ${DEAR_IMGUI_OSX_SOURCE}) + target_link_libraries(Diligent-Imgui PRIVATE "-framework GameController") endif() target_sources(Diligent-Imgui PRIVATE diff --git a/Imgui/src/ImGuiImplMacOS.mm b/Imgui/src/ImGuiImplMacOS.mm index 018fc4a3..97fb3bbb 100644 --- a/Imgui/src/ImGuiImplMacOS.mm +++ b/Imgui/src/ImGuiImplMacOS.mm @@ -23,9 +23,11 @@ #include "imgui.h" #include "ImGuiImplMacOS.hpp" -#include "../../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h" +#include "backends/imgui_impl_osx.h" #import +ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code); + namespace Diligent { @@ -37,7 +39,7 @@ ImGuiImplMacOS::ImGuiImplMacOS(const ImGuiDiligentCreateInfo& CI, void* _Nullable view) : ImGuiImplDiligent{CI} { - ImGui_ImplOSX_Init(); + ImGui_ImplOSX_Init((NSView*)view); ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "Diligent-ImGuiImplMacOS"; @@ -61,6 +63,167 @@ ImGuiImplDiligent::NewFrame(RenderSurfaceWidth, RenderSurfaceHeight, SurfacePreTransform); } + +// Must only be called for a mouse event, otherwise an exception occurs +// (Note that NSEventTypeScrollWheel is considered "other input". Oddly enough an exception does not occur with it, but the value will sometimes be wrong!) +static ImGuiMouseSource GetMouseSource(NSEvent* event) +{ + switch (event.subtype) + { + case NSEventSubtypeTabletPoint: + return ImGuiMouseSource_Pen; + // macOS considers input from relative touch devices (like the trackpad or Apple Magic Mouse) to be touch input. + // This doesn't really make sense for Dear ImGui, which expects absolute touch devices only. + // There does not seem to be a simple way to disambiguate things here so we consider NSEventSubtypeTouch events to always come from mice. + // See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html#//apple_ref/doc/uid/10000060i-CH13-SW24 + //case NSEventSubtypeTouch: + // return ImGuiMouseSource_TouchScreen; + case NSEventSubtypeMouseEvent: + default: + return ImGuiMouseSource_Mouse; + } +} + +static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) +{ + // Only process events from the window containing ImGui view + if (event.window != view.window) + return false; + ImGuiIO& io = ImGui::GetIO(); + + if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) + { + int button = (int)[event buttonNumber]; + if (button >= 0 && button < ImGuiMouseButton_COUNT) + { + io.AddMouseSourceEvent(GetMouseSource(event)); + io.AddMouseButtonEvent(button, true); + } + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) + { + int button = (int)[event buttonNumber]; + if (button >= 0 && button < ImGuiMouseButton_COUNT) + { + io.AddMouseSourceEvent(GetMouseSource(event)); + io.AddMouseButtonEvent(button, false); + } + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) + { + NSPoint mousePoint = event.locationInWindow; + if (event.window == nil) + mousePoint = [[view window] convertPointFromScreen:mousePoint]; + mousePoint = [view convertPoint:mousePoint fromView:nil]; + if ([view isFlipped]) + mousePoint = NSMakePoint(mousePoint.x, mousePoint.y); + else + mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); + io.AddMouseSourceEvent(GetMouseSource(event)); + io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y); + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeScrollWheel) + { + // Ignore canceled events. + // + // From macOS 12.1, scrolling with two fingers and then decelerating + // by tapping two fingers results in two events appearing: + // + // 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps + // two fingers to decelerate or stop the scroll events. + // + // 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the + // two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and + // scrollingDeltaY. When these are added to the current x and y positions of the scrolling view, + // it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here. + if (event.phase == NSEventPhaseCancelled) + return false; + + double wheel_dx = 0.0; + double wheel_dy = 0.0; + + #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + { + wheel_dx = [event scrollingDeltaX]; + wheel_dy = [event scrollingDeltaY]; + if ([event hasPreciseScrollingDeltas]) + { + wheel_dx *= 0.01; + wheel_dy *= 0.01; + } + } + else + #endif // MAC_OS_X_VERSION_MAX_ALLOWED + { + wheel_dx = [event deltaX] * 0.1; + wheel_dy = [event deltaY] * 0.1; + } + if (wheel_dx != 0.0 || wheel_dy != 0.0) + io.AddMouseWheelEvent((float)wheel_dx, (float)wheel_dy); + + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) + { + if ([event isARepeat]) + return io.WantCaptureKeyboard; + + int key_code = (int)[event keyCode]; + ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); + io.AddKeyEvent(key, event.type == NSEventTypeKeyDown); + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) + + return io.WantCaptureKeyboard; + } + + if (event.type == NSEventTypeFlagsChanged) + { + unsigned short key_code = [event keyCode]; + NSEventModifierFlags modifier_flags = [event modifierFlags]; + + io.AddKeyEvent(ImGuiMod_Shift, (modifier_flags & NSEventModifierFlagShift) != 0); + io.AddKeyEvent(ImGuiMod_Ctrl, (modifier_flags & NSEventModifierFlagControl) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (modifier_flags & NSEventModifierFlagOption) != 0); + io.AddKeyEvent(ImGuiMod_Super, (modifier_flags & NSEventModifierFlagCommand) != 0); + + ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); + if (key != ImGuiKey_None) + { + // macOS does not generate down/up event for modifiers. We're trying + // to use hardware dependent masks to extract that information. + // 'imgui_mask' is left as a fallback. + NSEventModifierFlags mask = 0; + switch (key) + { + case ImGuiKey_LeftCtrl: mask = 0x0001; break; + case ImGuiKey_RightCtrl: mask = 0x2000; break; + case ImGuiKey_LeftShift: mask = 0x0002; break; + case ImGuiKey_RightShift: mask = 0x0004; break; + case ImGuiKey_LeftSuper: mask = 0x0008; break; + case ImGuiKey_RightSuper: mask = 0x0010; break; + case ImGuiKey_LeftAlt: mask = 0x0020; break; + case ImGuiKey_RightAlt: mask = 0x0040; break; + default: + return io.WantCaptureKeyboard; + } + io.AddKeyEvent(key, (modifier_flags & mask) != 0); + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) + } + + return io.WantCaptureKeyboard; + } + + return false; +} + bool ImGuiImplMacOS::HandleOSXEvent(NSEvent *_Nonnull event, NSView *_Nonnull view) { std::lock_guard Lock(m_Mtx); From 244e7bc5f71de6aebb728d9d266e3ccc21d7d77c Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Sun, 13 Jul 2025 23:25:09 +0200 Subject: [PATCH 07/11] cleanup --- Imgui/CMakeLists.txt | 8 +- ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h | 24 -- .../imgui_v1.85/imgui_impl_osx_v1.85.mm | 378 ------------------ 3 files changed, 2 insertions(+), 408 deletions(-) delete mode 100644 ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h delete mode 100644 ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm diff --git a/Imgui/CMakeLists.txt b/Imgui/CMakeLists.txt index 2d4cb23c..711bc15f 100644 --- a/Imgui/CMakeLists.txt +++ b/Imgui/CMakeLists.txt @@ -84,12 +84,8 @@ else() list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.cpp) list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.h) elseif(PLATFORM_MACOS) - set(DEAR_IMGUI_OSX_SOURCE - ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm - ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h - ) - target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_OSX_SOURCE}) - source_group(TREE ${DILIGENT_DEAR_IMGUI_PATH}/backends PREFIX "dear_imgui_osx" FILES ${DEAR_IMGUI_OSX_SOURCE}) + list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm) + list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h) target_link_libraries(Diligent-Imgui PRIVATE "-framework GameController") endif() diff --git a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h deleted file mode 100644 index e4c1d04a..00000000 --- a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h +++ /dev/null @@ -1,24 +0,0 @@ -// dear imgui: Platform Backend for OSX / Cocoa -// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..) -// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac. - -// Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -#include "imgui.h" // IMGUI_IMPL_API - -@class NSEvent; -@class NSView; - -IMGUI_IMPL_API bool ImGui_ImplOSX_Init(); -IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); -IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view); diff --git a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm deleted file mode 100644 index 0e83476d..00000000 --- a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm +++ /dev/null @@ -1,378 +0,0 @@ -// dear imgui: Platform Backend for OSX / Cocoa -// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..) -// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac. - -// Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -#include "imgui.h" -#include "imgui_impl_osx_v1.85.h" -#import -#include - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. -// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. -// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. -// 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. -// 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. -// 2020-10-28: Inputs: Added a fix for handling keypad-enter key. -// 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap". -// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. -// 2019-10-11: Inputs: Fix using Backspace key. -// 2019-07-21: Re-added clipboard handlers as they are not enabled by default in core imgui.cpp (reverted 2019-05-18 change). -// 2019-05-28: Inputs: Added mouse cursor shape and visibility support. -// 2019-05-18: Misc: Removed clipboard handlers as they are now supported by core imgui.cpp. -// 2019-05-11: Inputs: Don't filter character values before calling AddInputCharacter() apart from 0xF700..0xFFFF range. -// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. -// 2018-07-07: Initial version. - -@class ImFocusObserver; - -// Data -static double g_Time = 0.0; -static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_MouseCursorHidden = false; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; -static ImFocusObserver* g_FocusObserver = NULL; - -// Undocumented methods for creating cursors. -@interface NSCursor() -+ (id)_windowResizeNorthWestSouthEastCursor; -+ (id)_windowResizeNorthEastSouthWestCursor; -+ (id)_windowResizeNorthSouthCursor; -+ (id)_windowResizeEastWestCursor; -@end - -static CFTimeInterval GetMachAbsoluteTimeInSeconds() -{ - return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9);; -} - -static void resetKeys() -{ - ImGuiIO& io = ImGui::GetIO(); - memset(io.KeysDown, 0, sizeof(io.KeysDown)); - io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; -} - -@interface ImFocusObserver : NSObject - -- (void)onApplicationBecomeActive:(NSNotification*)aNotification; -- (void)onApplicationBecomeInactive:(NSNotification*)aNotification; - -@end - -@implementation ImFocusObserver - -- (void)onApplicationBecomeActive:(NSNotification*)aNotification -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddFocusEvent(true); -} - -- (void)onApplicationBecomeInactive:(NSNotification*)aNotification -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddFocusEvent(false); - - // Unfocused applications do not receive input events, therefore we must manually - // release any pressed keys when application loses focus, otherwise they would remain - // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832 - resetKeys(); -} - -@end - -// Functions -bool ImGui_ImplOSX_Init() -{ - ImGuiIO& io = ImGui::GetIO(); - - // Setup backend capabilities flags - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) - io.BackendPlatformName = "imgui_impl_osx"; - - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. - const int offset_for_function_keys = 256 - 0xF700; - io.KeyMap[ImGuiKey_Tab] = '\t'; - io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Backspace] = 127; - io.KeyMap[ImGuiKey_Space] = 32; - io.KeyMap[ImGuiKey_Enter] = 13; - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeyPadEnter] = 3; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - // Load cursors. Some of them are undocumented. - g_MouseCursorHidden = false; - g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; - g_MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor]; - g_MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; - - // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled - // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. - // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. - io.SetClipboardTextFn = [](void*, const char* str) -> void - { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; - }; - - io.GetClipboardTextFn = [](void*) -> const char* - { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; - if (![available isEqualToString:NSPasteboardTypeString]) - return NULL; - - NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; - if (string == nil) - return NULL; - - const char* string_c = (const char*)[string UTF8String]; - size_t string_len = strlen(string_c); - static ImVector s_clipboard; - s_clipboard.resize((int)string_len + 1); - strcpy(s_clipboard.Data, string_c); - return s_clipboard.Data; - }; - - g_FocusObserver = [[ImFocusObserver alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver - selector:@selector(onApplicationBecomeActive:) - name:NSApplicationDidBecomeActiveNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver - selector:@selector(onApplicationBecomeInactive:) - name:NSApplicationDidResignActiveNotification - object:nil]; - - return true; -} - -void ImGui_ImplOSX_Shutdown() -{ - g_FocusObserver = NULL; -} - -static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() -{ - // Update buttons - ImGuiIO& io = ImGui::GetIO(); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || g_MouseDown[i]; - g_MouseJustPressed[i] = false; - } - - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) - return; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - if (!g_MouseCursorHidden) - { - g_MouseCursorHidden = true; - [NSCursor hide]; - } - } - else - { - // Show OS mouse cursor - [g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; - if (g_MouseCursorHidden) - { - g_MouseCursorHidden = false; - [NSCursor unhide]; - } - } -} - -void ImGui_ImplOSX_NewFrame(NSView* view) -{ - // Setup display size - ImGuiIO& io = ImGui::GetIO(); - if (view) - { - const float dpi = (float)[view.window backingScaleFactor]; - io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height); - io.DisplayFramebufferScale = ImVec2(dpi, dpi); - } - - // Setup time step - if (g_Time == 0.0) - { - g_Time = GetMachAbsoluteTimeInSeconds(); - } - double current_time = GetMachAbsoluteTimeInSeconds(); - io.DeltaTime = (float)(current_time - g_Time); - g_Time = current_time; - - ImGui_ImplOSX_UpdateMouseCursorAndButtons(); -} - -static int mapCharacterToKey(int c) -{ - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - if (c == 25) // SHIFT+TAB -> TAB - return 9; - if (c >= 0 && c < 256) - return c; - if (c >= 0xF700 && c < 0xF700 + 256) - return c - 0xF700 + 256; - return -1; -} - -bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) -{ - ImGuiIO& io = ImGui::GetIO(); - - if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) - { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = g_MouseJustPressed[button] = true; - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) - { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = false; - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) - { - NSPoint mousePoint = event.locationInWindow; - mousePoint = [view convertPoint:mousePoint fromView:nil]; - mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); - io.MousePos = ImVec2((float)mousePoint.x, (float)mousePoint.y); - } - - if (event.type == NSEventTypeScrollWheel) - { - double wheel_dx = 0.0; - double wheel_dy = 0.0; - - #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) - { - wheel_dx = [event scrollingDeltaX]; - wheel_dy = [event scrollingDeltaY]; - if ([event hasPreciseScrollingDeltas]) - { - wheel_dx *= 0.1; - wheel_dy *= 0.1; - } - } - else - #endif // MAC_OS_X_VERSION_MAX_ALLOWED - { - wheel_dx = [event deltaX]; - wheel_dy = [event deltaY]; - } - - if (fabs(wheel_dx) > 0.0) - io.MouseWheelH += (float)wheel_dx * 0.1f; - if (fabs(wheel_dy) > 0.0) - io.MouseWheel += (float)wheel_dy * 0.1f; - return io.WantCaptureMouse; - } - - // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. - if (event.type == NSEventTypeKeyDown) - { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) - { - int c = [str characterAtIndex:i]; - if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) - io.AddInputCharacter((unsigned int)c); - - // We must reset in case we're pressing a sequence of special keys while keeping the command pressed - int key = mapCharacterToKey(c); - if (key != -1 && key < 256 && !io.KeySuper) - resetKeys(); - if (key != -1) - io.KeysDown[key] = true; - } - return io.WantCaptureKeyboard; - } - - if (event.type == NSEventTypeKeyUp) - { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) - { - int c = [str characterAtIndex:i]; - int key = mapCharacterToKey(c); - if (key != -1) - io.KeysDown[key] = false; - } - return io.WantCaptureKeyboard; - } - - if (event.type == NSEventTypeFlagsChanged) - { - unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - - bool oldKeyCtrl = io.KeyCtrl; - bool oldKeyShift = io.KeyShift; - bool oldKeyAlt = io.KeyAlt; - bool oldKeySuper = io.KeySuper; - io.KeyCtrl = flags & NSEventModifierFlagControl; - io.KeyShift = flags & NSEventModifierFlagShift; - io.KeyAlt = flags & NSEventModifierFlagOption; - io.KeySuper = flags & NSEventModifierFlagCommand; - - // We must reset them as we will not receive any keyUp event if they where pressed with a modifier - if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) - resetKeys(); - return io.WantCaptureKeyboard; - } - - return false; -} From 3ae56245edbc3ec275af1cf0f2f48cf559b3eec6 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Fri, 25 Jul 2025 18:08:50 +0200 Subject: [PATCH 08/11] removed redundant AddRefs code readability --- Imgui/src/ImGuiDiligentRenderer.cpp | 31 +++++++++-------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/Imgui/src/ImGuiDiligentRenderer.cpp b/Imgui/src/ImGuiDiligentRenderer.cpp index 2afbef8f..f9d8f886 100644 --- a/Imgui/src/ImGuiDiligentRenderer.cpp +++ b/Imgui/src/ImGuiDiligentRenderer.cpp @@ -848,9 +848,6 @@ void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* t { auto* backend = static_cast(tex->BackendUserData); - // ---------------------------------------------------------------- - // 1) CREATE - // ---------------------------------------------------------------- if (tex->Status == ImTextureStatus_WantCreate) { IM_ASSERT(backend == nullptr && tex->TexID == ImTextureID_Invalid); @@ -871,9 +868,7 @@ void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* t ITexture* pTexture = nullptr; m_pDevice->CreateTexture(desc, &init, &pTexture); - pTexture->AddRef(); ITextureView* ptexView = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); - ptexView->AddRef(); // store texture view and texture pointers inside imgui and set texture state to ok tex->SetTexID(reinterpret_cast(ptexView)); @@ -881,15 +876,10 @@ void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* t tex->SetStatus(ImTextureStatus_OK); return; } - - // ---------------------------------------------------------------- - // 2) UPDATE - // ---------------------------------------------------------------- - if (tex->Status == ImTextureStatus_WantUpdates && backend != nullptr) + else if (tex->Status == ImTextureStatus_WantUpdates && backend != nullptr) { Box dstBox{Uint32(tex->UpdateRect.x), Uint32(tex->UpdateRect.x + tex->UpdateRect.w), - Uint32(tex->UpdateRect.y), Uint32(tex->UpdateRect.y + tex->UpdateRect.h), - 0, 1}; // Z range + Uint32(tex->UpdateRect.y), Uint32(tex->UpdateRect.y + tex->UpdateRect.h)}; TextureSubResData SubresData; SubresData.pData = tex->GetPixelsAt(tex->UpdateRect.x, tex->UpdateRect.y); @@ -899,11 +889,7 @@ void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* t tex->SetStatus(ImTextureStatus_OK); return; } - - // ---------------------------------------------------------------- - // 3) DESTROY - // ---------------------------------------------------------------- - if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) { DestroyTexture(tex); } @@ -911,11 +897,6 @@ void ImGuiDiligentRenderer::UpdateTexture(IDeviceContext* pCtx, ImTextureData* t void ImGuiDiligentRenderer::DestroyTexture(ImTextureData* tex) { - if (ITextureView* pTextureView = reinterpret_cast(tex->GetTexID())) - { - pTextureView->Release(); - } - if (auto* pTexture = static_cast(tex->BackendUserData)) { pTexture->Release(); @@ -932,9 +913,15 @@ void ImGuiDiligentRenderer::RenderDrawData(IDeviceContext* pCtx, ImDrawData* pDr // Handle requested texture creates/updates/destroys ----------------- if (pDrawData->Textures != nullptr) + { for (ImTextureData* tex : *pDrawData->Textures) + { if (tex->Status != ImTextureStatus_OK) + { UpdateTexture(pCtx, tex); + } + } + } // Avoid rendering when minimized if (pDrawData->DisplaySize.x <= 0.0f || pDrawData->DisplaySize.y <= 0.0f || pDrawData->CmdLists.empty()) From 3be81eb3e3dc94d9be9d12d57595489563400616 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Fri, 25 Jul 2025 21:49:35 +0200 Subject: [PATCH 09/11] Revert "cleanup" This reverts commit 244e7bc5f71de6aebb728d9d266e3ccc21d7d77c. --- Imgui/CMakeLists.txt | 8 +- ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h | 24 ++ .../imgui_v1.85/imgui_impl_osx_v1.85.mm | 378 ++++++++++++++++++ 3 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h create mode 100644 ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm diff --git a/Imgui/CMakeLists.txt b/Imgui/CMakeLists.txt index 711bc15f..2d4cb23c 100644 --- a/Imgui/CMakeLists.txt +++ b/Imgui/CMakeLists.txt @@ -84,8 +84,12 @@ else() list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.cpp) list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.h) elseif(PLATFORM_MACOS) - list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm) - list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h) + set(DEAR_IMGUI_OSX_SOURCE + ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm + ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h + ) + target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_OSX_SOURCE}) + source_group(TREE ${DILIGENT_DEAR_IMGUI_PATH}/backends PREFIX "dear_imgui_osx" FILES ${DEAR_IMGUI_OSX_SOURCE}) target_link_libraries(Diligent-Imgui PRIVATE "-framework GameController") endif() diff --git a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h new file mode 100644 index 00000000..e4c1d04a --- /dev/null +++ b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h @@ -0,0 +1,24 @@ +// dear imgui: Platform Backend for OSX / Cocoa +// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..) +// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac. + +// Implemented features: +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// Issues: +// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#include "imgui.h" // IMGUI_IMPL_API + +@class NSEvent; +@class NSView; + +IMGUI_IMPL_API bool ImGui_ImplOSX_Init(); +IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); +IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view); diff --git a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm new file mode 100644 index 00000000..0e83476d --- /dev/null +++ b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm @@ -0,0 +1,378 @@ +// dear imgui: Platform Backend for OSX / Cocoa +// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..) +// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac. + +// Implemented features: +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// Issues: +// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#include "imgui.h" +#include "imgui_impl_osx_v1.85.h" +#import +#include + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. +// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. +// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. +// 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. +// 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. +// 2020-10-28: Inputs: Added a fix for handling keypad-enter key. +// 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap". +// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. +// 2019-10-11: Inputs: Fix using Backspace key. +// 2019-07-21: Re-added clipboard handlers as they are not enabled by default in core imgui.cpp (reverted 2019-05-18 change). +// 2019-05-28: Inputs: Added mouse cursor shape and visibility support. +// 2019-05-18: Misc: Removed clipboard handlers as they are now supported by core imgui.cpp. +// 2019-05-11: Inputs: Don't filter character values before calling AddInputCharacter() apart from 0xF700..0xFFFF range. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. +// 2018-07-07: Initial version. + +@class ImFocusObserver; + +// Data +static double g_Time = 0.0; +static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; +static bool g_MouseCursorHidden = false; +static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; +static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; +static ImFocusObserver* g_FocusObserver = NULL; + +// Undocumented methods for creating cursors. +@interface NSCursor() ++ (id)_windowResizeNorthWestSouthEastCursor; ++ (id)_windowResizeNorthEastSouthWestCursor; ++ (id)_windowResizeNorthSouthCursor; ++ (id)_windowResizeEastWestCursor; +@end + +static CFTimeInterval GetMachAbsoluteTimeInSeconds() +{ + return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9);; +} + +static void resetKeys() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.KeysDown, 0, sizeof(io.KeysDown)); + io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; +} + +@interface ImFocusObserver : NSObject + +- (void)onApplicationBecomeActive:(NSNotification*)aNotification; +- (void)onApplicationBecomeInactive:(NSNotification*)aNotification; + +@end + +@implementation ImFocusObserver + +- (void)onApplicationBecomeActive:(NSNotification*)aNotification +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(true); +} + +- (void)onApplicationBecomeInactive:(NSNotification*)aNotification +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(false); + + // Unfocused applications do not receive input events, therefore we must manually + // release any pressed keys when application loses focus, otherwise they would remain + // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832 + resetKeys(); +} + +@end + +// Functions +bool ImGui_ImplOSX_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + + // Setup backend capabilities flags + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendPlatformName = "imgui_impl_osx"; + + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. + const int offset_for_function_keys = 256 - 0xF700; + io.KeyMap[ImGuiKey_Tab] = '\t'; + io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; + io.KeyMap[ImGuiKey_Backspace] = 127; + io.KeyMap[ImGuiKey_Space] = 32; + io.KeyMap[ImGuiKey_Enter] = 13; + io.KeyMap[ImGuiKey_Escape] = 27; + io.KeyMap[ImGuiKey_KeyPadEnter] = 3; + io.KeyMap[ImGuiKey_A] = 'A'; + io.KeyMap[ImGuiKey_C] = 'C'; + io.KeyMap[ImGuiKey_V] = 'V'; + io.KeyMap[ImGuiKey_X] = 'X'; + io.KeyMap[ImGuiKey_Y] = 'Y'; + io.KeyMap[ImGuiKey_Z] = 'Z'; + + // Load cursors. Some of them are undocumented. + g_MouseCursorHidden = false; + g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; + g_MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor]; + g_MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; + g_MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; + + // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled + // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. + // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. + io.SetClipboardTextFn = [](void*, const char* str) -> void + { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; + }; + + io.GetClipboardTextFn = [](void*) -> const char* + { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; + if (![available isEqualToString:NSPasteboardTypeString]) + return NULL; + + NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; + if (string == nil) + return NULL; + + const char* string_c = (const char*)[string UTF8String]; + size_t string_len = strlen(string_c); + static ImVector s_clipboard; + s_clipboard.resize((int)string_len + 1); + strcpy(s_clipboard.Data, string_c); + return s_clipboard.Data; + }; + + g_FocusObserver = [[ImFocusObserver alloc] init]; + [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver + selector:@selector(onApplicationBecomeActive:) + name:NSApplicationDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver + selector:@selector(onApplicationBecomeInactive:) + name:NSApplicationDidResignActiveNotification + object:nil]; + + return true; +} + +void ImGui_ImplOSX_Shutdown() +{ + g_FocusObserver = NULL; +} + +static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() +{ + // Update buttons + ImGuiIO& io = ImGui::GetIO(); + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[i] = g_MouseJustPressed[i] || g_MouseDown[i]; + g_MouseJustPressed[i] = false; + } + + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + if (!g_MouseCursorHidden) + { + g_MouseCursorHidden = true; + [NSCursor hide]; + } + } + else + { + // Show OS mouse cursor + [g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; + if (g_MouseCursorHidden) + { + g_MouseCursorHidden = false; + [NSCursor unhide]; + } + } +} + +void ImGui_ImplOSX_NewFrame(NSView* view) +{ + // Setup display size + ImGuiIO& io = ImGui::GetIO(); + if (view) + { + const float dpi = (float)[view.window backingScaleFactor]; + io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height); + io.DisplayFramebufferScale = ImVec2(dpi, dpi); + } + + // Setup time step + if (g_Time == 0.0) + { + g_Time = GetMachAbsoluteTimeInSeconds(); + } + double current_time = GetMachAbsoluteTimeInSeconds(); + io.DeltaTime = (float)(current_time - g_Time); + g_Time = current_time; + + ImGui_ImplOSX_UpdateMouseCursorAndButtons(); +} + +static int mapCharacterToKey(int c) +{ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + if (c == 25) // SHIFT+TAB -> TAB + return 9; + if (c >= 0 && c < 256) + return c; + if (c >= 0xF700 && c < 0xF700 + 256) + return c - 0xF700 + 256; + return -1; +} + +bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) +{ + ImGuiIO& io = ImGui::GetIO(); + + if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) + { + int button = (int)[event buttonNumber]; + if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) + g_MouseDown[button] = g_MouseJustPressed[button] = true; + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) + { + int button = (int)[event buttonNumber]; + if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) + g_MouseDown[button] = false; + return io.WantCaptureMouse; + } + + if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) + { + NSPoint mousePoint = event.locationInWindow; + mousePoint = [view convertPoint:mousePoint fromView:nil]; + mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); + io.MousePos = ImVec2((float)mousePoint.x, (float)mousePoint.y); + } + + if (event.type == NSEventTypeScrollWheel) + { + double wheel_dx = 0.0; + double wheel_dy = 0.0; + + #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + { + wheel_dx = [event scrollingDeltaX]; + wheel_dy = [event scrollingDeltaY]; + if ([event hasPreciseScrollingDeltas]) + { + wheel_dx *= 0.1; + wheel_dy *= 0.1; + } + } + else + #endif // MAC_OS_X_VERSION_MAX_ALLOWED + { + wheel_dx = [event deltaX]; + wheel_dy = [event deltaY]; + } + + if (fabs(wheel_dx) > 0.0) + io.MouseWheelH += (float)wheel_dx * 0.1f; + if (fabs(wheel_dy) > 0.0) + io.MouseWheel += (float)wheel_dy * 0.1f; + return io.WantCaptureMouse; + } + + // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. + if (event.type == NSEventTypeKeyDown) + { + NSString* str = [event characters]; + NSUInteger len = [str length]; + for (NSUInteger i = 0; i < len; i++) + { + int c = [str characterAtIndex:i]; + if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) + io.AddInputCharacter((unsigned int)c); + + // We must reset in case we're pressing a sequence of special keys while keeping the command pressed + int key = mapCharacterToKey(c); + if (key != -1 && key < 256 && !io.KeySuper) + resetKeys(); + if (key != -1) + io.KeysDown[key] = true; + } + return io.WantCaptureKeyboard; + } + + if (event.type == NSEventTypeKeyUp) + { + NSString* str = [event characters]; + NSUInteger len = [str length]; + for (NSUInteger i = 0; i < len; i++) + { + int c = [str characterAtIndex:i]; + int key = mapCharacterToKey(c); + if (key != -1) + io.KeysDown[key] = false; + } + return io.WantCaptureKeyboard; + } + + if (event.type == NSEventTypeFlagsChanged) + { + unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + + bool oldKeyCtrl = io.KeyCtrl; + bool oldKeyShift = io.KeyShift; + bool oldKeyAlt = io.KeyAlt; + bool oldKeySuper = io.KeySuper; + io.KeyCtrl = flags & NSEventModifierFlagControl; + io.KeyShift = flags & NSEventModifierFlagShift; + io.KeyAlt = flags & NSEventModifierFlagOption; + io.KeySuper = flags & NSEventModifierFlagCommand; + + // We must reset them as we will not receive any keyUp event if they where pressed with a modifier + if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) + resetKeys(); + return io.WantCaptureKeyboard; + } + + return false; +} From d8e2834ffe4df73fd5ed0d853363dfaac1e02a0d Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Fri, 25 Jul 2025 21:49:51 +0200 Subject: [PATCH 10/11] Revert "working on macos" This reverts commit 3ba093c6bd2f5dd4b1f4e8c71edce5bf627c3a11. --- Imgui/CMakeLists.txt | 11 ++- Imgui/src/ImGuiImplMacOS.mm | 167 +----------------------------------- 2 files changed, 7 insertions(+), 171 deletions(-) diff --git a/Imgui/CMakeLists.txt b/Imgui/CMakeLists.txt index 2d4cb23c..65ac1eb0 100644 --- a/Imgui/CMakeLists.txt +++ b/Imgui/CMakeLists.txt @@ -84,13 +84,12 @@ else() list(APPEND DEAR_IMGUI_SOURCE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.cpp) list(APPEND DEAR_IMGUI_INCLUDE ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_win32.h) elseif(PLATFORM_MACOS) - set(DEAR_IMGUI_OSX_SOURCE - ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.mm - ${DILIGENT_DEAR_IMGUI_PATH}/backends/imgui_impl_osx.h + set(DEAR_IMGUI_V185_SOURCE + ../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm + ../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h ) - target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_OSX_SOURCE}) - source_group(TREE ${DILIGENT_DEAR_IMGUI_PATH}/backends PREFIX "dear_imgui_osx" FILES ${DEAR_IMGUI_OSX_SOURCE}) - target_link_libraries(Diligent-Imgui PRIVATE "-framework GameController") + target_sources(Diligent-Imgui PRIVATE ${DEAR_IMGUI_V185_SOURCE}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/imgui_v1.85 PREFIX "dear_imgui_v1.85" FILES ${DEAR_IMGUI_V185_SOURCE}) endif() target_sources(Diligent-Imgui PRIVATE diff --git a/Imgui/src/ImGuiImplMacOS.mm b/Imgui/src/ImGuiImplMacOS.mm index 97fb3bbb..018fc4a3 100644 --- a/Imgui/src/ImGuiImplMacOS.mm +++ b/Imgui/src/ImGuiImplMacOS.mm @@ -23,11 +23,9 @@ #include "imgui.h" #include "ImGuiImplMacOS.hpp" -#include "backends/imgui_impl_osx.h" +#include "../../ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.h" #import -ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code); - namespace Diligent { @@ -39,7 +37,7 @@ ImGuiImplMacOS::ImGuiImplMacOS(const ImGuiDiligentCreateInfo& CI, void* _Nullable view) : ImGuiImplDiligent{CI} { - ImGui_ImplOSX_Init((NSView*)view); + ImGui_ImplOSX_Init(); ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "Diligent-ImGuiImplMacOS"; @@ -63,167 +61,6 @@ ImGuiImplDiligent::NewFrame(RenderSurfaceWidth, RenderSurfaceHeight, SurfacePreTransform); } - -// Must only be called for a mouse event, otherwise an exception occurs -// (Note that NSEventTypeScrollWheel is considered "other input". Oddly enough an exception does not occur with it, but the value will sometimes be wrong!) -static ImGuiMouseSource GetMouseSource(NSEvent* event) -{ - switch (event.subtype) - { - case NSEventSubtypeTabletPoint: - return ImGuiMouseSource_Pen; - // macOS considers input from relative touch devices (like the trackpad or Apple Magic Mouse) to be touch input. - // This doesn't really make sense for Dear ImGui, which expects absolute touch devices only. - // There does not seem to be a simple way to disambiguate things here so we consider NSEventSubtypeTouch events to always come from mice. - // See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html#//apple_ref/doc/uid/10000060i-CH13-SW24 - //case NSEventSubtypeTouch: - // return ImGuiMouseSource_TouchScreen; - case NSEventSubtypeMouseEvent: - default: - return ImGuiMouseSource_Mouse; - } -} - -static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) -{ - // Only process events from the window containing ImGui view - if (event.window != view.window) - return false; - ImGuiIO& io = ImGui::GetIO(); - - if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) - { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < ImGuiMouseButton_COUNT) - { - io.AddMouseSourceEvent(GetMouseSource(event)); - io.AddMouseButtonEvent(button, true); - } - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) - { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < ImGuiMouseButton_COUNT) - { - io.AddMouseSourceEvent(GetMouseSource(event)); - io.AddMouseButtonEvent(button, false); - } - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) - { - NSPoint mousePoint = event.locationInWindow; - if (event.window == nil) - mousePoint = [[view window] convertPointFromScreen:mousePoint]; - mousePoint = [view convertPoint:mousePoint fromView:nil]; - if ([view isFlipped]) - mousePoint = NSMakePoint(mousePoint.x, mousePoint.y); - else - mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); - io.AddMouseSourceEvent(GetMouseSource(event)); - io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y); - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeScrollWheel) - { - // Ignore canceled events. - // - // From macOS 12.1, scrolling with two fingers and then decelerating - // by tapping two fingers results in two events appearing: - // - // 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps - // two fingers to decelerate or stop the scroll events. - // - // 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the - // two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and - // scrollingDeltaY. When these are added to the current x and y positions of the scrolling view, - // it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here. - if (event.phase == NSEventPhaseCancelled) - return false; - - double wheel_dx = 0.0; - double wheel_dy = 0.0; - - #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) - { - wheel_dx = [event scrollingDeltaX]; - wheel_dy = [event scrollingDeltaY]; - if ([event hasPreciseScrollingDeltas]) - { - wheel_dx *= 0.01; - wheel_dy *= 0.01; - } - } - else - #endif // MAC_OS_X_VERSION_MAX_ALLOWED - { - wheel_dx = [event deltaX] * 0.1; - wheel_dy = [event deltaY] * 0.1; - } - if (wheel_dx != 0.0 || wheel_dy != 0.0) - io.AddMouseWheelEvent((float)wheel_dx, (float)wheel_dy); - - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) - { - if ([event isARepeat]) - return io.WantCaptureKeyboard; - - int key_code = (int)[event keyCode]; - ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); - io.AddKeyEvent(key, event.type == NSEventTypeKeyDown); - io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) - - return io.WantCaptureKeyboard; - } - - if (event.type == NSEventTypeFlagsChanged) - { - unsigned short key_code = [event keyCode]; - NSEventModifierFlags modifier_flags = [event modifierFlags]; - - io.AddKeyEvent(ImGuiMod_Shift, (modifier_flags & NSEventModifierFlagShift) != 0); - io.AddKeyEvent(ImGuiMod_Ctrl, (modifier_flags & NSEventModifierFlagControl) != 0); - io.AddKeyEvent(ImGuiMod_Alt, (modifier_flags & NSEventModifierFlagOption) != 0); - io.AddKeyEvent(ImGuiMod_Super, (modifier_flags & NSEventModifierFlagCommand) != 0); - - ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); - if (key != ImGuiKey_None) - { - // macOS does not generate down/up event for modifiers. We're trying - // to use hardware dependent masks to extract that information. - // 'imgui_mask' is left as a fallback. - NSEventModifierFlags mask = 0; - switch (key) - { - case ImGuiKey_LeftCtrl: mask = 0x0001; break; - case ImGuiKey_RightCtrl: mask = 0x2000; break; - case ImGuiKey_LeftShift: mask = 0x0002; break; - case ImGuiKey_RightShift: mask = 0x0004; break; - case ImGuiKey_LeftSuper: mask = 0x0008; break; - case ImGuiKey_RightSuper: mask = 0x0010; break; - case ImGuiKey_LeftAlt: mask = 0x0020; break; - case ImGuiKey_RightAlt: mask = 0x0040; break; - default: - return io.WantCaptureKeyboard; - } - io.AddKeyEvent(key, (modifier_flags & mask) != 0); - io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) - } - - return io.WantCaptureKeyboard; - } - - return false; -} - bool ImGuiImplMacOS::HandleOSXEvent(NSEvent *_Nonnull event, NSView *_Nonnull view) { std::lock_guard Lock(m_Mtx); From 5aa5c2ab664aa54b0759571c3ea723340e6775c8 Mon Sep 17 00:00:00 2001 From: Maciej Biedrzycki Date: Sat, 26 Jul 2025 18:34:25 +0200 Subject: [PATCH 11/11] updated macos 1.85 backend to be compatible with imgui 1.92.1+ --- .../imgui_v1.85/imgui_impl_osx_v1.85.mm | 584 +++++++++--------- 1 file changed, 279 insertions(+), 305 deletions(-) diff --git a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm index 0e83476d..f0a2ee21 100644 --- a/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm +++ b/ThirdParty/imgui_v1.85/imgui_impl_osx_v1.85.mm @@ -1,378 +1,352 @@ -// dear imgui: Platform Backend for OSX / Cocoa -// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..) -// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac. +// dear imgui: Platform Backend for macOS / Cocoa (legacy threading-friendly) +// ------------------------------------------------------------------------- +// - Based on the 1.85 backend, updated to Dear ImGui >= 1.92 new-IO API +// - Keeps ImGui_ImplOSX_HandleEvent() +// ------------------------------------------------------------------------- -// Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -#include "imgui.h" #include "imgui_impl_osx_v1.85.h" +#import "imgui.h" +#import "imgui_internal.h" #import +#import #include -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. -// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. -// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. -// 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. -// 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. -// 2020-10-28: Inputs: Added a fix for handling keypad-enter key. -// 2020-05-25: Inputs: Added a fix for missing trackpad clicks when done with "soft tap". -// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. -// 2019-10-11: Inputs: Fix using Backspace key. -// 2019-07-21: Re-added clipboard handlers as they are not enabled by default in core imgui.cpp (reverted 2019-05-18 change). -// 2019-05-28: Inputs: Added mouse cursor shape and visibility support. -// 2019-05-18: Misc: Removed clipboard handlers as they are now supported by core imgui.cpp. -// 2019-05-11: Inputs: Don't filter character values before calling AddInputCharacter() apart from 0xF700..0xFFFF range. -// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. -// 2018-07-07: Initial version. - -@class ImFocusObserver; +// ---- Undocumented cursor selectors ---------------------------------------- +@interface NSCursor () ++ (NSCursor*)_windowResizeNorthSouthCursor; ++ (NSCursor*)_windowResizeEastWestCursor; ++ (NSCursor*)_windowResizeNorthEastSouthWestCursor; ++ (NSCursor*)_windowResizeNorthWestSouthEastCursor; +@end -// Data -static double g_Time = 0.0; -static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_MouseCursorHidden = false; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; -static ImFocusObserver* g_FocusObserver = NULL; +// --------------------------------------------------------------------------- +// Data & helpers +// --------------------------------------------------------------------------- +static double g_Time = 0.0; +static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; +static bool g_MouseCursorHidden = false; +static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; // only for cursor -// Undocumented methods for creating cursors. -@interface NSCursor() -+ (id)_windowResizeNorthWestSouthEastCursor; -+ (id)_windowResizeNorthEastSouthWestCursor; -+ (id)_windowResizeNorthSouthCursor; -+ (id)_windowResizeEastWestCursor; -@end +static inline double AbsoluteSeconds() +{ return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9; } -static CFTimeInterval GetMachAbsoluteTimeInSeconds() +static ImGuiKey KeycodeToImGuiKey(unsigned key_code) { - return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9);; + switch (key_code) + { + case kVK_ANSI_A: return ImGuiKey_A; + case kVK_ANSI_S: return ImGuiKey_S; + case kVK_ANSI_D: return ImGuiKey_D; + case kVK_ANSI_F: return ImGuiKey_F; + case kVK_ANSI_H: return ImGuiKey_H; + case kVK_ANSI_G: return ImGuiKey_G; + case kVK_ANSI_Z: return ImGuiKey_Z; + case kVK_ANSI_X: return ImGuiKey_X; + case kVK_ANSI_C: return ImGuiKey_C; + case kVK_ANSI_V: return ImGuiKey_V; + case kVK_ANSI_B: return ImGuiKey_B; + case kVK_ANSI_Q: return ImGuiKey_Q; + case kVK_ANSI_W: return ImGuiKey_W; + case kVK_ANSI_E: return ImGuiKey_E; + case kVK_ANSI_R: return ImGuiKey_R; + case kVK_ANSI_Y: return ImGuiKey_Y; + case kVK_ANSI_T: return ImGuiKey_T; + case kVK_ANSI_1: return ImGuiKey_1; + case kVK_ANSI_2: return ImGuiKey_2; + case kVK_ANSI_3: return ImGuiKey_3; + case kVK_ANSI_4: return ImGuiKey_4; + case kVK_ANSI_6: return ImGuiKey_6; + case kVK_ANSI_5: return ImGuiKey_5; + case kVK_ANSI_Equal: return ImGuiKey_Equal; + case kVK_ANSI_9: return ImGuiKey_9; + case kVK_ANSI_7: return ImGuiKey_7; + case kVK_ANSI_Minus: return ImGuiKey_Minus; + case kVK_ANSI_8: return ImGuiKey_8; + case kVK_ANSI_0: return ImGuiKey_0; + case kVK_ANSI_RightBracket: return ImGuiKey_RightBracket; + case kVK_ANSI_O: return ImGuiKey_O; + case kVK_ANSI_U: return ImGuiKey_U; + case kVK_ANSI_LeftBracket: return ImGuiKey_LeftBracket; + case kVK_ANSI_I: return ImGuiKey_I; + case kVK_ANSI_P: return ImGuiKey_P; + case kVK_ANSI_L: return ImGuiKey_L; + case kVK_ANSI_J: return ImGuiKey_J; + case kVK_ANSI_Quote: return ImGuiKey_Apostrophe; + case kVK_ANSI_K: return ImGuiKey_K; + case kVK_ANSI_Semicolon: return ImGuiKey_Semicolon; + case kVK_ANSI_Backslash: return ImGuiKey_Backslash; + case kVK_ANSI_Comma: return ImGuiKey_Comma; + case kVK_ANSI_Slash: return ImGuiKey_Slash; + case kVK_ANSI_N: return ImGuiKey_N; + case kVK_ANSI_M: return ImGuiKey_M; + case kVK_ANSI_Period: return ImGuiKey_Period; + case kVK_ANSI_Grave: return ImGuiKey_GraveAccent; + case kVK_ANSI_KeypadDecimal: return ImGuiKey_KeypadDecimal; + case kVK_ANSI_KeypadMultiply: return ImGuiKey_KeypadMultiply; + case kVK_ANSI_KeypadPlus: return ImGuiKey_KeypadAdd; + case kVK_ANSI_KeypadClear: return ImGuiKey_NumLock; + case kVK_ANSI_KeypadDivide: return ImGuiKey_KeypadDivide; + case kVK_ANSI_KeypadEnter: return ImGuiKey_KeypadEnter; + case kVK_ANSI_KeypadMinus: return ImGuiKey_KeypadSubtract; + case kVK_ANSI_KeypadEquals: return ImGuiKey_KeypadEqual; + case kVK_ANSI_Keypad0: return ImGuiKey_Keypad0; + case kVK_ANSI_Keypad1: return ImGuiKey_Keypad1; + case kVK_ANSI_Keypad2: return ImGuiKey_Keypad2; + case kVK_ANSI_Keypad3: return ImGuiKey_Keypad3; + case kVK_ANSI_Keypad4: return ImGuiKey_Keypad4; + case kVK_ANSI_Keypad5: return ImGuiKey_Keypad5; + case kVK_ANSI_Keypad6: return ImGuiKey_Keypad6; + case kVK_ANSI_Keypad7: return ImGuiKey_Keypad7; + case kVK_ANSI_Keypad8: return ImGuiKey_Keypad8; + case kVK_ANSI_Keypad9: return ImGuiKey_Keypad9; + case kVK_Return: return ImGuiKey_Enter; + case kVK_Tab: return ImGuiKey_Tab; + case kVK_Space: return ImGuiKey_Space; + case kVK_Delete: return ImGuiKey_Backspace; + case kVK_Escape: return ImGuiKey_Escape; + case kVK_CapsLock: return ImGuiKey_CapsLock; + case kVK_Control: return ImGuiKey_LeftCtrl; + case kVK_Shift: return ImGuiKey_LeftShift; + case kVK_Option: return ImGuiKey_LeftAlt; + case kVK_Command: return ImGuiKey_LeftSuper; + case kVK_RightControl: return ImGuiKey_RightCtrl; + case kVK_RightShift: return ImGuiKey_RightShift; + case kVK_RightOption: return ImGuiKey_RightAlt; + case kVK_RightCommand: return ImGuiKey_RightSuper; +// case kVK_Function: return ImGuiKey_; +// case kVK_VolumeUp: return ImGuiKey_; +// case kVK_VolumeDown: return ImGuiKey_; +// case kVK_Mute: return ImGuiKey_; + case kVK_F1: return ImGuiKey_F1; + case kVK_F2: return ImGuiKey_F2; + case kVK_F3: return ImGuiKey_F3; + case kVK_F4: return ImGuiKey_F4; + case kVK_F5: return ImGuiKey_F5; + case kVK_F6: return ImGuiKey_F6; + case kVK_F7: return ImGuiKey_F7; + case kVK_F8: return ImGuiKey_F8; + case kVK_F9: return ImGuiKey_F9; + case kVK_F10: return ImGuiKey_F10; + case kVK_F11: return ImGuiKey_F11; + case kVK_F12: return ImGuiKey_F12; + case kVK_F13: return ImGuiKey_F13; + case kVK_F14: return ImGuiKey_F14; + case kVK_F15: return ImGuiKey_F15; + case kVK_F16: return ImGuiKey_F16; + case kVK_F17: return ImGuiKey_F17; + case kVK_F18: return ImGuiKey_F18; + case kVK_F19: return ImGuiKey_F19; + case kVK_F20: return ImGuiKey_F20; + case 0x6E: return ImGuiKey_Menu; + case kVK_Help: return ImGuiKey_Insert; + case kVK_Home: return ImGuiKey_Home; + case kVK_PageUp: return ImGuiKey_PageUp; + case kVK_ForwardDelete: return ImGuiKey_Delete; + case kVK_End: return ImGuiKey_End; + case kVK_PageDown: return ImGuiKey_PageDown; + case kVK_LeftArrow: return ImGuiKey_LeftArrow; + case kVK_RightArrow: return ImGuiKey_RightArrow; + case kVK_DownArrow: return ImGuiKey_DownArrow; + case kVK_UpArrow: return ImGuiKey_UpArrow; + default: return ImGuiKey_None; + } } -static void resetKeys() +static void ResetAllKeys() { ImGuiIO& io = ImGui::GetIO(); - memset(io.KeysDown, 0, sizeof(io.KeysDown)); - io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; -} -@interface ImFocusObserver : NSObject + // Only release genuine (non-alias) named keys + for (int k = (int)ImGuiKey_NamedKey_BEGIN; k < (int)ImGuiKey_COUNT; ++k) + { + ImGuiKey key = (ImGuiKey)k; + if (!ImGui::IsAliasKey(key)) // filter out MouseXXX, etc. + io.AddKeyEvent(key, false); + } -- (void)onApplicationBecomeActive:(NSNotification*)aNotification; -- (void)onApplicationBecomeInactive:(NSNotification*)aNotification; + // Explicitly release modifiers + io.AddKeyEvent(ImGuiMod_Ctrl, false); + io.AddKeyEvent(ImGuiMod_Shift, false); + io.AddKeyEvent(ImGuiMod_Alt, false); + io.AddKeyEvent(ImGuiMod_Super, false); +} +// --------------------------------------------------------------------------- +// Focus observer – release stuck keys on deactivate +// --------------------------------------------------------------------------- +@interface ImFocusObserver : NSObject +- (void)appDidBecomeActive:(NSNotification*)n; +- (void)appDidResignActive:(NSNotification*)n; @end - @implementation ImFocusObserver - -- (void)onApplicationBecomeActive:(NSNotification*)aNotification -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddFocusEvent(true); -} - -- (void)onApplicationBecomeInactive:(NSNotification*)aNotification -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddFocusEvent(false); - - // Unfocused applications do not receive input events, therefore we must manually - // release any pressed keys when application loses focus, otherwise they would remain - // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832 - resetKeys(); -} - +- (void)appDidBecomeActive:(NSNotification*)n { ImGui::GetIO().AddFocusEvent(true); } +- (void)appDidResignActive:(NSNotification*)n { ImGui::GetIO().AddFocusEvent(false); ResetAllKeys(); } @end +static ImFocusObserver* g_FocusObserver = nil; -// Functions +// --------------------------------------------------------------------------- +// Backend API +// --------------------------------------------------------------------------- bool ImGui_ImplOSX_Init() { ImGuiIO& io = ImGui::GetIO(); - - // Setup backend capabilities flags - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) - io.BackendPlatformName = "imgui_impl_osx"; - - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. - const int offset_for_function_keys = 256 - 0xF700; - io.KeyMap[ImGuiKey_Tab] = '\t'; - io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Backspace] = 127; - io.KeyMap[ImGuiKey_Space] = 32; - io.KeyMap[ImGuiKey_Enter] = 13; - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeyPadEnter] = 3; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - // Load cursors. Some of them are undocumented. - g_MouseCursorHidden = false; - g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; - g_MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor]; - g_MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; + io.BackendPlatformName = "imgui_impl_osx (1.85 compat, modern IO)"; + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + + // Cursors ---------------------------------------------------------------- + g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; + g_MouseCursors[ImGuiMouseCursor_TextInput] = [NSCursor IBeamCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = [NSCursor closedHandCursor]; + g_MouseCursors[ImGuiMouseCursor_Hand] = [NSCursor pointingHandCursor]; g_MouseCursors[ImGuiMouseCursor_NotAllowed] = [NSCursor operationNotAllowedCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; - - // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled - // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. - // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. - io.SetClipboardTextFn = [](void*, const char* str) -> void + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] + ? [NSCursor _windowResizeNorthSouthCursor] + : [NSCursor resizeUpDownCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] + ? [NSCursor _windowResizeEastWestCursor] + : [NSCursor resizeLeftRightCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] + ? [NSCursor _windowResizeNorthEastSouthWestCursor] + : [NSCursor closedHandCursor]; + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] + ? [NSCursor _windowResizeNorthWestSouthEastCursor] + : [NSCursor closedHandCursor]; + + // Clipboard -------------------------------------------------------------- + io.SetClipboardTextFn = [](void*, const char* s) { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; + NSPasteboard* pb = [NSPasteboard generalPasteboard]; + [pb declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; + [pb setString:[NSString stringWithUTF8String:s] forType:NSPasteboardTypeString]; }; - io.GetClipboardTextFn = [](void*) -> const char* { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; - if (![available isEqualToString:NSPasteboardTypeString]) - return NULL; - - NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; - if (string == nil) - return NULL; - - const char* string_c = (const char*)[string UTF8String]; - size_t string_len = strlen(string_c); - static ImVector s_clipboard; - s_clipboard.resize((int)string_len + 1); - strcpy(s_clipboard.Data, string_c); - return s_clipboard.Data; + NSPasteboard* pb = [NSPasteboard generalPasteboard]; + NSString* typ = [pb availableTypeFromArray:[NSArray arrayWithObject:NSPasteboardTypeString]]; + if (![typ isEqualToString:NSPasteboardTypeString]) return nullptr; + NSString* str = [pb stringForType:NSPasteboardTypeString]; if (!str) return nullptr; + static ImVector buf; const char* c = str.UTF8String; buf.resize((int)strlen(c)+1); strcpy(buf.Data,c); return buf.Data; }; + // Focus notifications ---------------------------------------------------- g_FocusObserver = [[ImFocusObserver alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver - selector:@selector(onApplicationBecomeActive:) - name:NSApplicationDidBecomeActiveNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:g_FocusObserver - selector:@selector(onApplicationBecomeInactive:) - name:NSApplicationDidResignActiveNotification - object:nil]; - + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:g_FocusObserver selector:@selector(appDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:nil]; + [nc addObserver:g_FocusObserver selector:@selector(appDidResignActive:) name:NSApplicationDidResignActiveNotification object:nil]; return true; } -void ImGui_ImplOSX_Shutdown() -{ - g_FocusObserver = NULL; -} +void ImGui_ImplOSX_Shutdown() { g_FocusObserver = nil; } -static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() +static void UpdateMouseCursor() { - // Update buttons ImGuiIO& io = ImGui::GetIO(); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || g_MouseDown[i]; - g_MouseJustPressed[i] = false; - } - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return; - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + ImGuiMouseCursor cur = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || cur == ImGuiMouseCursor_None) { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - if (!g_MouseCursorHidden) - { - g_MouseCursorHidden = true; - [NSCursor hide]; - } + if (!g_MouseCursorHidden) { g_MouseCursorHidden = true; [NSCursor hide]; } } else { - // Show OS mouse cursor - [g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; - if (g_MouseCursorHidden) - { - g_MouseCursorHidden = false; - [NSCursor unhide]; - } + NSCursor* c = g_MouseCursors[cur] ? g_MouseCursors[cur] + : g_MouseCursors[ImGuiMouseCursor_Arrow]; + [c set]; // <— standard C++14, no “?:” extension + if (g_MouseCursorHidden) { g_MouseCursorHidden = false; [NSCursor unhide]; } } } void ImGui_ImplOSX_NewFrame(NSView* view) { - // Setup display size ImGuiIO& io = ImGui::GetIO(); - if (view) - { - const float dpi = (float)[view.window backingScaleFactor]; - io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height); - io.DisplayFramebufferScale = ImVec2(dpi, dpi); - } - // Setup time step - if (g_Time == 0.0) + if (view) { - g_Time = GetMachAbsoluteTimeInSeconds(); + float dpi = view.window.backingScaleFactor; + io.DisplaySize = ImVec2(view.bounds.size.width, view.bounds.size.height); + io.DisplayFramebufferScale= ImVec2(dpi, dpi); } - double current_time = GetMachAbsoluteTimeInSeconds(); - io.DeltaTime = (float)(current_time - g_Time); - g_Time = current_time; - ImGui_ImplOSX_UpdateMouseCursorAndButtons(); -} + double now = AbsoluteSeconds(); + io.DeltaTime = (g_Time > 0.0) ? (float)(now - g_Time) : 1.0f/60.0f; + g_Time = now; -static int mapCharacterToKey(int c) -{ - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - if (c == 25) // SHIFT+TAB -> TAB - return 9; - if (c >= 0 && c < 256) - return c; - if (c >= 0xF700 && c < 0xF700 + 256) - return c - 0xF700 + 256; - return -1; + UpdateMouseCursor(); } -bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) +// --------------------------------------------------------------------------- +// Event translation +// --------------------------------------------------------------------------- +bool ImGui_ImplOSX_HandleEvent(NSEvent* e, NSView* view) { ImGuiIO& io = ImGui::GetIO(); - if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) + auto PushMods = [&](NSEventModifierFlags f) { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = g_MouseJustPressed[button] = true; - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) - { - int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = false; - return io.WantCaptureMouse; - } - - if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) - { - NSPoint mousePoint = event.locationInWindow; - mousePoint = [view convertPoint:mousePoint fromView:nil]; - mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); - io.MousePos = ImVec2((float)mousePoint.x, (float)mousePoint.y); - } - - if (event.type == NSEventTypeScrollWheel) + io.AddKeyEvent(ImGuiMod_Ctrl, (f & NSEventModifierFlagControl) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (f & NSEventModifierFlagShift) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (f & NSEventModifierFlagOption) != 0); + io.AddKeyEvent(ImGuiMod_Super, (f & NSEventModifierFlagCommand) != 0); + }; + auto PushMousePos = [&](NSPoint p) { - double wheel_dx = 0.0; - double wheel_dy = 0.0; - - #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) - { - wheel_dx = [event scrollingDeltaX]; - wheel_dy = [event scrollingDeltaY]; - if ([event hasPreciseScrollingDeltas]) - { - wheel_dx *= 0.1; - wheel_dy *= 0.1; - } - } - else - #endif // MAC_OS_X_VERSION_MAX_ALLOWED - { - wheel_dx = [event deltaX]; - wheel_dy = [event deltaY]; - } - - if (fabs(wheel_dx) > 0.0) - io.MouseWheelH += (float)wheel_dx * 0.1f; - if (fabs(wheel_dy) > 0.0) - io.MouseWheel += (float)wheel_dy * 0.1f; - return io.WantCaptureMouse; - } - - // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. - if (event.type == NSEventTypeKeyDown) + if (!view) return; + p = [view convertPoint:p fromView:nil]; + p.y = view.bounds.size.height - p.y; + io.AddMousePosEvent((float)p.x, (float)p.y); + }; + auto PushMouseBtn = [&](int b,bool d) { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) - { - int c = [str characterAtIndex:i]; - if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) - io.AddInputCharacter((unsigned int)c); - - // We must reset in case we're pressing a sequence of special keys while keeping the command pressed - int key = mapCharacterToKey(c); - if (key != -1 && key < 256 && !io.KeySuper) - resetKeys(); - if (key != -1) - io.KeysDown[key] = true; - } - return io.WantCaptureKeyboard; - } + io.AddMouseButtonEvent(b,d); if (b>=0&&b= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + { dx=e.scrollingDeltaX; dy=e.scrollingDeltaY; if (e.hasPreciseScrollingDeltas){dx*=0.1; dy*=0.1;} } + else +#endif + { dx=e.deltaX; dy=e.deltaY; } + if (fabs(dx)||fabs(dy)) io.AddMouseWheelEvent((float)dx*0.1f,(float)dy*0.1f); + return io.WantCaptureMouse; } - return io.WantCaptureKeyboard; - } - if (event.type == NSEventTypeFlagsChanged) - { - unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + case NSEventTypeKeyDown: + PushMods(e.modifierFlags); + if (ImGuiKey k = KeycodeToImGuiKey((unsigned)e.keyCode)) io.AddKeyEvent(k,true); + for (NSUInteger i = 0; i < e.charactersIgnoringModifiers.length; ++i) + { + unsigned short ch = [e.charactersIgnoringModifiers characterAtIndex:i]; + if (ch != 0x7F && (ch < 0xF700 || ch > 0xFFFF)) // skip DEL + function-key range + io.AddInputCharacter(ch); + } + return io.WantCaptureKeyboard; - bool oldKeyCtrl = io.KeyCtrl; - bool oldKeyShift = io.KeyShift; - bool oldKeyAlt = io.KeyAlt; - bool oldKeySuper = io.KeySuper; - io.KeyCtrl = flags & NSEventModifierFlagControl; - io.KeyShift = flags & NSEventModifierFlagShift; - io.KeyAlt = flags & NSEventModifierFlagOption; - io.KeySuper = flags & NSEventModifierFlagCommand; + case NSEventTypeKeyUp: + PushMods(e.modifierFlags); + if (ImGuiKey k = KeycodeToImGuiKey((unsigned)e.keyCode)) io.AddKeyEvent(k,false); + return io.WantCaptureKeyboard; - // We must reset them as we will not receive any keyUp event if they where pressed with a modifier - if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) - resetKeys(); - return io.WantCaptureKeyboard; + case NSEventTypeFlagsChanged: PushMods(e.modifierFlags); return io.WantCaptureKeyboard; + default: break; } - return false; }