1// Dear ImGui: standalone example application for DirectX 9
2
3// Learn about Dear ImGui:
4// - FAQ https://dearimgui.com/faq
5// - Getting Started https://dearimgui.com/getting-started
6// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
7// - Introduction, links and more at the top of imgui.cpp
8
9#include "imgui.h"
10#include "imgui_impl_dx9.h"
11#include "imgui_impl_win32.h"
12#include <d3d9.h>
13#include <tchar.h>
14
15// Data
16static LPDIRECT3D9 g_pD3D = nullptr;
17static LPDIRECT3DDEVICE9 g_pd3dDevice = nullptr;
18static bool g_DeviceLost = false;
19static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
20static D3DPRESENT_PARAMETERS g_d3dpp = {};
21
22// Forward declarations of helper functions
23bool CreateDeviceD3D(HWND hWnd);
24void CleanupDeviceD3D();
25void ResetDevice();
26LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
27
28// Main code
29int main(int, char**)
30{
31 // Make process DPI aware and obtain main monitor scale
32 ImGui_ImplWin32_EnableDpiAwareness();
33 float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
34
35 // Create application window
36 WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
37 ::RegisterClassExW(&wc);
38 HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX9 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);
39
40 // Initialize Direct3D
41 if (!CreateDeviceD3D(hwnd))
42 {
43 CleanupDeviceD3D();
44 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
45 return 1;
46 }
47
48 // Show the window
49 ::ShowWindow(hwnd, SW_SHOWDEFAULT);
50 ::UpdateWindow(hwnd);
51
52 // Setup Dear ImGui context
53 IMGUI_CHECKVERSION();
54 ImGui::CreateContext();
55 ImGuiIO& io = ImGui::GetIO(); (void)io;
56 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
57 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
58 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
59 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
60 //io.ConfigViewportsNoAutoMerge = true;
61 //io.ConfigViewportsNoTaskBarIcon = true;
62
63 // Setup Dear ImGui style
64 ImGui::StyleColorsDark();
65 //ImGui::StyleColorsLight();
66
67 // Setup scaling
68 ImGuiStyle& style = ImGui::GetStyle();
69 style.ScaleAllSizes(scale_factor: main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
70 style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose)
71 io.ConfigDpiScaleFonts = true; // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
72 io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.
73
74 // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
75 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
76 {
77 style.WindowRounding = 0.0f;
78 style.Colors[ImGuiCol_WindowBg].w = 1.0f;
79 }
80
81 // Setup Platform/Renderer backends
82 ImGui_ImplWin32_Init(hwnd);
83 ImGui_ImplDX9_Init(device: g_pd3dDevice);
84
85 // Load Fonts
86 // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
87 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
88 // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
89 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
90 // - Read 'docs/FONTS.md' for more instructions and details.
91 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
92 //style.FontSizeBase = 20.0f;
93 //io.Fonts->AddFontDefault();
94 //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
95 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
96 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
97 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
98 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
99 //IM_ASSERT(font != nullptr);
100
101 // Our state
102 bool show_demo_window = true;
103 bool show_another_window = false;
104 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
105
106 // Main loop
107 bool done = false;
108 while (!done)
109 {
110 // Poll and handle messages (inputs, window resize, etc.)
111 // See the WndProc() function below for our to dispatch events to the Win32 backend.
112 MSG msg;
113 while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
114 {
115 ::TranslateMessage(&msg);
116 ::DispatchMessage(&msg);
117 if (msg.message == WM_QUIT)
118 done = true;
119 }
120 if (done)
121 break;
122
123 // Handle lost D3D9 device
124 if (g_DeviceLost)
125 {
126 HRESULT hr = g_pd3dDevice->TestCooperativeLevel();
127 if (hr == D3DERR_DEVICELOST)
128 {
129 ::Sleep(10);
130 continue;
131 }
132 if (hr == D3DERR_DEVICENOTRESET)
133 ResetDevice();
134 g_DeviceLost = false;
135 }
136
137 // Handle window resize (we don't resize directly in the WM_SIZE handler)
138 if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
139 {
140 g_d3dpp.BackBufferWidth = g_ResizeWidth;
141 g_d3dpp.BackBufferHeight = g_ResizeHeight;
142 g_ResizeWidth = g_ResizeHeight = 0;
143 ResetDevice();
144 }
145
146 // Start the Dear ImGui frame
147 ImGui_ImplDX9_NewFrame();
148 ImGui_ImplWin32_NewFrame();
149 ImGui::NewFrame();
150
151 // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
152 if (show_demo_window)
153 ImGui::ShowDemoWindow(p_open: &show_demo_window);
154
155 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
156 {
157 static float f = 0.0f;
158 static int counter = 0;
159
160 ImGui::Begin(name: "Hello, world!"); // Create a window called "Hello, world!" and append into it.
161
162 ImGui::Text(fmt: "This is some useful text."); // Display some text (you can use a format strings too)
163 ImGui::Checkbox(label: "Demo Window", v: &show_demo_window); // Edit bools storing our window open/close state
164 ImGui::Checkbox(label: "Another Window", v: &show_another_window);
165
166 ImGui::SliderFloat(label: "float", v: &f, v_min: 0.0f, v_max: 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
167 ImGui::ColorEdit3(label: "clear color", col: (float*)&clear_color); // Edit 3 floats representing a color
168
169 if (ImGui::Button(label: "Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
170 counter++;
171 ImGui::SameLine();
172 ImGui::Text(fmt: "counter = %d", counter);
173
174 ImGui::Text(fmt: "Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
175 ImGui::End();
176 }
177
178 // 3. Show another simple window.
179 if (show_another_window)
180 {
181 ImGui::Begin(name: "Another Window", p_open: &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
182 ImGui::Text(fmt: "Hello from another window!");
183 if (ImGui::Button(label: "Close Me"))
184 show_another_window = false;
185 ImGui::End();
186 }
187
188 // Rendering
189 ImGui::EndFrame();
190 g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
191 g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
192 g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
193 D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*clear_color.w*255.0f), (int)(clear_color.y*clear_color.w*255.0f), (int)(clear_color.z*clear_color.w*255.0f), (int)(clear_color.w*255.0f));
194 g_pd3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0);
195 if (g_pd3dDevice->BeginScene() >= 0)
196 {
197 ImGui::Render();
198 ImGui_ImplDX9_RenderDrawData(draw_data: ImGui::GetDrawData());
199 g_pd3dDevice->EndScene();
200 }
201
202 // Update and Render additional Platform Windows
203 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
204 {
205 ImGui::UpdatePlatformWindows();
206 ImGui::RenderPlatformWindowsDefault();
207 }
208
209 HRESULT result = g_pd3dDevice->Present(nullptr, nullptr, nullptr, nullptr);
210 if (result == D3DERR_DEVICELOST)
211 g_DeviceLost = true;
212 }
213
214 // Cleanup
215 ImGui_ImplDX9_Shutdown();
216 ImGui_ImplWin32_Shutdown();
217 ImGui::DestroyContext();
218
219 CleanupDeviceD3D();
220 ::DestroyWindow(hwnd);
221 ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
222
223 return 0;
224}
225
226// Helper functions
227bool CreateDeviceD3D(HWND hWnd)
228{
229 if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == nullptr)
230 return false;
231
232 // Create the D3DDevice
233 ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
234 g_d3dpp.Windowed = TRUE;
235 g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
236 g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // Need to use an explicit format with alpha if needing per-pixel alpha composition.
237 g_d3dpp.EnableAutoDepthStencil = TRUE;
238 g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
239 g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // Present with vsync
240 //g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync, maximum unthrottled framerate
241 if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
242 return false;
243
244 return true;
245}
246
247void CleanupDeviceD3D()
248{
249 if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
250 if (g_pD3D) { g_pD3D->Release(); g_pD3D = nullptr; }
251}
252
253void ResetDevice()
254{
255 ImGui_ImplDX9_InvalidateDeviceObjects();
256 HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp);
257 if (hr == D3DERR_INVALIDCALL)
258 IM_ASSERT(0);
259 ImGui_ImplDX9_CreateDeviceObjects();
260}
261
262// Forward declare message handler from imgui_impl_win32.cpp
263extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
264
265// Win32 message handler
266// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
267// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
268// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
269// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
270LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
271{
272 if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
273 return true;
274
275 switch (msg)
276 {
277 case WM_SIZE:
278 if (wParam == SIZE_MINIMIZED)
279 return 0;
280 g_ResizeWidth = (UINT)LOWORD(lParam); // Queue resize
281 g_ResizeHeight = (UINT)HIWORD(lParam);
282 return 0;
283 case WM_SYSCOMMAND:
284 if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
285 return 0;
286 break;
287 case WM_DESTROY:
288 ::PostQuitMessage(0);
289 return 0;
290 }
291 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
292}
293

source code of imgui/examples/example_win32_directx9/main.cpp