This commit is contained in:
cdozdil
2024-08-11 15:29:50 +03:00
parent a2e80f01d0
commit f3d3aa5adc
16 changed files with 3114 additions and 150 deletions
+3 -15
View File
@@ -650,8 +650,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom
Config::Instance()->CurrentFeature != nullptr && Config::Instance()->CurrentFeature->FrameCount() > Config::Instance()->MenuInitDelay.value_or(75) &&
!ImGuiOverlayDx12::IsInitedDx12())
{
Hooks::ContextRenderingStart();
auto hwnd = Util::GetProcessWindow();
HWND consoleWindow = GetConsoleWindow();
@@ -673,8 +671,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom
if (consoleAllocated)
FreeConsole();
Hooks::ContextRenderingStop();
}
if (handleId < 1000000)
@@ -701,13 +697,10 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom
// Check window recreation
if (Config::Instance()->OverlayMenu.value_or(true) && ImGuiOverlayDx12::IsInitedDx12())
{
Hooks::ContextRenderingStart();
HWND currentHandle = Util::GetProcessWindow();
if (ImGuiOverlayDx12::Handle() != currentHandle)
ImGuiOverlayDx12::ReInitDx12(currentHandle);
Hooks::ContextRenderingStop();
}
if (InCallback)
@@ -813,7 +806,6 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom
Config::Instance()->CurrentFeature = nullptr;
Hooks::ContextRenderingStop();
Hooks::ContextEvaluateStop(nullptr, false);
}
else
@@ -969,17 +961,13 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom
Config::Instance()->CurrentFeature = deviceContext;
if (deviceContext->Name() != "DLSSD")
Hooks::ContextRenderingStart();
bool evalResult = deviceContext->Evaluate(InCmdList, InParameters);
if (deviceContext->Name() != "DLSSD")
Hooks::ContextEvaluateStop(InCmdList, true);
if (Config::Instance()->OverlayMenu.value_or(true))
ImGuiOverlayDx12::CaptureQueue(InCmdList);
Hooks::ContextEvaluateStop(InCmdList, true);
LOG_TRACE("done: {0:X}", (UINT)evalResult);
if (evalResult)
+13 -4
View File
@@ -82,7 +82,7 @@
<IncludePath>$(SolutionDir)external\vulkan\include;$(SolutionDir)external\nvngx_dlss_sdk;$(SolutionDir)external\xess\inc\xess;$(SolutionDir)external\FidelityFX-SDK\ffx-api\include\ffx_api;$(SolutionDir)external\simpleini;$(SolutionDir)external\unordered_dense\include;$(SolutionDir)external\spdlog\include;$(IncludePath)</IncludePath>
<LibraryPath>$(ProjectDir)fsr2\lib;$(ProjectDir)fsr2_212\lib;$(ProjectDir)vulkan;$(ProjectDir)d3dx;$(ProjectDir)detours;$(SolutionDir)external\xess\lib;$(LibraryPath)</LibraryPath>
<TargetName>dxgi</TargetName>
<OutDir>D:\Folders\Games\SteamLibrary\steamapps\common\Horizon Zero Dawn\</OutDir>
<OutDir>D:\Folders\Games\Deep Rock Galactic\FSD\Binaries\Win64\</OutDir>
<IntDir>.\x64\Debug</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -212,7 +212,12 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\</Command>
<ClInclude Include="exports\Winhttp.h" />
<ClInclude Include="exports\Wininet.h" />
<ClInclude Include="exports\Winmm.h" />
<ClInclude Include="hooks\Dx12_Hooks.h" />
<ClInclude Include="hooks\Dxgi_Hooks.h" />
<ClInclude Include="enums\Enums.h" />
<ClInclude Include="hooks\LoadLibrary_Hooks.h" />
<ClInclude Include="hooks\NvApi_Hooks.h" />
<ClInclude Include="hooks\Nvngx_Hooks.h" />
<ClInclude Include="hooks\Vulkan_Hooks.h" />
<ClInclude Include="Working_Mode.h" />
<ClInclude Include="output_scaling\OS_Common.h" />
@@ -237,7 +242,6 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\</Command>
<ClInclude Include="backends\IFeature.h" />
<ClInclude Include="backends\IFeature_Vk.h" />
<ClInclude Include="detours\detours.h" />
<ClInclude Include="dllmain.h" />
<ClInclude Include="imgui\imgui\imconfig.h" />
<ClInclude Include="imgui\imgui\imgui.h" />
<ClInclude Include="imgui\imgui\imgui_impl_dx11.h" />
@@ -256,7 +260,7 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\</Command>
<ClInclude Include="imgui\imgui_overlay_dx11.h" />
<ClInclude Include="imgui\imgui_overlay_dx12.h" />
<ClInclude Include="imgui\imgui_overlay_vk.h" />
<ClInclude Include="imgui\wrapped_swapchain.h" />
<ClInclude Include="hooks\proxies\Wrapped_SwapChain.h" />
<ClInclude Include="Logger.h" />
<ClInclude Include="NVNGX_Parameter.h" />
<ClInclude Include="NVNGX_Proxy.h" />
@@ -300,7 +304,12 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\</Command>
<ClCompile Include="backends\fsr2\FSR2Feature_Dx12.cpp" />
<ClCompile Include="backends\fsr2\FSR2Feature_Vk.cpp" />
<ClCompile Include="backends\IFeature_Dx12.cpp" />
<ClCompile Include="exports\Dxgi.cpp" />
<ClCompile Include="hooks\Dx12_Hooks.cpp" />
<ClCompile Include="hooks\Dxgi_Hooks.cpp" />
<ClCompile Include="hooks\LoadLibrary_Hooks.cpp" />
<ClCompile Include="hooks\NvApi_Hooks.cpp" />
<ClCompile Include="hooks\Nvngx_Hooks.cpp" />
<ClCompile Include="hooks\Vulkan_Hooks.cpp" />
<ClCompile Include="Working_Mode.cpp" />
<ClCompile Include="imgui\imgui\imgui.cpp" />
@@ -319,7 +328,7 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\</Command>
<ClCompile Include="imgui\imgui_overlay_dx11.cpp" />
<ClCompile Include="imgui\imgui_overlay_dx12.cpp" />
<ClCompile Include="imgui\imgui_overlay_vk.cpp" />
<ClCompile Include="imgui\wrapped_swapchain.cpp" />
<ClCompile Include="hooks\proxies\Wrapped_SwapChain.cpp" />
<ClCompile Include="Logger.cpp" />
<ClCompile Include="NVNGX.cpp" />
<ClCompile Include="NVNGX_DLSS_Dx11.cpp" />
+10
View File
@@ -0,0 +1,10 @@
#pragma once
enum SwapchainSource
{
Unknown,
Dx12,
SL,
FSR3_Mod,
FSR3_Native
};
+56 -74
View File
@@ -1,17 +1,16 @@
#include "Dx12_Hooks.h"
#include "../pch.h"
#include "../Config.h"
#include "../detours/detours.h"
// Menu Hooks
typedef void(*PFN_ExecuteCommandLists)(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*);
typedef ULONG(*PFN_Release)(IUnknown*);
typedef HRESULT(*PFN_CreateCommandQueue)(ID3D12Device* This, const D3D12_COMMAND_QUEUE_DESC* pDesc, REFIID riid, void** ppCommandQueue);
typedef void(*PFN_ExecuteCommandLists)(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*);
typedef ULONG(*PFN_CommandQueueRelease)(IUnknown*);
static PFN_D3D12_CREATE_DEVICE o_D3D12CreateDevice = nullptr;
static PFN_CreateCommandQueue o_CreateCommandQueue = nullptr;
static PFN_Release o_CommandQueueRelease = nullptr;
static PFN_CommandQueueRelease o_CommandQueueRelease = nullptr;
static PFN_ExecuteCommandLists o_ExecuteCommandLists = nullptr;
// Sampler stuff for MipLodBias and Anisotropic Filtering
@@ -26,13 +25,20 @@ static PFN_SetRootSignature o_SetGraphicRootSignature = nullptr;
static ID3D12RootSignature* rootSigCompute = nullptr;
static ID3D12RootSignature* rootSigGraphic = nullptr;
static bool contextRendering = false;
static ULONGLONG computeTime = 0;
static ULONGLONG graphTime = 0;
static ULONGLONG lastEvalTime = 0;
static uint64_t evaluateStart;
static uint64_t computeRSTime = 0;
static uint64_t graphRSTime = 0;
static uint64_t lastEvalTime = 0;
static uint64_t evalStartTime = 0;
static std::mutex sigatureMutex;
static void HookToCommandList(ID3D12GraphicsCommandList* InCmdList);
static void HookToDevice(ID3D12Device* InDevice);
// Commnad list & queue
static std::function<ULONG(IUnknown*)> commandQueueReleaseCallback = nullptr;
static std::function<void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*)> executeCommandListCallback = nullptr;
static int64_t GetTicks()
{
LARGE_INTEGER ticks;
@@ -43,16 +49,14 @@ static int64_t GetTicks()
return ticks.QuadPart;
}
static void HookToCommandList(ID3D12GraphicsCommandList* InCmdList);
static void HookToDevice(ID3D12Device* InDevice);
static void hkSetComputeRootSignature(ID3D12GraphicsCommandList* commandList, ID3D12RootSignature* pRootSignature)
{
if (!contextRendering && commandList != nullptr && pRootSignature != nullptr)
if (Config::Instance()->RestoreComputeSignature.value_or(false) &&
commandList != nullptr && pRootSignature != nullptr && (lastEvalTime == 0 || lastEvalTime == evalStartTime))
{
sigatureMutex.lock();
rootSigCompute = pRootSignature;
computeTime = GetTicks();
computeRSTime = GetTicks();
sigatureMutex.unlock();
}
@@ -61,11 +65,12 @@ static void hkSetComputeRootSignature(ID3D12GraphicsCommandList* commandList, ID
static void hkSetGraphicRootSignature(ID3D12GraphicsCommandList* commandList, ID3D12RootSignature* pRootSignature)
{
if (!contextRendering && commandList != nullptr && pRootSignature != nullptr)
if (Config::Instance()->RestoreGraphicSignature.value_or(false) &&
commandList != nullptr && pRootSignature != nullptr && (lastEvalTime == 0 || lastEvalTime == evalStartTime))
{
sigatureMutex.lock();
rootSigGraphic = pRootSignature;
graphTime = GetTicks();
graphRSTime = GetTicks();
sigatureMutex.unlock();
}
@@ -122,7 +127,7 @@ static void hkCreateSampler(ID3D12Device* device, const D3D12_SAMPLER_DESC* pDes
return o_CreateSampler(device, &newDesc, DestDescriptor);
}
static HRESULT WINAPI hkD3D12CreateDevice(IUnknown* pAdapter, D3D_FEATURE_LEVEL MinimumFeatureLevel, REFIID riid, void** ppDevice)
static HRESULT hkD3D12CreateDevice(IUnknown* pAdapter, D3D_FEATURE_LEVEL MinimumFeatureLevel, REFIID riid, void** ppDevice)
{
auto result = o_D3D12CreateDevice(pAdapter, MinimumFeatureLevel, riid, ppDevice);
@@ -137,50 +142,25 @@ static HRESULT WINAPI hkD3D12CreateDevice(IUnknown* pAdapter, D3D_FEATURE_LEVEL
return result;
}
static void WINAPI hkExecuteCommandLists(ID3D12CommandQueue* pCommandQueue, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists)
static void hkExecuteCommandLists(ID3D12CommandQueue* pCommandQueue, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists)
{
// TODO: Continue from here to Dx12 Overlay Menu stuff
//
//if (_cqDx12 == nullptr && _dlssCommandList != nullptr)
//{
// // trying to find command list for dlss,
// // it should be native dx12 cq
// for (size_t i = 0; i < NumCommandLists; i++)
// {
// if (ppCommandLists[i] == _dlssCommandList)
// {
// LOG_INFO("new _cqDx12: {0:X}", (ULONG64)pCommandQueue);
// _cqDx12 = pCommandQueue;
// break;
// }
// }
//}
if (executeCommandListCallback != nullptr)
executeCommandListCallback(pCommandQueue, NumCommandLists, ppCommandLists);
return o_ExecuteCommandLists(pCommandQueue, NumCommandLists, ppCommandLists);
}
static ULONG WINAPI hkCommandQueueRelease(IUnknown* This)
static ULONG hkCommandQueueRelease(IUnknown* This)
{
auto result = o_CommandQueueRelease(This);
// TODO: Continue from here to Dx12 Overlay Menu stuff
//if (result == 0)
//{
// if (This == _cqDx12)
// _cqDx12 = nullptr;
// else if (This == _cqFsr3Mod)
// _cqFsr3Mod = nullptr;
// else if (This == _cqFsr3Native)
// _cqFsr3Native = nullptr;
// else if (This == _cqSL)
// _cqSL = nullptr;
//}
if (commandQueueReleaseCallback != nullptr)
commandQueueReleaseCallback(This);
return result;
}
static HRESULT WINAPI hkCreateCommandQueue(ID3D12Device* This, const D3D12_COMMAND_QUEUE_DESC* pDesc, REFIID riid, void** ppCommandQueue)
static HRESULT hkCreateCommandQueue(ID3D12Device* This, const D3D12_COMMAND_QUEUE_DESC* pDesc, REFIID riid, void** ppCommandQueue)
{
auto result = o_CreateCommandQueue(This, pDesc, riid, ppCommandQueue);
@@ -189,7 +169,7 @@ static HRESULT WINAPI hkCreateCommandQueue(ID3D12Device* This, const D3D12_COMMA
auto commandQueue = (ID3D12CommandQueue*)*ppCommandQueue;
PVOID* pVTable = *(PVOID**)commandQueue;
o_CommandQueueRelease = (PFN_Release)pVTable[2];
o_CommandQueueRelease = (PFN_CommandQueueRelease)pVTable[2];
o_ExecuteCommandLists = (PFN_ExecuteCommandLists)pVTable[10];
if (o_ExecuteCommandLists != nullptr || o_CommandQueueRelease != nullptr)
@@ -326,7 +306,7 @@ void Hooks::DetachDx12Hooks()
void Hooks::ContextEvaluateStart()
{
evaluateStart = GetTicks();
evalStartTime = GetTicks();
}
void Hooks::ContextEvaluateStop(ID3D12GraphicsCommandList* InCmdList, bool InRestore)
@@ -335,38 +315,35 @@ void Hooks::ContextEvaluateStop(ID3D12GraphicsCommandList* InCmdList, bool InRes
{
sigatureMutex.lock();
if (Config::Instance()->RestoreComputeSignature.value_or(false) && computeTime != 0 && computeTime > lastEvalTime && computeTime <= evaluateStart && rootSigCompute != nullptr)
if (Config::Instance()->RestoreComputeSignature.value_or(false) && computeRSTime != 0 &&
computeRSTime > lastEvalTime && computeRSTime <= evalStartTime && rootSigCompute != nullptr)
{
LOG_TRACE("restore orgComputeRootSig: {0:X}", (UINT64)rootSigCompute);
o_SetComputeRootSignature(InCmdList, rootSigCompute);
}
else
else if (Config::Instance()->RestoreComputeSignature.value_or(false))
{
if (Config::Instance()->RestoreComputeSignature.value_or(false))
{
LOG_TRACE("orgComputeRootSig lastEvalTime: {0}", lastEvalTime);
LOG_TRACE("orgComputeRootSig computeTime: {0}", computeTime);
LOG_TRACE("orgComputeRootSig evaluateStart: {0}", evaluateStart);
}
LOG_TRACE("orgComputeRootSig lastEvalTime: {0}", lastEvalTime);
LOG_TRACE("orgComputeRootSig computeRSTime: {0}", computeRSTime);
LOG_TRACE("orgComputeRootSig evalStartTime: {0}", evalStartTime);
}
if (Config::Instance()->RestoreGraphicSignature.value_or(false) && graphTime != 0 && graphTime > lastEvalTime && graphTime <= evaluateStart && rootSigGraphic != nullptr)
if (Config::Instance()->RestoreGraphicSignature.value_or(false) &&
graphRSTime != 0 && graphRSTime > lastEvalTime && graphRSTime <= evalStartTime && rootSigGraphic != nullptr)
{
LOG_TRACE("restore orgGraphicRootSig: {0:X}", (UINT64)rootSigGraphic);
o_SetGraphicRootSignature(InCmdList, rootSigGraphic);
}
else
else if (Config::Instance()->RestoreGraphicSignature.value_or(false))
{
if (Config::Instance()->RestoreGraphicSignature.value_or(false))
{
LOG_TRACE("orgGraphicRootSig lastEvalTime: {0}", lastEvalTime);
LOG_TRACE("orgGraphicRootSig computeTime: {0}", graphTime);
LOG_TRACE("orgGraphicRootSig evaluateStart: {0}", evaluateStart);
}
LOG_TRACE("orgGraphicRootSig lastEvalTime: {0}", lastEvalTime);
LOG_TRACE("orgGraphicRootSig computeRSTime: {0}", graphRSTime);
LOG_TRACE("orgGraphicRootSig evalStartTime: {0}", evalStartTime);
}
contextRendering = false;
lastEvalTime = evaluateStart;
lastEvalTime = evalStartTime;
rootSigCompute = nullptr;
rootSigGraphic = nullptr;
@@ -375,12 +352,17 @@ void Hooks::ContextEvaluateStop(ID3D12GraphicsCommandList* InCmdList, bool InRes
}
}
void Hooks::ContextRenderingStart()
bool Hooks::IsDeviceCaptured()
{
contextRendering = true;
return o_D3D12CreateDevice != nullptr;
}
void Hooks::ContextRenderingStop()
void Hooks::SetCommandQueueReleaseCallback(std::function<ULONG(IUnknown*)> InCallback)
{
contextRendering = false;
}
commandQueueReleaseCallback = InCallback;
}
void Hooks::SetExecuteCommandListCallback(std::function<void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*)> InCallback)
{
executeCommandListCallback = InCallback;
}
+5 -2
View File
@@ -1,4 +1,5 @@
#pragma once
#include "../pch.h"
#include <d3d12.h>
@@ -10,6 +11,8 @@ namespace Hooks
void ContextEvaluateStart();
void ContextEvaluateStop(ID3D12GraphicsCommandList* InCmdList, bool InRestore);
void ContextRenderingStart();
void ContextRenderingStop();
bool IsDeviceCaptured();
void SetCommandQueueReleaseCallback(std::function<ULONG(IUnknown*)> InCallback);
void SetExecuteCommandListCallback(std::function<void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*)> InCallback);
}
+11 -3
View File
@@ -1,16 +1,24 @@
#include "Dxgi_Hooks.h"
#include "../pch.h"
#include "../exports/Dxgi.h"
#include "../detours/detours.h"
typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY)(REFIID riid, _COM_Outptr_ void** ppFactory);
typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void** ppFactory);
typedef HRESULT(*PFN_CreateSwapChain)(IDXGIFactory*, IUnknown*, DXGI_SWAP_CHAIN_DESC*, IDXGISwapChain**);
typedef HRESULT(*PFN_CreateSwapChainForHwnd)(IDXGIFactory*, IUnknown*, HWND, const DXGI_SWAP_CHAIN_DESC1*, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*, IDXGIOutput*, IDXGISwapChain1**);
typedef HRESULT(*PFN_CreateSwapChainForCoreWindow)(IDXGIFactory*, IUnknown*, IUnknown*, const DXGI_SWAP_CHAIN_DESC1*, IDXGIOutput*, IDXGISwapChain1**);
typedef HRESULT(*PFN_CreateSwapChainForComposition)(IDXGIFactory*, IUnknown*, const DXGI_SWAP_CHAIN_DESC1*, IDXGIOutput*, IDXGISwapChain1**);
static PFN_CREATE_DXGI_FACTORY o_CreateDxgiFactory = nullptr;
static PFN_CREATE_DXGI_FACTORY o_CreateDxgiFactory1 = nullptr;
static PFN_CREATE_DXGI_FACTORY_2 o_CreateDxgiFactory2 = nullptr;
static PFN_CreateSwapChain o_CreateSwapChain = nullptr;
static PFN_CreateSwapChainForHwnd o_CreateSwapChainForHwnd = nullptr;
static PFN_CreateSwapChainForComposition o_CreateSwapChainForComposition = nullptr;
static PFN_CreateSwapChainForCoreWindow o_CreateSwapChainForCoreWindow = nullptr;
static std::function<void> createSwapChainCallback = nullptr;
void Hooks::AttachDxgiHooks()
{
if (o_CreateDxgiFactory != nullptr || o_CreateDxgiFactory1 != nullptr || o_CreateDxgiFactory2 != nullptr)
+7
View File
@@ -1,7 +1,14 @@
#pragma once
#include "../pch.h"
#include "../enums/Enums.h"
#include <dxgi1_6.h>
namespace Hooks
{
void AttachDxgiHooks();
void DetachDxgiHooks();
void DxgiClean(std::function<void(SwapchainSource, bool)>);
void DxgiPresent(std::function<void(SwapchainSource, IDXGISwapChain3*, UINT)>);
}
@@ -1,17 +1,19 @@
#include "wrapped_swapchain.h"
#include "Wrapped_SwapChain.h"
// Used RenderDoc's wrapped object as referance
// https://github.com/baldurk/renderdoc/blob/v1.x/renderdoc/driver/dxgi/dxgi_wrapped.cpp
WrappedIDXGISwapChain4::WrappedIDXGISwapChain4(IDXGISwapChain* real,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT)> renderTrig,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)> renderTrig1,
std::function<void(bool)> clearTrig) : m_pReal(real), RenderTrig(renderTrig), RenderTrig1(renderTrig1), ClearTrig(clearTrig), m_iRefcount(1)
WrappedIDXGISwapChain4::WrappedIDXGISwapChain4(IDXGISwapChain* InRealSwapChain,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT)>* InPresentTrig,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)>* InPresentTrig1,
std::function<void(bool)>* InClearTrig,
HWND* InMenuHandle) : m_pReal(InRealSwapChain), PresentTrig(InPresentTrig), PresentTrig1(InPresentTrig1),
ClearTrig(InClearTrig), m_iRefcount(1), menuHandle(InMenuHandle)
{
real->QueryInterface(__uuidof(IDXGISwapChain1), (void**)&m_pReal1);
real->QueryInterface(__uuidof(IDXGISwapChain2), (void**)&m_pReal2);
real->QueryInterface(__uuidof(IDXGISwapChain3), (void**)&m_pReal3);
real->QueryInterface(__uuidof(IDXGISwapChain4), (void**)&m_pReal4);
m_pReal->QueryInterface(IID_PPV_ARGS(&m_pReal1));
m_pReal->QueryInterface(IID_PPV_ARGS(&m_pReal2));
m_pReal->QueryInterface(IID_PPV_ARGS(&m_pReal3));
m_pReal->QueryInterface(IID_PPV_ARGS(&m_pReal4));
}
WrappedIDXGISwapChain4::~WrappedIDXGISwapChain4()
@@ -115,8 +117,8 @@ HRESULT WrappedIDXGISwapChain4::ResizeBuffers(UINT BufferCount, UINT Width, UINT
{
LOG_FUNC();
if (ClearTrig != nullptr)
ClearTrig(false);
if (*ClearTrig != nullptr)
(*ClearTrig)(false);
HRESULT ret = m_pReal->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
@@ -134,8 +136,8 @@ HRESULT WrappedIDXGISwapChain4::ResizeBuffers1(UINT BufferCount, UINT Width, UIN
{
LOG_FUNC();
if (ClearTrig != nullptr)
ClearTrig(false);
if (*ClearTrig != nullptr)
(*ClearTrig)(false);
HRESULT ret = m_pReal3->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);
return ret;
@@ -145,8 +147,8 @@ HRESULT WrappedIDXGISwapChain4::SetFullscreenState(BOOL Fullscreen, IDXGIOutput*
{
LOG_FUNC();
if (ClearTrig != nullptr)
ClearTrig(true);
if (*ClearTrig != nullptr)
(*ClearTrig)(true);
return m_pReal->SetFullscreenState(Fullscreen, pTarget);
}
@@ -171,16 +173,16 @@ HRESULT WrappedIDXGISwapChain4::GetDevice(REFIID riid, void** ppDevice)
HRESULT WrappedIDXGISwapChain4::Present(UINT SyncInterval, UINT Flags)
{
if (RenderTrig != nullptr && m_pReal3 != nullptr)
RenderTrig(m_pReal3, SyncInterval, Flags);
if (*PresentTrig != nullptr && m_pReal3 != nullptr)
(*PresentTrig)(m_pReal3, SyncInterval, Flags);
return m_pReal->Present(SyncInterval, Flags);
}
HRESULT WrappedIDXGISwapChain4::Present1(UINT SyncInterval, UINT Flags, const DXGI_PRESENT_PARAMETERS* pPresentParameters)
{
if (RenderTrig1 != nullptr && m_pReal3 != nullptr)
RenderTrig1(m_pReal3, SyncInterval, Flags, pPresentParameters);
if (*PresentTrig1 != nullptr && m_pReal3 != nullptr)
(*PresentTrig1)(m_pReal3, SyncInterval, Flags, pPresentParameters);
return m_pReal1->Present1(SyncInterval, Flags, pPresentParameters);
}
@@ -1,7 +1,7 @@
#pragma once
#include "../pch.h"
#include "dxgi1_6.h"
#include "imgui_overlay_base.h"
#include "../../pch.h"
#include <dxgi1_6.h>
class WrappedIDXGISwapChain4 : public IDXGISwapChain4
{
@@ -11,18 +11,20 @@ class WrappedIDXGISwapChain4 : public IDXGISwapChain4
IDXGISwapChain3* m_pReal3 = nullptr;
IDXGISwapChain4* m_pReal4 = nullptr;
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT)> RenderTrig = nullptr;
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)> RenderTrig1 = nullptr;
std::function<void(bool)> ClearTrig = nullptr;
HWND* menuHandle = nullptr;
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT)>* PresentTrig = nullptr;
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)>* PresentTrig1 = nullptr;
std::function<void(bool)>* ClearTrig = nullptr;
unsigned int m_iRefcount;
public:
WrappedIDXGISwapChain4(IDXGISwapChain* real,
std::function<HRESULT(IDXGISwapChain3*, UINT , UINT)> renderTrig,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)> renderTrig1,
std::function<void(bool)> clearTrig);
WrappedIDXGISwapChain4(IDXGISwapChain* InRealSwapChain,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT)>* InPresentTrig,
std::function<HRESULT(IDXGISwapChain3*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*)>* InPresentTrig1,
std::function<void(bool)>* InClearTrig,
HWND* InMenuHandle);
virtual ~WrappedIDXGISwapChain4();
@@ -36,15 +38,15 @@ public:
ULONG STDMETHODCALLTYPE Release()
{
unsigned int ret = InterlockedDecrement(&m_iRefcount);
auto ret = InterlockedDecrement(&m_iRefcount);
if (ret == 0)
{
HWND hwnd = nullptr;
m_pReal1->GetHwnd(&hwnd);
if (ClearTrig != nullptr && hwnd == ImGuiOverlayBase::Handle())
ClearTrig(true);
if (*ClearTrig != nullptr && hwnd == *menuHandle)
(*ClearTrig)(true);
delete this;
}
+253
View File
@@ -0,0 +1,253 @@
#ifndef IMTERM_MISC_HPP
#define IMTERM_MISC_HPP
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Copyright C 2019, Lucas Lazare ///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ///
/// files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, ///
/// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software ///
/// is furnished to do so, subject to the following conditions: ///
/// ///
/// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ///
/// ///
/// The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the ///
/// warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or ///
/// copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, ///
/// arising from, out of or in connection with the software or the use or other dealings in the Software. ///
/// ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <utility>
#include <iterator>
#include <functional>
#include <type_traits>
namespace misc {
// std::identity is c++20
struct identity {
template<typename T>
constexpr T&& operator()(T&& t) const {
return std::forward<T>(t);
}
};
namespace details {
struct structured_void {};
inline structured_void no_value{};
template <typename T>
struct non_void {
using type = T;
};
template <>
struct non_void<void> {
using type = structured_void;
};
}
// type T if T is non void, details::structured void otherwise
template <typename T>
using non_void_t = typename details::non_void<T>::type;
// Returns the length of the given byte string, at most buffer_size
constexpr unsigned int strnlen(const char* beg, unsigned int buffer_size) {
unsigned int len = 0;
while (len < buffer_size && beg[len] != '\0') {
++len;
}
return len;
}
// std::is_sorted is not constexpr pre C++20
template <typename ForwardIt, typename EndIt, typename Comparator = std::less<std::remove_reference_t<decltype(*std::declval<ForwardIt>())>>>
constexpr bool is_sorted(ForwardIt it, EndIt last, Comparator&& comp = {}) {
if (it == last) {
return true;
}
for (ForwardIt next = std::next(it) ; next != last ; ++next, ++it) {
if (comp(*next, *it)) {
return false;
}
}
return true;
}
// returns the length of the longest element of the list, minimum 0
template <typename ForwardIt, typename EndIt, typename SizeExtractor>
constexpr auto max_size(ForwardIt it, EndIt last, SizeExtractor&& size_of) -> decltype(size_of(*it)){
using size_type = decltype(size_of(*it));
size_type max_size = 0u;
while (it != last) {
size_type current_size = size_of(*it++);
if (current_size > max_size) {
max_size = current_size;
}
}
return max_size;
}
// copies until it reaches the end of any of the two collections
template <typename ForwardIt, typename OutputIt>
constexpr void copy(ForwardIt src_beg, ForwardIt src_end, OutputIt dest_beg, OutputIt dest_end) {
while(src_beg != src_end && dest_beg != dest_end) {
*dest_beg++ = *src_beg++;
}
}
// same as misc::copy, but in reversed order
template <typename BidirIt1, typename BidirIt2>
constexpr void copy_backward(BidirIt1 src_beg, BidirIt1 src_end, BidirIt2 dest_beg, BidirIt2 dest_end) {
auto copy_length = std::distance(src_beg, src_end);
auto avail_length = std::distance(dest_beg, dest_end);
if (avail_length < copy_length) {
std::advance(src_end, avail_length - copy_length);
} else {
std::advance(dest_end, copy_length - avail_length);
}
while(src_beg != src_end && dest_beg != dest_end) {
*--dest_end = *--src_end;
}
}
// Returns new end of dest collection
// ignores the n first values of the destination
template <typename ForwardIt, typename RandomAccessIt>
constexpr RandomAccessIt erase_insert(ForwardIt src_beg, ForwardIt src_end, RandomAccessIt dest_beg, RandomAccessIt dest_end, RandomAccessIt dest_max, unsigned int n) {
n = std::min(static_cast<unsigned>(std::distance(dest_beg, dest_end)), n);
auto copy_length = static_cast<unsigned>(std::distance(src_beg, src_end));
auto avail_length = static_cast<unsigned>(std::distance(dest_end, dest_max)) + n;
if (copy_length <= avail_length) {
misc::copy_backward(dest_beg + n, dest_end, dest_beg + copy_length, dest_end + copy_length);
misc::copy(src_beg, src_end, dest_beg, dest_beg + copy_length);
return dest_end + copy_length - n;
} else {
std::advance(src_beg, copy_length - avail_length);
copy_length = avail_length;
misc::copy_backward(dest_beg + n, dest_end, dest_beg + copy_length, dest_end + copy_length);
misc::copy(src_beg, src_end, dest_beg, dest_beg + copy_length);
return dest_end + copy_length - n;
}
}
// returns the last element in the range [begin, end) that is equal to val, returns end if there is no such element
template <typename ForwardIterator, typename ValueType>
constexpr ForwardIterator find_last(ForwardIterator begin, ForwardIterator end, const ValueType& val) {
auto rend = std::reverse_iterator(begin);
auto rbegin = std::reverse_iterator(end);
auto search = std::find(rbegin, rend, val);
if (search == rend) {
return end;
}
return std::prev(search.base());
}
// Returns an iterator to the first character that has no a space after him
template <typename ForwardIterator, typename SpaceDetector>
constexpr ForwardIterator find_terminating_word(ForwardIterator begin, ForwardIterator end, SpaceDetector&& is_space_pred) {
auto rend = std::reverse_iterator(begin);
auto rbegin = std::reverse_iterator(end);
int sp_size = 0;
auto is_space = [&sp_size, &is_space_pred, &end] (char c) {
sp_size = is_space_pred(std::string_view{&c, static_cast<unsigned>(&*std::prev(end) - &c)});
return sp_size > 0;
};
auto search = std::find_if(rbegin, rend, is_space);
if (search == rend) {
return begin;
}
ForwardIterator it = std::prev(search.base());
it += sp_size;
return it;
}
constexpr bool success(std::errc ec) {
return ec == std::errc{};
}
// Search any element starting by "prefix" in the sorted collection formed by [c_beg, c_end)
// str_ext must map dectype(*c_beg) to std::string_view
// transform is whatever transformation you want to do to the matching elements.
template <typename ForwardIt, typename StrExtractor = identity, typename Transform = identity>
auto prefix_search(std::string_view prefix, ForwardIt c_beg, ForwardIt c_end,
StrExtractor&& str_ext = {}, Transform&& transform = {}) -> std::vector<std::decay_t<decltype(transform(*c_beg))>> {
auto lower = std::lower_bound(c_beg, c_end, prefix);
auto higher = std::upper_bound(lower, c_end, prefix, [&str_ext](std::string_view pre, const auto& element) {
std::string_view val = str_ext(element);
return pre.substr(0, std::min(val.size(), pre.size())) < val.substr(0, std::min(val.size(), pre.size()));
});
std::vector<std::decay_t<decltype(transform(*c_beg))>> ans;
std::transform(lower, higher, std::back_inserter(ans), std::forward<Transform>(transform));
return ans;
}
// returns an iterator to the first element starting by "prefix"
// is_space is a predicate returning a value greater than 0 if a given string_view starts by a space and 0 otherwise
// str_ext is an unary functor maping decltype(*beg) to std::string_view
template <typename ForwardIt, typename IsSpace, typename StrExtractor = identity>
ForwardIt find_first_prefixed(std::string_view prefix, ForwardIt beg, ForwardIt end, IsSpace&& is_space, StrExtractor&& str_ext = {}) {
// std::string_view::start_with is C++20
auto start_with = [&](std::string_view str, std::string_view pr) {
std::string_view::size_type idx = 0;
int space_count{};
while (idx < str.size() && (space_count = is_space(str.substr(idx))) > 0) {
idx += space_count;
}
return (str.size() - idx) >= pr.size() ? str.substr(idx, pr.size()) == pr : false;
};
while (beg != end) {
if (start_with(str_ext(*beg), prefix)) {
return beg;
}
++beg;
}
return beg;
}
namespace details {
template <typename AlwaysVoid, template<typename...> typename Op, typename... Args>
struct detector : std::false_type {
using type = std::is_same<void,void>;
};
template <template<typename...> typename Op, typename... Args>
struct detector<std::void_t<Op<Args...>>, Op, Args...> : std::true_type {
using type = Op<Args...>;
};
}
template <template<typename...> typename Op, typename... Args>
using is_detected = typename details::detector<void, Op, Args...>;
template <template<typename...> typename Op, typename... Args>
using is_detected_t = typename is_detected <Op, Args...>::type;
// compile time function detection, return type agnostic
template <template<typename...> typename Op, typename... Args>
constexpr bool is_detected_v = is_detected<Op, Args...>::value;
// compile time function detection
template <template<typename...> typename Op, typename ReturnType, typename... Args>
constexpr bool is_detected_with_return_type_v = std::is_same_v<is_detected_t<Op, Args...>, ReturnType>;
// dummy mutex
struct no_mutex {
constexpr no_mutex() noexcept = default;
constexpr void lock() {}
constexpr void unlock() {}
constexpr bool try_lock() { return true; }
};
}
#endif //IMTERM_MISC_HPP
+485
View File
@@ -0,0 +1,485 @@
#ifndef IMTERM_TERMINAL_HPP
#define IMTERM_TERMINAL_HPP
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Copyright C 2019, Lucas Lazare ///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ///
/// files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, ///
/// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software ///
/// is furnished to do so, subject to the following conditions: ///
/// ///
/// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ///
/// ///
/// The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the ///
/// warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or ///
/// copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, ///
/// arising from, out of or in connection with the software or the use or other dealings in the Software. ///
/// ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <atomic>
#include <vector>
#include <string>
#include <utility>
#include <optional>
#include <array>
#include <imgui.h>
#include "utils.hpp"
#include "misc.hpp"
#ifdef IMTERM_USE_FMT
#include "fmt/format.h"
#endif
namespace ImTerm {
// checking that you can use a given class as a TerminalHelper
// giving human-friendlier error messages than just letting the compiler explode
namespace details {
template<typename TerminalHelper, typename CommandTypeCref>
struct assert_wellformed {
template<typename T>
using find_commands_by_prefix_method_v1 =
decltype(std::declval<T&>().find_commands_by_prefix(std::declval<std::string_view>()));
template<typename T>
using find_commands_by_prefix_method_v2 =
decltype(std::declval<T&>().find_commands_by_prefix(std::declval<const char *>(),
std::declval<const char *>()));
template<typename T>
using list_commands_method = decltype(std::declval<T&>().list_commands());
template<typename T>
using format_method = decltype(std::declval<T&>().format(std::declval<std::string>(), std::declval<message::type>()));
static_assert(
misc::is_detected_with_return_type_v<find_commands_by_prefix_method_v1, std::vector<CommandTypeCref>, TerminalHelper>,
"TerminalHelper should implement the method 'std::vector<command_type_cref> find_command_by_prefix(std::string_view)'. "
"See term::terminal_helper_example for reference");
static_assert(
misc::is_detected_with_return_type_v<find_commands_by_prefix_method_v2, std::vector<CommandTypeCref>, TerminalHelper>,
"TerminalHelper should implement the method 'std::vector<command_type_cref> find_command_by_prefix(const char*, const char*)'. "
"See term::terminal_helper_example for reference");
static_assert(
misc::is_detected_with_return_type_v<list_commands_method, std::vector<CommandTypeCref>, TerminalHelper>,
"TerminalHelper should implement the method 'std::vector<command_type_cref> list_commands()'. "
"See term::terminal_helper_example for reference");
static_assert(
misc::is_detected_with_return_type_v<format_method, std::optional<message>, TerminalHelper>,
"TerminalHelper should implement the method 'std::optional<term::message> format(std::string, term::message::type)'. "
"See term::terminal_helper_example for reference");
};
}
template<typename TerminalHelper>
class terminal {
using buffer_type = std::array<char, 1024>;
using small_buffer_type = std::array<char, 128>;
public:
using value_type = misc::non_void_t<typename TerminalHelper::value_type>;
using command_type = command_t<terminal<TerminalHelper>>;
using command_type_cref = std::reference_wrapper<const command_type>;
using argument_type = argument_t<terminal>;
using terminal_helper_is_valid = details::assert_wellformed<TerminalHelper, command_type_cref>;
inline static const std::vector<config_panels> DEFAULT_ORDER = {config_panels::clearbutton,
config_panels::autoscroll, config_panels::autowrap, config_panels::long_filter, config_panels::loglevel};
// You shall call this constructor you used a non void value_type
template <typename T = value_type, typename = std::enable_if_t<!std::is_same_v<T, misc::details::structured_void>>>
explicit terminal(value_type& arg_value, const char * window_name_ = "terminal", int base_width_ = 900,
int base_height_ = 200, std::shared_ptr<TerminalHelper> th = std::make_shared<TerminalHelper>())
: terminal(arg_value, window_name_, base_width_, base_height_, std::move(th), terminal_helper_is_valid{}) {}
// You shall call this constructor you used a void value_type
template <typename T = value_type, typename = std::enable_if_t<std::is_same_v<T, misc::details::structured_void>>>
explicit terminal(const char * window_name_ = "terminal", int base_width_ = 900,
int base_height_ = 200, std::shared_ptr<TerminalHelper> th = std::make_shared<TerminalHelper>())
: terminal(misc::details::no_value, window_name_, base_width_, base_height_, std::move(th), terminal_helper_is_valid{}) {}
// Returns the underlying terminal helper
std::shared_ptr<TerminalHelper> get_terminal_helper() {
return m_t_helper;
}
// shows the terminal. Call at each frame (in a more ImGui style, this would be something like ImGui::terminal(....);
// returns true if the terminal thinks it should still be displayed next frame, false if it thinks it should be hidden
// return value is true except if a command required a close, or if the "escape" key was pressed.
bool show(const std::vector<config_panels>& panels_order = DEFAULT_ORDER) noexcept;
// returns the command line history
const std::vector<std::string>& get_history() const noexcept {
return m_command_history;
}
// if invoked, the next call to "show" will return false
void set_should_close() noexcept {
m_close_request = true;
}
// clears all theme's optionals
void reset_colors() noexcept;
// returns current theme
struct theme& theme() {
return m_colors;
}
// set whether the autocompletion OSD should be above/under the text input, or if it should be disabled
void set_autocomplete_pos(position p) {
m_autocomplete_pos = p;
}
// returns current autocompletion position
position get_autocomplete_pos() const {
return m_autocomplete_pos;
}
#ifdef IMTERM_USE_FMT
// logs a colorless text to the message panel
// added as terminal message with info severity
template <typename... Args>
void add_formatted(const char* fmt, Args&&... args) {
add_text(fmt::format(fmt, std::forward<Args>(args)...));
}
// logs a colorless text to the message panel
// added as terminal message with warn severity
template <typename... Args>
void add_formatted_err(const char* fmt, Args&&... args) {
add_text_err(fmt::format(fmt, std::forward<Args>(args)...));
}
#endif
// logs a text to the message panel
// added as terminal message with info severity
void add_text(std::string str, unsigned int color_beg, unsigned int color_end);
// logs a text to the message panel, color spans from color_beg to the end of the message
// added as terminal message with info severity
void add_text(std::string str, unsigned int color_beg) {
add_text(str, color_beg, static_cast<unsigned>(str.size()));
}
// logs a colorless text to the message panel
// added as a terminal message with info severity,
void add_text(std::string str) {
add_text(str, 0, 0);
}
// logs a text to the message panel
// added as terminal message with warn severity
void add_text_err(std::string str, unsigned int color_beg, unsigned int color_end);
// logs a text to the message panel, color spans from color_beg to the end of the message
// added as terminal message with warn severity
void add_text_err(std::string str, unsigned int color_beg) {
add_text_err(str, color_beg, static_cast<unsigned>(str.size()));
}
// logs a colorless text to the message panel
// added as terminal message with warn severity
void add_text_err(std::string str) {
add_text_err(str, 0, 0);
}
// logs a message to the message panel
void add_message(const message& msg) {
add_message(message{msg});
}
void add_message(message&& msg);
// clears the message panel
void clear();
message::severity::severity_t log_level() noexcept {
return static_cast<message::severity::severity_t>(m_level + static_cast<int>(m_lowest_log_level_val));
}
void log_level(message::severity::severity_t new_level) noexcept {
if (m_lowest_log_level_val > new_level) {
set_min_log_level(new_level);
}
m_level = new_level - m_lowest_log_level_val;
}
// returns the text used to label the button that clears the message panel
// set it to an empty optional if you don't want the button to be displayed
std::optional<std::string>& clear_text() noexcept {
return m_clear_text;
}
// returns the text used to label the checkbox enabling or disabling message panel auto scrolling
// set it to an empty optional if you don't want the checkbox to be displayed
std::optional<std::string>& autoscroll_text() noexcept {
return m_autoscroll_text;
}
// returns the text used to label the checkbox enabling or disabling message panel text auto wrap
// set it to an empty optional if you don't want the checkbox to be displayed
std::optional<std::string>& autowrap_text() noexcept {
return m_autowrap_text;
}
// returns the text used to label the drop down list used to select the minimum severity to be displayed
// set it to an empty optional if you don't want the drop down list to be displayed
std::optional<std::string>& log_level_text() noexcept {
return m_log_level_text;
}
// returns the text used to label the text input used to filter out logs
// set it to an empty optional if you don't want the filter to be displayed
std::optional<std::string>& filter_hint() noexcept {
return m_filter_hint;
}
// allows you to set the text in the log_level drop down list
// the std::string_view/s are copied, so you don't need to manage their life-time
// set log_level_text() to an empty optional if you want to disable the drop down list
void set_level_list_text(std::string_view trace_str, std::string_view debug_str, std::string_view info_str,
std::string_view warn_str, std::string_view err_str, std::string_view critical_str, std::string_view none_str);
// sets the maximum verbosity a user can set in the terminal with the log_level drop down list
// for instance, if you pass 'info', the user will be able to select 'info','warning','error', 'critical', and 'none',
// but will never be able to view 'trace' and 'debug' messages
void set_min_log_level(message::severity::severity_t level);
// Adds custom flags to the terminal window
void set_flags(ImGuiWindowFlags flags) noexcept {
m_flags = flags;
}
// Sets the maximum number of saved messages
void set_max_log_len(std::vector<message>::size_type max_size);
// Sets the size of the terminal
void set_size(unsigned int x, unsigned int y) noexcept {
set_width(x);
set_height(y);
}
void set_size(ImVec2 sz) noexcept {
assert(sz.x >= 0);
assert(sz.y >= 0);
set_size(static_cast<unsigned>(sz.x), static_cast<unsigned>(sz.y));
}
void set_width(unsigned int x) noexcept {
m_base_width = x;
m_update_width = true;
}
void set_height(unsigned int y) noexcept {
m_base_height = y;
m_update_height = true;
}
// Get last window size
ImVec2 get_size() const noexcept {
return m_current_size;
}
// Allows / disallow resize on a given axis
void allow_x_resize() noexcept {
m_allow_x_resize = true;
}
void allow_y_resize() noexcept {
m_allow_y_resize = true;
}
void disallow_x_resize() noexcept {
m_allow_x_resize = false;
}
void disallow_y_resize() noexcept {
m_allow_y_resize = false;
}
void set_x_resize_allowance(bool allowed) noexcept {
m_allow_x_resize = allowed;
}
void set_y_resize_allowance(bool allowed) noexcept {
m_allow_y_resize = allowed;
}
// executes a statement, simulating user input
// returns false if given string is too long to be interpreted
// if true is returned, any text inputed by the user is overridden
bool execute(std::string_view str) noexcept {
if (str.size() <= m_command_buffer.size()) {
std::copy(str.begin(), str.end(), m_command_buffer.begin());
m_buffer_usage = str.size();
call_command();
m_buffer_usage = 0;
m_command_buffer[0] = '\0';
return true;
}
return false;
}
private:
explicit terminal(value_type& arg_value, const char * window_name_, int base_width_, int base_height_, std::shared_ptr<TerminalHelper> th, terminal_helper_is_valid&&);
void try_log(std::string_view str, message::type type);
void compute_text_size() noexcept;
void display_settings_bar(const std::vector<config_panels>& panels_order) noexcept;
void display_messages() noexcept;
void display_command_line() noexcept;
// displaying command_line itself
void show_input_text() noexcept;
void handle_unfocus() noexcept;
void show_autocomplete() noexcept;
void call_command() noexcept;
void push_message(message&&);
std::optional<std::string> resolve_history_reference(std::string_view str, bool& modified) const noexcept;
std::pair<bool, std::string> resolve_history_references(std::string_view str, bool& modified) const;
static int command_line_callback_st(ImGuiInputTextCallbackData * data) noexcept;
int command_line_callback(ImGuiInputTextCallbackData * data) noexcept;
static int try_push_style(ImGuiCol col, const std::optional<ImVec4>& color) {
if (color) {
ImGui::PushStyleColor(col, *color);
return 1;
}
return 0;
}
static int try_push_style(ImGuiCol col, const std::optional<theme::constexpr_color>& color) {
if (color) {
ImGui::PushStyleColor(col, color->imv4());
return 1;
}
return 0;
}
int is_space(std::string_view str) const;
bool is_digit(char c) const;
unsigned long get_length(std::string_view str) const;
// Returns a vector containing each element that were space separated
// Returns an empty optional if a '"' char was not matched with a closing '"',
// except if ignore_non_match was set to true
std::optional<std::vector<std::string>> split_by_space(std::string_view in, bool ignore_non_match = false) const;
inline void try_lock()
{
while (m_flag.test_and_set(std::memory_order_seq_cst)) {}
}
inline void try_unlock()
{
m_flag.clear(std::memory_order_seq_cst);
}
////////////
value_type& m_argument_value;
mutable std::shared_ptr<TerminalHelper> m_t_helper;
bool m_should_show_next_frame{true};
bool m_close_request{false};
const char * const m_window_name;
ImGuiWindowFlags m_flags{ImGuiWindowFlags_None};
int m_base_width;
int m_base_height;
bool m_update_width{true};
bool m_update_height{true};
bool m_allow_x_resize{true};
bool m_allow_y_resize{true};
ImVec2 m_current_size{0.f, 0.f};
struct theme m_colors{};
// configuration
bool m_autoscroll{true}; // TODO: accessors
bool m_autowrap{true}; // TODO: accessors
std::vector<std::string>::size_type m_last_size{0u};
int m_level{message::severity::trace}; // TODO: accessors
#ifdef IMTERM_ENABLE_REGEX
bool m_regex_search{true}; // TODO: accessors, button
#endif
std::optional<std::string> m_autoscroll_text;
std::optional<std::string> m_clear_text;
std::optional<std::string> m_log_level_text;
std::optional<std::string> m_autowrap_text;
std::optional<std::string> m_filter_hint;
std::string m_level_list_text{};
const char* m_longest_log_level{nullptr}; // points to the longest log level, in m_level_list_text
const char* m_lowest_log_level{nullptr}; // points to the lowest log level possible, in m_level_list_text
message::severity::severity_t m_lowest_log_level_val{message::severity::trace};
small_buffer_type m_log_text_filter_buffer{};
small_buffer_type::size_type m_log_text_filter_buffer_usage{0u};
// message panel variables
unsigned long m_last_flush_at_history{0u}; // for the [-n] indicator on command line
bool m_flush_bit{false};
std::vector<message> m_logs{};
std::vector<message>::size_type m_max_log_len{5'000}; // TODO: command
std::vector<message>::size_type m_current_log_oldest_idx{0};
// command line variables
buffer_type m_command_buffer{};
buffer_type::size_type m_buffer_usage{0u}; // max accessible: command_buffer[buffer_usage - 1]
// (buffer_usage might be 0 for empty string)
buffer_type::size_type m_previous_buffer_usage{0u};
bool m_should_take_focus{false};
ImGuiID m_previously_active_id{0u};
ImGuiID m_input_text_id{0u};
// autocompletion
std::vector<command_type_cref> m_current_autocomplete{};
std::vector<std::string> m_current_autocomplete_strings{};
std::string_view m_autocomlete_separator{" | "};
position m_autocomplete_pos{position::down};
bool m_command_entered{false};
// command line: completion using history
std::string m_command_line_backup{};
std::string_view m_command_line_backup_prefix{};
std::vector<std::string> m_command_history{};
std::optional<std::vector<std::string>::iterator> m_current_history_selection{};
bool m_ignore_next_textinput{false};
bool m_has_focus{false};
std::atomic_flag m_flag;
};
}
#include "terminal.tpp"
#undef IMTERM_FMT_INCLUDED
#endif //IMTERM_TERMINAL_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,346 @@
#ifndef TERMINAL_HELPER_HPP
#define TERMINAL_HELPER_HPP
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Copyright C 2019, Lucas Lazare ///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ///
/// files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, ///
/// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software ///
/// is furnished to do so, subject to the following conditions: ///
/// ///
/// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ///
/// ///
/// The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the ///
/// warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or ///
/// copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, ///
/// arising from, out of or in connection with the software or the use or other dealings in the Software. ///
/// ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <set>
#include <array>
#include "terminal.hpp"
#if __has_include("spdlog/spdlog.h")
#include "spdlog/common.h"
#include "spdlog/formatter.h"
#include "spdlog/pattern_formatter.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/sinks/base_sink.h"
#define IMTERM_SPDLOG_INCLUDED
// helpers
namespace ImTerm::details {
constexpr message::severity::severity_t to_imterm_severity(spdlog::level::level_enum);
constexpr spdlog::level::level_enum to_spdlog_severity(message::severity::severity_t);
constexpr spdlog::level::level_enum to_spdlog_severity(message::type);
inline ImTerm::message to_imterm_msg(const spdlog::details::log_msg&);
}
#endif
namespace ImTerm {
// terminal_helper_example is meant to be an example
// if you want to inherit from one, pick term::basic_terminal_helper (see below)
template <typename T>
class terminal_helper_example {
public:
using value_type = T; // < Mandatory, this type will be passed to commands via argument_type
using term_t = ImTerm::terminal<terminal_helper_example>;
using command_type = ImTerm::command_t<ImTerm::terminal<terminal_helper_example>>;
using argument_type = ImTerm::argument_t<ImTerm::terminal<terminal_helper_example>>;
using command_type_cref = std::reference_wrapper<const command_type>;
// mandatory : return every command starting by prefix
std::vector<command_type_cref> find_commands_by_prefix(std::string_view prefix) {
auto compare_by_name = [](const command_type& cmd) { return cmd.name; };
auto map_to_cref = [](const command_type& cmd) { return std::cref(cmd); };
return misc::prefix_search(prefix, cmd_list.begin(), cmd_list.end(), std::move(compare_by_name), std::move(map_to_cref));
}
// mandatory : return every command starting by the text formed by [beg, end)
std::vector<command_type_cref> find_commands_by_prefix(const char* beg, const char* end) {
return find_commands_by_prefix({beg, static_cast<unsigned>(end - beg)});
}
// mandatory: returns the full command list
std::vector<command_type_cref> list_commands() {
return find_commands_by_prefix(std::string_view{});
}
// mandatory: formats the given string.
// msg type is either user_input, error, or cmd_history_completion
// return an empty optional if you do not want the string to be logged
// message's members 'is_term_message' and 'severity' are ignored
std::optional<ImTerm::message> format(std::string str, [[maybe_unused]] ImTerm::message::type msg_type) {
ImTerm::message msg;
msg.color_beg = msg.color_end = 0u;
msg.value = std::move(str);
return {std::move(msg)}; // other fields are ignored
}
// optional : to be friendlier with non ascii encoding
// return value : 0 if str does not begin by a space
// otherwise, the number of characters participating in the representation of the beginning space
// should not return a value > str.size()
// the passed string_view is never empty (at least one char)
// int is_space(std::string_view str) const {
// return str[0] == ' ';
// }
// optional : to be friendlier with non ascii encoding
// return value : number of glyphs represented in str (== str.size() for ascii)
// unsigned long get_length(std::string_view str) {
// return str.size();
// }
// optional : called right after terminal's construction. You may store the terminal for future usage
// void set_terminal(term_t& term) {
// }
// command samples (implemented as static methods, but they can be outside of a class, if you will to)
static std::vector<std::string> no_completion(argument_type&) { return {}; }
static void clear(argument_type& arg) {
arg.term.clear();
}
static void echo(argument_type& arg) {
std::string str{};
str = arg.command_line[1];
for (auto it = std::next(arg.command_line.begin(), 2) ; it != arg.command_line.end() ; ++it) {
str.reserve(str.size() + it->size() + 1);
str += ' ';
str += *it;
}
message msg;
msg.value = std::move(str);
msg.color_beg = msg.color_end = 0; // color is disabled when color_beg == color_end
// other parameters are ignored
arg.term.add_message(std::move(msg));
}
static constexpr std::array<command_type, 2> cmd_list = {
command_type{"clear ", "clears the screen", clear, no_completion},
command_type{"echo ", "prints text to the screen", echo, no_completion},
};
};
// Basic terminal helper
// You may inherit to save some hassle
// Template parameter TerminalHelper is in most cases the derived class (and should be if you don't know what to put)
// Template parameter Value is the type passed to commands together with the other arguments
// You may add commands with the 'add_command_' method.
// Refer to terminal_helper_example (see above) for a commented example
template <typename TerminalHelper, typename Value>
class basic_terminal_helper {
public:
using value_type = Value;
using term_t = ImTerm::terminal<TerminalHelper>;
using command_type = ImTerm::command_t<ImTerm::terminal<TerminalHelper>>;
using argument_type = ImTerm::argument_t<ImTerm::terminal<TerminalHelper>>;
using command_type_cref = std::reference_wrapper<const command_type>;
basic_terminal_helper() = default;
basic_terminal_helper(const basic_terminal_helper&) = default;
basic_terminal_helper(basic_terminal_helper&&) noexcept = default;
std::vector<command_type_cref> find_commands_by_prefix(std::string_view prefix) {
auto compare_name = [](const command_type& cmd) { return cmd.name; };
auto map_to_cref = [](const command_type& cmd) { return std::cref(cmd); };
return misc::prefix_search(prefix, cmd_list_.begin(), cmd_list_.end(), std::move(compare_name),
std::move(map_to_cref));
}
std::vector<command_type_cref> find_commands_by_prefix(const char * beg, const char * end) {
return find_commands_by_prefix({beg, static_cast<unsigned>(end - beg)});
}
std::vector<command_type_cref> list_commands() {
std::vector<command_type_cref> ans;
ans.reserve(cmd_list_.size());
for (const command_type& cmd : cmd_list_) {
ans.emplace_back(cmd);
}
return ans;
}
std::optional<ImTerm::message> format(std::string str, ImTerm::message::type) {
ImTerm::message msg;
msg.value = std::move(str);
msg.color_beg = msg.color_end = 0u;
return {std::move(msg)};
}
protected:
void add_command_(const command_type& cmd) {
cmd_list_.emplace(cmd);
}
std::set<command_type> cmd_list_{};
};
#ifdef IMTERM_SPDLOG_INCLUDED
// Basic spdlog terminal helper inheriting spdlog::sinks::sink (logging messages to the terminal)
// You may inherit to save some hassle
// Template parameter TerminalHelper is in most cases the derived class (and should be if you don't know what to put)
// Template parameter Value is the type passed to commands together with the other arguments
// Template parameter Mutex is passed to spdlog::sinks::base_sink
// You may add commands with the 'add_command_' method.
// Refer to terminal_helper_example (see above) for a commented example
// should not be used after move
template <typename TerminalHelper, typename Value, typename Mutex>
class basic_spdlog_terminal_helper : public basic_terminal_helper<TerminalHelper, Value>, public spdlog::sinks::base_sink<Mutex> {
using SinkBase = spdlog::sinks::base_sink<Mutex>;
using TermHBase = basic_terminal_helper<TerminalHelper,Value>;
public:
using typename TermHBase::term_t;
explicit basic_spdlog_terminal_helper(std::string terminal_to_terminal_logger_name = "ImTerm Terminal")
: logger_name_{std::move(terminal_to_terminal_logger_name)} {
set_terminal_pattern_("%T.%e - [%^command line%$]: %v", message::type::error);
set_terminal_pattern_("%T.%e - %^%v%$", message::type::user_input);
set_terminal_pattern_("%T.%e - %^%v%$", message::type::cmd_history_completion);
}
basic_spdlog_terminal_helper(basic_spdlog_terminal_helper&& other) noexcept
: SinkBase{}
, TermHBase(std::move(other))
, terminal_{std::exchange(other.terminal_, nullptr)}
, terminal_formatter_{std::move(other.terminal_formatter_)}
, logger_name_{std::move(other.logger_name_)}
{
SinkBase::set_level(other.level());
SinkBase::set_formatter(std::move(other.formatter_));
}
virtual ~basic_spdlog_terminal_helper() noexcept = default;
std::optional<ImTerm::message> format(std::string str, [[maybe_unused]] ImTerm::message::type type) {
spdlog::details::log_msg msg(logger_name_, {}, str);
spdlog::memory_buf_t buff{};
terminal_formatter_[static_cast<int>(type)]->format(msg, buff);
ImTerm::message term_msg = details::to_imterm_msg(msg);
term_msg.value = fmt::to_string(buff);
return term_msg;
}
// this method is called automatically right after ImTerm::terminal's construction
// used to sink logs to the message panel
void set_terminal(term_t& term) {
terminal_ = &term;
}
// set logging pattern per message type, for feed-back messages from the terminal
void set_terminal_pattern(const std::string& pattern, ImTerm::message::type type) {
std::lock_guard<Mutex> lock(SinkBase::mutex_);
set_terminal_pattern_(std::make_unique<spdlog::pattern_formatter>(pattern), type);
}
void set_terminal_formatter(std::unique_ptr<spdlog::formatter>&& terminal_formatter, ImTerm::message::type type) {
std::lock_guard<Mutex> lock(SinkBase::mutex_);
set_terminal_formatter_(std::move(terminal_formatter), type);
}
protected:
void set_terminal_pattern_(const std::string& pattern, ImTerm::message::type type) {
set_terminal_formatter_(std::make_unique<spdlog::pattern_formatter>(pattern), type);
}
void set_terminal_formatter_(std::unique_ptr<spdlog::formatter>&& terminal_formatter, ImTerm::message::type type) {
terminal_formatter_[static_cast<int>(type)] = std::move(terminal_formatter);
}
void sink_it_(const spdlog::details::log_msg& msg) override {
if (msg.level == spdlog::level::off) {
return;
}
assert(terminal_ != nullptr);
spdlog::memory_buf_t buff{};
SinkBase::formatter_->format(msg, buff);
terminal_->add_message({details::to_imterm_severity(msg.level), fmt::to_string(buff)
, msg.color_range_start, msg.color_range_end, false});
}
void flush_() override {}
term_t* terminal_{};
std::array<std::unique_ptr<spdlog::formatter>, 3> terminal_formatter_{}; // user_input, error, cmd_history_completion (c.f. ImTerm::message::type)
std::string logger_name_;
};
namespace details {
constexpr message::severity::severity_t to_imterm_severity(spdlog::level::level_enum lvl) {
assert(lvl != spdlog::level::off);
if constexpr (
ImTerm::message::severity::trace == static_cast<int>(spdlog::level::trace) &&
ImTerm::message::severity::debug == static_cast<int>(spdlog::level::debug) &&
ImTerm::message::severity::info == static_cast<int>(spdlog::level::info) &&
ImTerm::message::severity::warn == static_cast<int>(spdlog::level::warn) &&
ImTerm::message::severity::err == static_cast<int>(spdlog::level::err) &&
ImTerm::message::severity::critical == static_cast<int>(spdlog::level::critical)) {
return static_cast<message::severity::severity_t>(lvl);
} else {
switch (lvl) {
case spdlog::level::trace: return ImTerm::message::severity::trace;
case spdlog::level::debug: return ImTerm::message::severity::debug;
case spdlog::level::info: return ImTerm::message::severity::info;
case spdlog::level::warn: return ImTerm::message::severity::warn;
case spdlog::level::err: return ImTerm::message::severity::err;
case spdlog::level::critical: return ImTerm::message::severity::critical;
default:
assert(false);
return {};
}
}
}
constexpr spdlog::level::level_enum to_spdlog_severity(message::severity::severity_t lvl) {
if constexpr (
ImTerm::message::severity::trace == static_cast<int>(spdlog::level::trace) &&
ImTerm::message::severity::debug == static_cast<int>(spdlog::level::debug) &&
ImTerm::message::severity::info == static_cast<int>(spdlog::level::info) &&
ImTerm::message::severity::warn == static_cast<int>(spdlog::level::warn) &&
ImTerm::message::severity::err == static_cast<int>(spdlog::level::err) &&
ImTerm::message::severity::critical == static_cast<int>(spdlog::level::critical)) {
return static_cast<spdlog::level::level_enum>(lvl);
} else {
switch (lvl) {
case ImTerm::message::severity::trace: return spdlog::level::trace;
case ImTerm::message::severity::debug: return spdlog::level::debug;
case ImTerm::message::severity::info: return spdlog::level::info;
case ImTerm::message::severity::warn: return spdlog::level::warn;
case ImTerm::message::severity::err: return spdlog::level::err;
case ImTerm::message::severity::critical: return spdlog::level::critical;
}
}
}
inline ImTerm::message to_imterm_msg(const spdlog::details::log_msg& msg) {
ImTerm::message term_msg{};
term_msg.severity = to_imterm_severity(msg.level);
term_msg.color_beg = msg.color_range_start;
term_msg.color_end = msg.color_range_end;
return term_msg;
}
}
#endif
}
#undef IMTERM_SPDLOG_INCLUDED
#endif //TERMINAL_HELPER_HPP
+266
View File
@@ -0,0 +1,266 @@
#ifndef IMTERM_UTILS_HPP
#define IMTERM_UTILS_HPP
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Copyright C 2019, Lucas Lazare ///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation ///
/// files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, ///
/// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software ///
/// is furnished to do so, subject to the following conditions: ///
/// ///
/// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ///
/// ///
/// The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the ///
/// warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or ///
/// copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, ///
/// arising from, out of or in connection with the software or the use or other dealings in the Software. ///
/// ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <string>
#include <string_view>
#include <array>
#include <optional>
#include <array>
#include <imgui.h>
#include "misc.hpp"
namespace ImTerm {
// argument passed to commands
template<typename Terminal>
struct argument_t {
using value_type = misc::non_void_t<typename Terminal::value_type>;
value_type& val; // misc::details::structured_void if Terminal::value_type is void, reference to Terminal::value_type otherwise
Terminal& term; // reference to the ImTerm::terminal that called the command
std::vector<std::string> command_line; // list of arguments the user specified in the command line. command_line[0] is the command name
};
// structure used to represent a command
template<typename Terminal>
struct command_t {
using command_function = void (*)(argument_t<Terminal>&);
using further_completion_function = std::vector<std::string> (*)(argument_t<Terminal>& argument_line);
std::string_view name{}; // name of the command
std::string_view description{}; // short description
command_function call{}; // function doing whatever you want
further_completion_function complete{}; // function called when users starts typing in arguments for your command
// return a vector of strings containing possible completions.
friend constexpr bool operator<(const command_t& lhs, const command_t& rhs) {
return lhs.name < rhs.name;
}
friend constexpr bool operator<(const command_t& lhs, const std::string_view& rhs) {
return lhs.name < rhs;
}
friend constexpr bool operator<(const std::string_view& lhs, const command_t& rhs) {
return lhs < rhs.name;
}
};
struct message {
enum class type {
user_input, // terminal wants to log user input
error, // terminal wants to log an error in user input
cmd_history_completion, // terminal wants to log that it replaced "!:*" family input in the appropriate string
};
struct severity {
enum severity_t { // done this way to be used as array index without a cast
trace,
debug,
info,
warn,
err,
critical
};
};
severity::severity_t severity; // severity of the message
std::string value; // text to be displayed
// the actual color used depends on the message's severity
size_t color_beg; // color begins at value.data() + color_beg
size_t color_end; // color ends at value.data() + color_end - 1
// if color_beg == color_end, nothing is colorized.
bool is_term_message; // if set to true, current msg is considered to be originated from the terminal,
// never filtered out by severity filter, and applying for different rules regarding colors.
// severity is also ignored for such messages
};
enum class config_panels {
autoscroll,
autowrap,
clearbutton,
filter,
long_filter, // like filter, but takes up space
loglevel,
blank, // invisible panel that takes up place, aligning items to the right. More than one can be used, splitting up the consumed space
// ie: {clearbutton (C), blank, filter (F), blank, loglevel (L)} will result in the layout [C F L]
// Shares space with long_filter
none
};
enum class position {
up,
down,
nowhere // disabled
};
// Various settable colors for the terminal
// if an optional is empty, current ImGui color will be used
struct theme {
struct constexpr_color {
float r,g,b,a;
ImVec4 imv4() const {
return {r,g,b,a};
}
};
std::string_view name; // if you want to give a name to the theme
std::optional<constexpr_color> text; // global text color
std::optional<constexpr_color> window_bg; // ImGuiCol_WindowBg & ImGuiCol_ChildBg
std::optional<constexpr_color> border; // ImGuiCol_Border
std::optional<constexpr_color> border_shadow; // ImGuiCol_BorderShadow
std::optional<constexpr_color> button; // ImGuiCol_Button
std::optional<constexpr_color> button_hovered; // ImGuiCol_ButtonHovered
std::optional<constexpr_color> button_active; // ImGuiCol_ButtonActive
std::optional<constexpr_color> frame_bg; // ImGuiCol_FrameBg
std::optional<constexpr_color> frame_bg_hovered; // ImGuiCol_FrameBgHovered
std::optional<constexpr_color> frame_bg_active; // ImGuiCol_FrameBgActive
std::optional<constexpr_color> text_selected_bg; // ImGuiCol_TextSelectedBg, for text input field
std::optional<constexpr_color> check_mark; // ImGuiCol_CheckMark
std::optional<constexpr_color> title_bg; // ImGuiCol_TitleBg
std::optional<constexpr_color> title_bg_active; // ImGuiCol_TitleBgActive
std::optional<constexpr_color> title_bg_collapsed; // ImGuiCol_TitleBgCollapsed
std::optional<constexpr_color> message_panel; // logging panel
std::optional<constexpr_color> auto_complete_selected; // left-most text in the autocompletion OSD
std::optional<constexpr_color> auto_complete_non_selected; // every text but the left most in the autocompletion OSD
std::optional<constexpr_color> auto_complete_separator; // color for the separator in the autocompletion OSD
std::optional<constexpr_color> cmd_backlog; // color for message type user_input
std::optional<constexpr_color> cmd_history_completed; // color for message type cmd_history_completion
std::optional<constexpr_color> log_level_drop_down_list_bg; // ImGuiCol_PopupBg
std::optional<constexpr_color> log_level_active; // ImGuiCol_HeaderActive
std::optional<constexpr_color> log_level_hovered; // ImGuiCol_HeaderHovered
std::optional<constexpr_color> log_level_selected; // ImGuiCol_Header
std::optional<constexpr_color> scrollbar_bg; // ImGuiCol_ScrollbarBg
std::optional<constexpr_color> scrollbar_grab; // ImGuiCol_ScrollbarGrab
std::optional<constexpr_color> scrollbar_grab_active; // ImGuiCol_ScrollbarGrabActive
std::optional<constexpr_color> scrollbar_grab_hovered; // ImGuiCol_ScrollbarGrabHovered
std::optional<constexpr_color> filter_hint; // ImGuiCol_TextDisabled
std::optional<constexpr_color> filter_text; // user input in log filter
std::optional<constexpr_color> matching_text; // text matching the log filter
std::array<std::optional<constexpr_color>, message::severity::critical + 1> log_level_colors{}; // colors by severity
};
namespace themes {
constexpr theme light = {
"Light Rainbow",
theme::constexpr_color{0.100f, 0.100f, 0.100f, 1.000f}, //text
theme::constexpr_color{0.243f, 0.443f, 0.624f, 1.000f}, //window_bg
theme::constexpr_color{0.600f, 0.600f, 0.600f, 1.000f}, //border
theme::constexpr_color{0.000f, 0.000f, 0.000f, 0.000f}, //border_shadow
theme::constexpr_color{0.902f, 0.843f, 0.843f, 0.875f}, //button
theme::constexpr_color{0.824f, 0.765f, 0.765f, 0.875f}, //button_hovered
theme::constexpr_color{0.627f, 0.569f, 0.569f, 0.875f}, //button_active
theme::constexpr_color{0.902f, 0.843f, 0.843f, 0.875f}, //frame_bg
theme::constexpr_color{0.824f, 0.765f, 0.765f, 0.875f}, //frame_bg_hovered
theme::constexpr_color{0.627f, 0.569f, 0.569f, 0.875f}, //frame_bg_active
theme::constexpr_color{0.260f, 0.590f, 0.980f, 0.350f}, //text_selected_bg
theme::constexpr_color{0.843f, 0.000f, 0.373f, 1.000f}, //check_mark
theme::constexpr_color{0.243f, 0.443f, 0.624f, 0.850f}, //title_bg
theme::constexpr_color{0.165f, 0.365f, 0.506f, 1.000f}, //title_bg_active
theme::constexpr_color{0.243f, 0.443f, 0.624f, 0.850f}, //title_collapsed
theme::constexpr_color{0.902f, 0.843f, 0.843f, 0.875f}, //message_panel
theme::constexpr_color{0.196f, 1.000f, 0.196f, 1.000f}, //auto_complete_selected
theme::constexpr_color{0.000f, 0.000f, 0.000f, 1.000f}, //auto_complete_non_selected
theme::constexpr_color{0.000f, 0.000f, 0.000f, 0.392f}, //auto_complete_separator
theme::constexpr_color{0.519f, 0.118f, 0.715f, 1.000f}, //cmd_backlog
theme::constexpr_color{1.000f, 0.430f, 0.059f, 1.000f}, //cmd_history_completed
theme::constexpr_color{0.901f, 0.843f, 0.843f, 0.784f}, //log_level_drop_down_list_bg
theme::constexpr_color{0.443f, 0.705f, 1.000f, 1.000f}, //log_level_active
theme::constexpr_color{0.443f, 0.705f, 0.784f, 0.705f}, //log_level_hovered
theme::constexpr_color{0.443f, 0.623f, 0.949f, 1.000f}, //log_level_selected
theme::constexpr_color{0.000f, 0.000f, 0.000f, 0.000f}, //scrollbar_bg
theme::constexpr_color{0.470f, 0.470f, 0.588f, 1.000f}, //scrollbar_grab
theme::constexpr_color{0.392f, 0.392f, 0.509f, 1.000f}, //scrollbar_grab_active
theme::constexpr_color{0.509f, 0.509f, 0.666f, 1.000f}, //scrollbar_grab_hovered
theme::constexpr_color{0.470f, 0.470f, 0.470f, 1.000f}, //filter_hint
theme::constexpr_color{0.100f, 0.100f, 0.100f, 1.000f}, //filter_text
theme::constexpr_color{0.549f, 0.196f, 0.039f, 1.000f}, //matching_text
{
theme::constexpr_color{0.078f, 0.117f, 0.764f, 1.f}, // log_level::trace
theme::constexpr_color{0.100f, 0.100f, 0.100f, 1.f}, // log_level::debug
theme::constexpr_color{0.301f, 0.529f, 0.000f, 1.f}, // log_level::info
theme::constexpr_color{0.784f, 0.431f, 0.058f, 1.f}, // log_level::warning
theme::constexpr_color{0.901f, 0.117f, 0.117f, 1.f}, // log_level::error
theme::constexpr_color{0.901f, 0.117f, 0.117f, 1.f}, // log_level::critical
}
};
constexpr theme cherry {
"Dark Cherry",
theme::constexpr_color{0.649f, 0.661f, 0.669f, 1.000f}, //text
theme::constexpr_color{0.130f, 0.140f, 0.170f, 1.000f}, //window_bg
theme::constexpr_color{0.310f, 0.310f, 1.000f, 0.000f}, //border
theme::constexpr_color{0.000f, 0.000f, 0.000f, 0.000f}, //border_shadow
theme::constexpr_color{0.470f, 0.770f, 0.830f, 0.140f}, //button
theme::constexpr_color{0.455f, 0.198f, 0.301f, 0.860f}, //button_hovered
theme::constexpr_color{0.455f, 0.198f, 0.301f, 1.000f}, //button_active
theme::constexpr_color{0.200f, 0.220f, 0.270f, 1.000f}, //frame_bg
theme::constexpr_color{0.455f, 0.198f, 0.301f, 0.780f}, //frame_bg_hovered
theme::constexpr_color{0.455f, 0.198f, 0.301f, 1.000f}, //frame_bg_active
theme::constexpr_color{0.455f, 0.198f, 0.301f, 0.430f}, //text_selected_bg
theme::constexpr_color{0.710f, 0.202f, 0.207f, 1.000f}, //check_mark
theme::constexpr_color{0.232f, 0.201f, 0.271f, 1.000f}, //title_bg
theme::constexpr_color{0.502f, 0.075f, 0.256f, 1.000f}, //title_bg_active
theme::constexpr_color{0.200f, 0.220f, 0.270f, 0.750f}, //title_bg_collapsed
theme::constexpr_color{0.100f, 0.100f, 0.100f, 0.500f}, //message_panel
theme::constexpr_color{1.000f, 1.000f, 1.000f, 1.000f}, //auto_complete_selected
theme::constexpr_color{0.500f, 0.450f, 0.450f, 1.000f}, //auto_complete_non_selected
theme::constexpr_color{0.600f, 0.600f, 0.600f, 1.000f}, //auto_complete_separator
theme::constexpr_color{0.860f, 0.930f, 0.890f, 1.000f}, //cmd_backlog
theme::constexpr_color{0.153f, 0.596f, 0.498f, 1.000f}, //cmd_history_completed
theme::constexpr_color{0.100f, 0.100f, 0.100f, 0.860f}, //log_level_drop_down_list_bg
theme::constexpr_color{0.730f, 0.130f, 0.370f, 1.000f}, //log_level_active
theme::constexpr_color{0.450f, 0.190f, 0.300f, 0.430f}, //log_level_hovered
theme::constexpr_color{0.730f, 0.130f, 0.370f, 0.580f}, //log_level_selected
theme::constexpr_color{0.000f, 0.000f, 0.000f, 0.000f}, //scrollbar_bg
theme::constexpr_color{0.690f, 0.690f, 0.690f, 0.800f}, //scrollbar_grab
theme::constexpr_color{0.490f, 0.490f, 0.490f, 0.800f}, //scrollbar_grab_active
theme::constexpr_color{0.490f, 0.490f, 0.490f, 1.000f}, //scrollbar_grab_hovered
theme::constexpr_color{0.649f, 0.661f, 0.669f, 1.000f}, //filter_hint
theme::constexpr_color{1.000f, 1.000f, 1.000f, 1.000f}, //filter_text
theme::constexpr_color{0.490f, 0.240f, 1.000f, 1.000f}, //matching_text
{
theme::constexpr_color{0.549f, 0.561f, 0.569f, 1.f}, // log_level::trace
theme::constexpr_color{0.153f, 0.596f, 0.498f, 1.f}, // log_level::debug
theme::constexpr_color{0.459f, 0.686f, 0.129f, 1.f}, // log_level::info
theme::constexpr_color{0.839f, 0.749f, 0.333f, 1.f}, // log_level::warning
theme::constexpr_color{1.000f, 0.420f, 0.408f, 1.f}, // log_level::error
theme::constexpr_color{1.000f, 0.420f, 0.408f, 1.f}, // log_level::critical
},
};
constexpr std::array list {
cherry,
light
};
}
}
#endif //IMTERM_UTILS_HPP
+17 -17
View File
@@ -17,14 +17,14 @@
#include "../detours/detours.h"
#include "wrapped_swapchain.h"
//#include "wrapped_swapchain.h"
// Dx12 overlay code adoptes from
// https://github.com/bruhmoment21/UniversalHookX
// MipMap hooks
typedef void(__fastcall* PFN_CreateSampler)(ID3D12Device* device, const D3D12_SAMPLER_DESC* pDesc, D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor);
static inline PFN_CreateSampler orgCreateSampler = nullptr;
static inline PFN_CreateSampler o_CreateSampler = nullptr;
typedef struct FfxFrameGenerationConfig
{
@@ -50,7 +50,7 @@ static ID3D12Resource* g_mainRenderTargetResource[NUM_BACK_BUFFERS] = { };
static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = { };
typedef void(WINAPI* PFN_ExecuteCommandLists)(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*);
typedef ULONG(WINAPI* PFN_Release)(IUnknown*);
typedef ULONG(WINAPI* PFN_CommandQueueRelease)(IUnknown*);
typedef void* (WINAPI* PFN_ffxGetDX12SwapchainPtr)(void* ffxSwapChain);
typedef int32_t(WINAPI* PFN_ffxCreateFrameinterpolationSwapchainForHwndDX12)(HWND hWnd, const DXGI_SWAP_CHAIN_DESC1* desc1,
@@ -69,7 +69,7 @@ static PFN_CreateCommandQueue oCreateCommandQueue = nullptr;
static PFN_CreateDXGIFactory oCreateDXGIFactory = nullptr;
static PFN_CreateDXGIFactory1 oCreateDXGIFactory1 = nullptr;
static PFN_CreateDXGIFactory2 oCreateDXGIFactory2 = nullptr;
static PFN_Release oCommandQueueRelease = nullptr;
static PFN_CommandQueueRelease oCommandQueueRelease = nullptr;
// Dx12
static PFN_Present oPresent_Dx12 = nullptr;
@@ -410,28 +410,28 @@ static void hkCreateSampler(ID3D12Device* device, const D3D12_SAMPLER_DESC* pDes
Config::Instance()->lastMipBias = newDesc.MipLODBias;
}
return orgCreateSampler(device, &newDesc, DestDescriptor);
return o_CreateSampler(device, &newDesc, DestDescriptor);
}
static void HookToDevice(ID3D12Device* InDevice)
{
if (!ImGuiOverlayDx12::IsEarlyBind() && orgCreateSampler != nullptr || InDevice == nullptr)
if (!ImGuiOverlayDx12::IsEarlyBind() && o_CreateSampler != nullptr || InDevice == nullptr)
return;
// Get the vtable pointer
PVOID* pVTable = *(PVOID**)InDevice;
// Get the address of the SetComputeRootSignature function from the vtable
orgCreateSampler = (PFN_CreateSampler)pVTable[22];
o_CreateSampler = (PFN_CreateSampler)pVTable[22];
// Apply the detour
if (orgCreateSampler != nullptr)
if (o_CreateSampler != nullptr)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)orgCreateSampler, hkCreateSampler);
DetourAttach(&(PVOID&)o_CreateSampler, hkCreateSampler);
DetourTransactionCommit();
}
@@ -1048,7 +1048,7 @@ static ffxReturnCode_t hkFfxConfigure_FSR3(ffxContext* context, const ffxConfigu
#pragma region Hooks for ExecuteCommandLists
static void WINAPI hkExecuteCommandLists_Dx12(ID3D12CommandQueue* pCommandQueue, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists)
static void WINAPI hkExecuteCommandLists(ID3D12CommandQueue* pCommandQueue, UINT NumCommandLists, ID3D12CommandList* const* ppCommandLists)
{
if (_cqDx12 == nullptr && _dlssCommandList != nullptr)
{
@@ -1327,7 +1327,7 @@ static HRESULT WINAPI hkCreateCommandQueue(ID3D12Device* This, const D3D12_COMMA
void** pCommandQueueVTable = *reinterpret_cast<void***>(cq);
oCommandQueueRelease = (PFN_Release)pCommandQueueVTable[2];
oCommandQueueRelease = (PFN_CommandQueueRelease)pCommandQueueVTable[2];
oExecuteCommandLists_Dx12 = (PFN_ExecuteCommandLists)pCommandQueueVTable[10];
if (oExecuteCommandLists_Dx12 != nullptr)
@@ -1337,7 +1337,7 @@ static HRESULT WINAPI hkCreateCommandQueue(ID3D12Device* This, const D3D12_COMMA
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists_Dx12);
DetourAttach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists);
DetourAttach(&(PVOID&)oCommandQueueRelease, hkCommandQueueRelease);
DetourTransactionCommit();
@@ -1557,7 +1557,7 @@ static bool CheckDx12(ID3D12Device* InDevice)
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists_Dx12);
DetourAttach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists);
DetourTransactionCommit();
}
@@ -2550,7 +2550,7 @@ void DeatachAllHooks()
if (oExecuteCommandLists_Dx12 != nullptr)
{
DetourDetach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists_Dx12);
DetourDetach(&(PVOID&)oExecuteCommandLists_Dx12, hkExecuteCommandLists);
oExecuteCommandLists_Dx12 = nullptr;
}
@@ -2656,10 +2656,10 @@ void DeatachAllHooks()
offxFsr3ConfigureFrameGeneration_FSR3 = nullptr;
}
if (orgCreateSampler != nullptr)
if (o_CreateSampler != nullptr)
{
DetourDetach(&(PVOID&)orgCreateSampler, hkCreateSampler);
orgCreateSampler = nullptr;
DetourDetach(&(PVOID&)o_CreateSampler, hkCreateSampler);
o_CreateSampler = nullptr;
}
DetourTransactionCommit();
+3 -2
View File
@@ -25,8 +25,9 @@
// Enables logging of DLSS NV Parameters
//#define DLSS_PARAM_DUMP
inline HMODULE dllModule;
inline DWORD processId;
inline static HMODULE dllModule;
inline static HMODULE nvngxModule;
inline static DWORD processId;
#define LOG_TRACE(msg, ...) \
spdlog::trace(__FUNCTION__ " " msg, ##__VA_ARGS__)