DCS EFM ImGui (FmGui)  v1.2
Source only library for using ImGui with the DCS: World EFM API.
FmGui.cpp
1 /* =============================================================================
2  * DCS-EFM-ImGui, file: FmGui.cpp Created: 18-JUN-2022
3  *
4  * Copyright 2022-2023 Brian Hoffpauir, USA
5  * All rights reserved.
6  *
7  * Redistribution and use of this source file, with or without modification, is
8  * permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of this source file must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  * =============================================================================
28  */
29 #include "FmGui.hpp"
30 
31 #include <MinHook.h>
32 
33 #include <sstream>
34 #include <stack>
35 #include <algorithm>
36 
37 // DirectX headers here:
38 #include <d3d11.h>
39 
40 // ImGui Implementation Headers here:
41 #include <imgui_impl_dx11.h>
42 #include <imgui_impl_win32.h>
43 
44 // Simple linking solution for DirectX 11:
45 #pragma comment(lib, "d3d11.lib")
46 
47 // Function from imgui_impl_win32; defined elsewhere:
48 extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND s_hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
49 
50 using IDXGISwapChainPresentPtr = std::add_pointer<HRESULT FMGUI_FASTCALL(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags)>::type;
51 
52 namespace FmGui
53 {
54  // Functions
55  static LPVOID LookupSwapChainVTable(void);
56  static std::string MinHookStatusToStdString(MH_STATUS mhStatus);
57  static HRESULT FMGUI_FASTCALL SwapChainPresentImpl(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags);
58  static HRESULT GetDevice(IDXGISwapChain *const pSwapChain, ID3D11Device **ppDevice);
59  static HRESULT GetDeviceContext(IDXGISwapChain *const pSwapChain, ID3D11Device **ppDevice, ID3D11DeviceContext **ppDeviceContext);
60  static void OnResize(IDXGISwapChain *pSwapChain, UINT newWidth, UINT newHeight);
61  static LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
62 
63  template <typename Type>
64  inline void ReleaseCOM(Type *pInterface)
65  {
66  if (pInterface != nullptr)
67  {
68  // Call release member function of COM object:
69  pInterface->Release();
70  pInterface = nullptr;
71  }
72  }
73 
74  template <typename Type>
75  inline void ReleaseCOM(Type **ppInterface)
76  {
77  if (*ppInterface != nullptr)
78  {
79  (*ppInterface)->Release();
80  (*ppInterface) = nullptr;
81  }
82  }
83  // Variables
84  static ID3D11Device *s_pDevice = nullptr;
85  static ID3D11DeviceContext *s_pDeviceContext = nullptr;
86  static ID3D11RenderTargetView *s_pRenderTargetView = nullptr;
87  static constexpr LPCSTR kDXGI_MODULE_NAME = "dxgi.dll";
88  static std::FILE *s_pFileStdout = stdout;
89  static std::FILE *s_pFileStderr = stderr;
90  // Function pointer type
91  static IDXGISwapChainPresentPtr s_pSwapChainPresentTrampoline = nullptr;
92  static HWND s_hWnd = nullptr;
93  // WndProc used by application, in this case DCS: World
94  static WNDPROC s_pWndProcApp = nullptr;
95  static bool s_bInitialized = false, s_bWidgetsEnabled = false;
96  static RoutinePtr s_pWidgetRoutine = nullptr;
97  static InputRoutinePtr s_pInputRoutine = nullptr;
98  static ImGuiContext *s_pImGuiContext = nullptr;
99 #if defined FMGUI_ENABLE_IMPLOT
100  static ImPlotContext *s_pImPlotContext = nullptr;
101 #endif
102  static bool s_bImplWin32Initialized = false;
103  static bool s_bImplDX11Initialized = false;
104  static Config s_config;
105  static MessageCallback s_pMessageCallback = nullptr;
106  static std::stack<Message> s_msgStack;
107  static constexpr std::stack<Message>::size_type kMSG_STACK_MAX_SIZE = 24;
108 } // End namespace (FmGui)
109 
110 // Todo turn this logic into a inline static function.
111 #define PUSH_MSG(SEVERITY, CONTENT) ;
112  //\
113  //if (s_msgStack.size() > kMSG_STACK_MAX_SIZE) \
114  //{ \
115  // s_msgStack.pop(); \
116  //} \
117  //else \
118  //{ \
119  // if (s_msgStack.top().content != CONTENT) \
120  // { \
121  // s_msgStack.emplace(SEVERITY, CONTENT, __FILE__, __func__, __LINE__); \
122  // if (s_pMessageCallback != nullptr) \
123  // s_pMessageCallback(s_msgStack.top()); \
124  // } \
125  //} \
126 
128 {
129  s_pWidgetRoutine = pRoutine;
130 }
131 
133 {
134  s_pInputRoutine = pInputRoutine;
135 }
136 
138 {
139  s_pMessageCallback = pMessageCallback;
140 }
141 
142 std::string FmGui::AddressDump(void)
143 {
144  std::ostringstream oss;
145  oss.precision(2);
146  oss << std::hex
147  << "ID3D11Device Pointer Location: "
148  << reinterpret_cast<void *>(&s_pDevice) << '\n'
149  << "ID3D11DeviceContext Pointer Location: "
150  << reinterpret_cast<void *>(&s_pDeviceContext) << '\n'
151  << "ID3D11RenderTargetView Pointer Location: "
152  << reinterpret_cast<void *>(&s_pRenderTargetView) << '\n';
153  // << "IDXGISwapChain Pointer Location: "
154  // << reinterpret_cast<void *>(&pSwapChain) << '\n';
155  return oss.str();
156 }
157 
159 {
160  ID3D11InfoQueue *pInfoQueue = nullptr;
161  if (!s_pDevice || FAILED(s_pDevice->QueryInterface(__uuidof(ID3D11InfoQueue),
162  reinterpret_cast<void **>(&pInfoQueue))))
163  {
164  PUSH_MSG(MessageSeverity::kHIGH, "QueryInterface failed!");
165  return std::string();
166  }
167 
168  if (FAILED(pInfoQueue->PushEmptyStorageFilter()))
169  {
170  PUSH_MSG(MessageSeverity::kHIGH, "ID3D11InfoQueue::PushEmptyStorageFilter failed!");
171  return std::string();
172  }
173 
174  std::ostringstream oss;
175  const UINT64 kMsgCount = pInfoQueue->GetNumStoredMessages();
176 
177  for (UINT64 index = 0; index < kMsgCount; ++index)
178  {
179  SIZE_T msgSize = 0;
180  // Get the size of the message.
181  if (FAILED(pInfoQueue->GetMessage(index, nullptr, &msgSize)))
182  {
183  PUSH_MSG(MessageSeverity::kHIGH, "GetMessage failed!");
184  return std::string();
185  }
186  // Allocate memory.
187  D3D11_MESSAGE *pMessage = (D3D11_MESSAGE *)std::malloc(msgSize);
188  if (!pMessage)
189  {
190  PUSH_MSG(MessageSeverity::kHIGH, "std::malloc failed!");
191  return std::string();
192  }
193  // Get the message itself.
194  if (FAILED(pInfoQueue->GetMessage(index, pMessage, &msgSize)))
195  {
196  PUSH_MSG(MessageSeverity::kHIGH, "ID3D11InfoQueue::GetMessaged failed!");
197  std::free(pMessage);
198  return std::string();
199  }
200 
201  oss << "D3D11 MESSAGE|ID:" << static_cast<int>(pMessage->ID)
202  << "|CATEGORY:" << static_cast<int>(pMessage->Category)
203  << "|SEVERITY:" << static_cast<int>(pMessage->Severity)
204  << "|DESC_LEN:" << pMessage->DescriptionByteLength
205  << "|DESC:" << pMessage->pDescription;
206  std::free(pMessage);
207  }
208  pInfoQueue->ClearStoredMessages();
209  ReleaseCOM(pInfoQueue);
210  return std::string();
211 }
212 
213 LPVOID FmGui::LookupSwapChainVTable(void)
214 {
215 #if 0
216  /*
217  * The following code has been disabled for now.It may be further explored
218  * in the future.
219  */
220  WNDCLASSEXA wndClassEx;
221  ZeroMemory(&wndClassEx, sizeof(wndClassEx));
222  wndClassEx.cbSize = sizeof(WNDCLASSEXA);
223  wndClassEx.style = CS_HREDRAW | CS_VREDRAW;
224  wndClassEx.lpfnWndProc = nullptr;
225  wndClassEx.cbClsExtra = 0;
226  wndClassEx.cbWndExtra = 0;
227  wndClassEx.hInstance = GetModuleHandle(nullptr);
228  wndClassEx.hIcon = nullptr;
229  wndClassEx.hCursor = nullptr;
230  wndClassEx.hbrBackground = nullptr;
231  wndClassEx.lpszMenuName = nullptr;
232  wndClassEx.lpszClassName = "FmGuiWndClassName";
233  wndClassEx.hIconSm = nullptr;
234 
235  if (RegisterClassExA(&wndClassEx) == 0)
236  {
237  PUSH_MSG(MessageSeverity::kHIGH, "RegisterClassEx failed!");
238  return nullptr;
239  }
240 
241  constexpr int kFAKE_WND_WIDTH = 100, kFAKE_WND_HEIGHT = 100;
242  HWND hLocalWnd = CreateWindowExA(
243  NULL,
244  wndClassEx.lpszClassName,
245  "Fake Window",
246  WS_OVERLAPPEDWINDOW,
247  CW_USEDEFAULT, CW_USEDEFAULT,
248  kFAKE_WND_WIDTH, kFAKE_WND_HEIGHT,
249  nullptr,
250  nullptr,
251  wndClassEx.hInstance,
252  nullptr
253  );
254 
255  if (!hLocalWnd)
256  {
257  PUSH_MSG(MessageSeverity::kHIGH, "CreateWindowEx failed!");
258  UnregisterClassA(wndClassEx.lpszClassName, wndClassEx.hInstance);
259  return nullptr;
260  }
261 #endif
266  D3D_FEATURE_LEVEL featureLevel;
267  const D3D_FEATURE_LEVEL kFeatureLevels[] = {
268  D3D_FEATURE_LEVEL_10_1,
269  D3D_FEATURE_LEVEL_11_0
270  };
271  constexpr UINT kNUM_FEAT_LEVELS = std::size(kFeatureLevels);
272 
273  UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
274 #if defined _DEBUG
275  creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
276 #endif
277  ID3D11Device *pLocalDevice = nullptr;
278  ID3D11DeviceContext *pLocalDeviceContext = nullptr;
279  IDXGISwapChain *pLocalSwapChain = nullptr;
280 
281  DXGI_RATIONAL refreshRateRational;
282  ZeroMemory(&refreshRateRational, sizeof(refreshRateRational));
283  refreshRateRational.Numerator = 60;
284  refreshRateRational.Denominator = 1;
285 
286  DXGI_MODE_DESC bufferModeDesc;
287  ZeroMemory(&bufferModeDesc, sizeof(bufferModeDesc));
288  bufferModeDesc.Width = 100;
289  bufferModeDesc.Height = 100;
290  bufferModeDesc.RefreshRate = refreshRateRational;
291  bufferModeDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
292  bufferModeDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
293 
294  DXGI_SAMPLE_DESC sampleDesc;
295  ZeroMemory(&sampleDesc, sizeof(sampleDesc));
296  sampleDesc.Count = 1;
297  sampleDesc.Quality = 0;
298 
299  DXGI_SWAP_CHAIN_DESC swapChainDesc;
300  ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
301  swapChainDesc.BufferDesc = bufferModeDesc;
302  swapChainDesc.SampleDesc = sampleDesc;
303  swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
304  swapChainDesc.BufferCount = 1;
305 
306  HWND hWndFg = GetForegroundWindow(); // GetActiveWindow()
307  if (!hWndFg)
308  {
309  PUSH_MSG(MessageSeverity::kHIGH, "GetForegroundWindow failed!");
310  return nullptr;
311  }
312  else
313  {
314  constexpr std::size_t kTITLE_BUFF_SIZE = 256;
315  CHAR title[kTITLE_BUFF_SIZE];
316  GetWindowTextA(hWndFg, title, kTITLE_BUFF_SIZE);
317 
318  char buffer[kTITLE_BUFF_SIZE];
319  std::snprintf(buffer, kTITLE_BUFF_SIZE, "FmGui has window \"%s\".", title);
320  PUSH_MSG(MessageSeverity::kNOTIFICATION, std::string(buffer, kTITLE_BUFF_SIZE));
321  }
322 
323  swapChainDesc.OutputWindow = hWndFg;
324  swapChainDesc.Windowed = TRUE;
325  swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
326  swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
327  if (FAILED(D3D11CreateDeviceAndSwapChain(
328  nullptr,
329  D3D_DRIVER_TYPE_HARDWARE,
330  nullptr,
331  creationFlags,
332  kFeatureLevels,
333  kNUM_FEAT_LEVELS,
334  D3D11_SDK_VERSION,
335  &swapChainDesc,
336  &pLocalSwapChain,
337  &pLocalDevice,
338  &featureLevel,
339  &pLocalDeviceContext)))
340  {
341  PUSH_MSG(MessageSeverity::kHIGH, "D3D11CreateDeviceAndSwapChain failed!");
342  // DestroyWindow(hLocalWnd);
343  // UnregisterClassA(wndClassEx.lpszClassName, wndClassEx.hInstance);
344  return nullptr;
345  }
346 
347  DWORD_PTR *pLocalSwapChainVTable = nullptr;
348  pLocalSwapChainVTable = reinterpret_cast<DWORD_PTR *>(pLocalSwapChain);
349  pLocalSwapChainVTable = reinterpret_cast<DWORD_PTR *>(pLocalSwapChainVTable[0]);
350  LPVOID result = reinterpret_cast<LPVOID>(pLocalSwapChainVTable[8]);
351 
352  ReleaseCOM(pLocalDevice);
353  ReleaseCOM(pLocalDeviceContext);
354  ReleaseCOM(pLocalSwapChain);
355  // DestroyWindow(hLocalWnd);
356  // UnregisterClassA(wndClassEx.lpszClassName, wndClassEx.hInstance);
357  return result;
358 }
359 
360 bool FmGui::SetWidgetVisibility(bool bEnabled)
361 {
362  bool bPrevValue = s_bWidgetsEnabled;
363  s_bWidgetsEnabled = bEnabled;
364  return bPrevValue;
365 }
366 
367 std::string FmGui::MinHookStatusToStdString(MH_STATUS mhStatus)
368 {
369  constexpr std::size_t kBUFF_SIZE = 124;
370  char buffer[kBUFF_SIZE];
371 
372  const int kWritten = std::snprintf(buffer, kBUFF_SIZE, "%s", MH_StatusToString(mhStatus));
373  return (kWritten > 0) ? std::string(buffer, kWritten) : "";
374 }
375 
376 bool FmGui::StartupHook(const Config &config)
377 {
378  // Copy the given config:
379  s_config = config;
380 
381  PUSH_MSG(MessageSeverity::kNOTIFICATION, "Redirecting Direct3D routines...");
382  // HMODULE hDxgi = GetModuleHandleA(kDXGI_MODULE_NAME);
383  // DWORD_PTR dwpDxgi = reinterpret_cast<DWORD_PTR>(hDxgi);
384  LPVOID pSwapChainPresentOriginal = LookupSwapChainVTable();
385  if (!pSwapChainPresentOriginal)
386  {
387  PUSH_MSG(MessageSeverity::kHIGH, "FmGui::LookupSwapChainVTable!");
388  }
389  // DWORD_PTR hDxgi = (DWORD_PTR)GetModuleHandle(L"dxgi.dll");
390  //
391  // LPVOID pSwapChainPresentOriginal = reinterpret_cast<LPVOID>(
392  // (IDXGISwapChainPresentPtr)((DWORD_PTR)hDxgi + 0x5070));
393 
394  // Initialize MinHook:
395  MH_STATUS mhStatus = MH_Initialize();
396  if (mhStatus != MH_OK)
397  {
398  PUSH_MSG(MessageSeverity::kHIGH, "MH_Initialize failed: " + MinHookStatusToStdString(mhStatus) + '!');
399  return false;
400  }
401 
402  // Create swap chain present trampoline using MinHook:
403  mhStatus = MH_CreateHook(pSwapChainPresentOriginal, &SwapChainPresentImpl,
404  reinterpret_cast<LPVOID *>(&s_pSwapChainPresentTrampoline));
405  if (mhStatus != MH_OK)
406  {
407  PUSH_MSG(MessageSeverity::kHIGH, "MH_CreateHook failed: " + MinHookStatusToStdString(mhStatus) + '!');
408  return false;
409  }
410 
411  // Enable the created hook:
412  mhStatus = MH_EnableHook(pSwapChainPresentOriginal);
413  if (mhStatus != MH_OK)
414  {
415  PUSH_MSG(MessageSeverity::kHIGH, "MH_EnableHook failed: " + MinHookStatusToStdString(mhStatus) + '!');
416  return false;
417  }
418 
419  PUSH_MSG(MessageSeverity::kNOTIFICATION, "Direct3D Redirection complete.");
420  return true;
421 }
422 
423 HRESULT FMGUI_FASTCALL FmGui::SwapChainPresentImpl(IDXGISwapChain *pSwapChain, UINT syncInterval, UINT flags)
424 {
425  if (!s_bInitialized)
426  {
427  // Setup ImGui & ImPlot contexts:
428  bool bResult;
429  HRESULT hResult;
430 
431  PUSH_MSG(MessageSeverity::kNOTIFICATION, "Setting up present hook...");
432 
433  hResult = GetDevice(pSwapChain, &s_pDevice);
434  if (FAILED(hResult))
435  {
436  PUSH_MSG(MessageSeverity::kHIGH, "FmGui::GetDevice failed!");
437  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
438  }
439 
440  hResult = GetDeviceContext(pSwapChain, &s_pDevice, &s_pDeviceContext);
441  if (FAILED(hResult))
442  {
443  PUSH_MSG(MessageSeverity::kHIGH, "FmGui::GetDeviceContext failed!");
444  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
445  }
446 
447  s_pImGuiContext = ImGui::CreateContext();
448  if (!s_pImGuiContext)
449  {
450  PUSH_MSG(MessageSeverity::kHIGH, "ImGui::CreateContext failed!");
451  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
452  }
453 #ifdef FMGUI_ENABLE_IMPLOT
454  s_pImPlotContext = ImPlot::CreateContext();
455  if (!s_pImPlotContext)
456  {
457  PUSH_MSG(MessageSeverity::kHIGH, "ImPlot::CreateContext failed!");
458  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
459  }
460 #endif
461  ImGui::SetCurrentContext(s_pImGuiContext);
462 #ifdef FMGUI_ENABLE_IMPLOT
463  ImPlot::SetCurrentContext(s_pImPlotContext);
464 #endif
465  ImGuiIO &imGuiIO = ImGui::GetIO();
466  // Configure the current ImGui content:
467  imGuiIO.ConfigFlags |= s_config.configFlags;
468 #ifdef FMGUI_ENABLE_IMPLOT
476  imGuiIO.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
477 #endif
478  if (s_config.iniFileName.empty())
479  imGuiIO.IniFilename = nullptr;
480  else
481  imGuiIO.IniFilename = s_config.iniFileName.c_str();
482 
483  imGuiIO.IniSavingRate = s_config.iniSavingRate;
484  switch (s_config.style)
485  {
486  case Style::kCLASSIC:
487  ImGui::StyleColorsClassic();
488  break;
489  case Style::kDARK:
490  ImGui::StyleColorsDark();
491  break;
492  case Style::kLIGHT:
493  ImGui::StyleColorsLight();
494  break;
495  }
496 
497  // Get the IDXGISwapChain's description.
498  DXGI_SWAP_CHAIN_DESC swapChainDesc;
499  ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
500  if (FAILED(pSwapChain->GetDesc(&swapChainDesc)))
501  {
502  PUSH_MSG(MessageSeverity::kHIGH, "IDXGISwapChain::GetDesc failed!");
503  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
504  }
505  // Set global window handle to the OutputWindow of the IDXGISwapChain.
506  s_hWnd = swapChainDesc.OutputWindow;
507  s_pWndProcApp = reinterpret_cast<WNDPROC>(SetWindowLongPtr(s_hWnd, GWLP_WNDPROC,
508  reinterpret_cast<LONG_PTR>(WndProc)));
509  if (s_pWndProcApp == NULL)
510  {
511  PUSH_MSG(MessageSeverity::kHIGH, "SetWindowLongPtr failed!");
512  return S_FALSE;
513  }
514 
515  // ImGui Win32 and DX11 implementation initialization.
516  bResult = ImGui_ImplWin32_Init(s_hWnd);
517  s_bImplWin32Initialized = bResult;
518  if (!bResult)
519  {
520  PUSH_MSG(MessageSeverity::kHIGH, "ImGui_ImplWin32_Init failed!");
521  return S_FALSE;
522  }
523 
524  bResult = ImGui_ImplDX11_Init(s_pDevice, s_pDeviceContext);
525  s_bImplDX11Initialized = bResult;
526  if (!bResult)
527  {
528  PUSH_MSG(MessageSeverity::kHIGH, "ImGui_ImplDX11_Init failed!");
529  return S_FALSE;
530  }
531  imGuiIO.ImeWindowHandle = s_hWnd;
532  //imGuiIO.SetPlatformImeDataFn(ImGui::GetMainViewport(), );
533  //ImGui::GetMainViewport()->PlatformHandleRaw = s_hWnd;
534 
535  // Retrieve the back buffer from the IDXGISwapChain.
536  ID3D11Texture2D *pSwapChainBackBuffer = nullptr;
537  hResult = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<LPVOID *>(&pSwapChainBackBuffer));
538  if (FAILED(hResult))
539  {
540  PUSH_MSG(MessageSeverity::kHIGH, "IDXGISwapChain::GetBuffer failed!");
541  return S_FALSE;
542  }
543 
544  hResult = s_pDevice->CreateRenderTargetView(pSwapChainBackBuffer, nullptr, &s_pRenderTargetView);
545  if (FAILED(hResult))
546  {
547  PUSH_MSG(MessageSeverity::kHIGH, "ID3D11Device::CreateRenderTargetView failed!");
548  return S_FALSE;
549  }
550  ReleaseCOM(pSwapChainBackBuffer);
551 
552  s_bInitialized = true; // Set to initialized
553  }
554  else
555  {
556  // Prepare next frame for ImGui:
557  ImGui::SetCurrentContext(s_pImGuiContext);
558 #ifdef FMGUI_ENABLE_IMPLOT
559  ImPlot::SetCurrentContext(s_pImPlotContext);
560 #endif
561  // Check for NULL context.
562  if (!ImGui::GetCurrentContext())
563  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
564 
565  ImGui_ImplWin32_NewFrame();
566  ImGui_ImplDX11_NewFrame();
567 
568  ImGui::NewFrame();
569  if (s_bWidgetsEnabled)
570  {
571  if (s_pWidgetRoutine != nullptr)
572  s_pWidgetRoutine();
573  }
574  ImGui::EndFrame();
575  ImGui::Render();
576 
577  s_pDeviceContext->OMSetRenderTargets(1, &s_pRenderTargetView, nullptr);
578  ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
579  }
580  return s_pSwapChainPresentTrampoline(pSwapChain, syncInterval, flags);
581 }
582 
583 HRESULT FmGui::GetDevice(IDXGISwapChain *const pSwapChain, ID3D11Device **ppDevice)
584 {
585  HRESULT retVal = pSwapChain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<PVOID *>(ppDevice));
586  return retVal;
587 }
588 
589 HRESULT FmGui::GetDeviceContext(IDXGISwapChain *const pSwapChain, ID3D11Device **ppDevice, ID3D11DeviceContext **ppDeviceContext)
590 {
591  (*ppDevice)->GetImmediateContext(ppDeviceContext);
592  return (ppDeviceContext != nullptr) ? S_OK : E_FAIL;
593 }
594 
596 {
597  // Reverse order of initialization.
598  MH_STATUS mhStatus = MH_DisableHook(MH_ALL_HOOKS);
599  if (mhStatus != MH_OK)
600  {
601  PUSH_MSG(MessageSeverity::kHIGH, "MH_DisableHook failed: " + MinHookStatusToStdString(mhStatus) + '!');
602  return false;
603  }
604 
605  mhStatus = MH_Uninitialize();
606  if (mhStatus != MH_OK)
607  {
608  PUSH_MSG(MessageSeverity::kHIGH, "MH_Uninitialize failed: " + MinHookStatusToStdString(mhStatus) + '!');
609  return false;
610  }
611 
612  if (s_bImplDX11Initialized)
613  {
614  ImGui_ImplDX11_Shutdown();
615  s_bImplDX11Initialized = false;
616  }
617 
618  if (s_bImplWin32Initialized)
619  {
620  ImGui_ImplWin32_Shutdown();
621  s_bImplWin32Initialized = false;
622  }
623 #if defined FMGUI_ENABLE_IMPLOT
624  if (s_pImPlotContext != nullptr)
625  {
626  ImPlot::DestroyContext(s_pImPlotContext);
627  }
628 #endif
629 
630  if (s_pImGuiContext != nullptr)
631  {
632  ImGui::DestroyContext(s_pImGuiContext);
633  s_pImGuiContext = nullptr;
634  }
635 
636  ReleaseCOM(s_pDevice);
637  ReleaseCOM(s_pDeviceContext);
638  ReleaseCOM(s_pRenderTargetView);
639  if (s_hWnd)
640  {
641  // Set hWnd's WndProc back to it's original proc.
642  if (SetWindowLongPtr(s_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(s_pWndProcApp)) == 0)
643  {
644  PUSH_MSG(MessageSeverity::kHIGH, "SetWindowLongPtr failed!");
645  return false;
646  }
647  }
648  // Set the Present initialization check to false.
649  s_bInitialized = false;
650  return true;
651 }
652 
654 {
655  return s_msgStack.top();
656 }
657 
658 FmGui::MessageVector FmGui::GetEveryMessage(void)
659 {
660  MessageVector errorMsgs;
661  while (!s_msgStack.empty())
662  {
663  errorMsgs.push_back(s_msgStack.top());
664  s_msgStack.pop();
665  }
666  std::reverse(std::begin(errorMsgs), std::end(errorMsgs));
667  return errorMsgs;
668 }
669 
670 void FmGui::OnResize(IDXGISwapChain *pSwapChain, UINT newWidth, UINT newHeight)
671 {
682  ReleaseCOM(s_pRenderTargetView);
683 
684  DXGI_SWAP_CHAIN_DESC swapChainDesc;
685  ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
686  if (FAILED(pSwapChain->GetDesc(&swapChainDesc)))
687  {
688  PUSH_MSG(MessageSeverity::kHIGH, "IDXGISwapChain::GetDesc failed!");
689  return;
690  }
691 
692  if (FAILED(pSwapChain->ResizeBuffers(swapChainDesc.BufferCount,
693  newWidth, newHeight,
694  swapChainDesc.BufferDesc.Format, 0u)))
695  {
696  PUSH_MSG(MessageSeverity::kHIGH, "IDXGISwapChain::ResizeBuffers failed!");
697  return;
698  }
699 
700  ID3D11Texture2D *pSwapChainBackBuffer = nullptr;
701  if (FAILED(pSwapChain->GetBuffer(0u, __uuidof(ID3D11Texture2D),
702  reinterpret_cast<void **>(&pSwapChainBackBuffer))))
703  {
704  PUSH_MSG(MessageSeverity::kHIGH, "IDXGISwapChain::GetBuffer failed!");
705  ReleaseCOM(pSwapChainBackBuffer);
706  return;
707  }
708 
709  if (FAILED(s_pDevice->CreateRenderTargetView(pSwapChainBackBuffer, nullptr, &s_pRenderTargetView)))
710  {
711  PUSH_MSG(MessageSeverity::kHIGH, "ID3D11Device::CreateRenderTargetView failed!");
712  ReleaseCOM(pSwapChainBackBuffer);
713  return;
714  }
715  ReleaseCOM(pSwapChainBackBuffer);
716 
717  s_pDeviceContext->OMSetRenderTargets(1, &s_pRenderTargetView, nullptr);
718  CD3D11_VIEWPORT viewport(0.0f, 0.0f, static_cast<float>(newWidth), static_cast<float>(newHeight), 0.0f, 1.0f);
719  s_pDeviceContext->RSSetViewports(1, &viewport);
720 }
721 
722 LRESULT FmGui::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
723 {
724  // Check if there is a valid context:
725  if (ImGui::GetCurrentContext())
726  {
727  ImGuiIO &imGuiIO = ImGui::GetIO();
728  POINT cursorPos; GetCursorPos(&cursorPos);
729 
730  if (s_hWnd)
731  ScreenToClient(s_hWnd, &cursorPos);
732 
733  imGuiIO.MousePos.x = cursorPos.x;
734  imGuiIO.MousePos.y = cursorPos.y;
735  }
736  // Only handle if widgets are enabled:
737  if (s_bWidgetsEnabled)
738  {
739  // Check for a non-NULL context and handle ImGui events
740  if (ImGui::GetCurrentContext() && ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam))
741  {
742  return TRUE;
743  }
744  // Handle user's non-NULL input routine
745  if (s_pInputRoutine != nullptr)
746  s_pInputRoutine(uMsg, wParam, lParam);
747  }
748  // Other events:
749  switch (uMsg)
750  {
751  case WM_SIZE:
752  {
753  const UINT newWidth = static_cast<UINT>(LOWORD(lParam));
754  const UINT newHeight = static_cast<UINT>(HIWORD(lParam));
755  // TODO: Resize IDXGISwapChain
756  // OnResize(newWidth, newHeight);
757  break;
758  }
759  default:
760  break;
761  }
762  return CallWindowProc(s_pWndProcApp, hWnd, uMsg, wParam, lParam);
763 }
FmGui types & functions.
Definition: FmGui.cpp:53
void SetMessageCallback(MessageCallback pMessageCallback)
Definition: FmGui.cpp:137
bool StartupHook(const Config &config=Config())
Definition: FmGui.cpp:376
std::string AddressDump(void)
Definition: FmGui.cpp:142
std::vector< Message > GetEveryMessage(void)
Definition: FmGui.cpp:658
void SetRoutinePtr(RoutinePtr pRoutine)
Definition: FmGui.cpp:127
std::add_pointer< void(void)>::type RoutinePtr
Alias for FmGui routine function.
Definition: FmGui.hpp:125
void SetInputRoutinePtr(InputRoutinePtr pInputRoutine)
Definition: FmGui.cpp:132
std::add_pointer< void(UINT uMsg, WPARAM wParam, LPARAM lParam)>::type InputRoutinePtr
Alias for FmGui input routine function.
Definition: FmGui.hpp:127
std::add_pointer< void(const Message &message)>::type MessageCallback
Alias for message callback.
Definition: FmGui.hpp:123
bool SetWidgetVisibility(bool bEnabled)
Definition: FmGui.cpp:360
const Message & GetLastError(void)
Definition: FmGui.cpp:653
std::string DebugLayerMessageDump(void)
Definition: FmGui.cpp:158
bool ShutdownHook(void)
Definition: FmGui.cpp:595
FmGui configuration data.
Definition: FmGui.hpp:76
ImGuiConfigFlags configFlags
Definition: FmGui.hpp:88
Style style
Definition: FmGui.hpp:82
std::string iniFileName
Definition: FmGui.hpp:95
float iniSavingRate
Definition: FmGui.hpp:101
FmGui message data.
Definition: FmGui.hpp:113