1// dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications)
2// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
3
4// Implemented features:
5// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
6// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
7// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5]
8// [X] Platform: Gamepad support.
9// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
10// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
11
12// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
13// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
14// Learn about Dear ImGui:
15// - FAQ https://dearimgui.com/faq
16// - Getting Started https://dearimgui.com/getting-started
17// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
18// - Introduction, links and more at the top of imgui.cpp
19
20// Configuration flags to add in your imconfig file:
21//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant.
22
23// CHANGELOG
24// (minor and older changes stripped away, please see git history for details)
25// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
26// 2025-06-02: [Docking] WM_DPICHANGED also apply io.ConfigDpiScaleViewports for main viewport instead of letting it be done by application code.
27// 2025-04-30: Inputs: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) would fail to claim it again the next subsequent click. (#8594)
28// 2025-03-26: [Docking] Viewports: fixed an issue when closing a window from the OS close button (with io.ConfigViewportsNoDecoration = false) while user code was discarding the 'bool* p_open = false' output from Begin(). Because we allowed the Win32 window to close early, Windows destroyed it and our imgui window became not visible even though user code was still submitting it.
29// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468)
30// 2025-02-21: [Docking] WM_SETTINGCHANGE's SPI_SETWORKAREA message also triggers a refresh of monitor list. (#8415)
31// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
32// 2024-11-21: [Docking] Fixed a crash when multiple processes are running with multi-viewports, caused by misusage of GetProp(). (#8162, #8069)
33// 2024-10-28: [Docking] Rely on property stored inside HWND to retrieve context/viewport, should facilitate attempt to use this for parallel contexts. (#8069)
34// 2024-09-16: [Docking] Inputs: fixed an issue where a viewport destroyed while clicking would hog mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971)
35// 2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768)
36// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
37// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL).
38// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window.
39// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218)
40// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702)
41// 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162)
42// 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
43// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
44// 2022-09-28: Inputs: Convert WM_CHAR values with MultiByteToWideChar() when window class was registered as MBCS (not Unicode).
45// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
46// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
47// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
48// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
49// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates.
50// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
51// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
52// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
53// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness.
54// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages.
55// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus.
56// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events).
57// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
58// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1).
59// 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS).
60// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi).
61// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1.
62// 2021-01-25: Inputs: Dynamically loading XInput DLL.
63// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
64// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
65// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
66// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
67// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
68// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
69// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
70// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
71// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
72// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
73// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
74// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
75// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
76// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
77// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
78// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
79// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
80// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
81// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
82// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
83// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
84// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
85// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
86// 2016-11-12: Inputs: Only call Win32 ::SetCursor(nullptr) when io.MouseDrawCursor is set.
87
88#include "imgui.h"
89#ifndef IMGUI_DISABLE
90#include "imgui_impl_win32.h"
91#ifndef WIN32_LEAN_AND_MEAN
92#define WIN32_LEAN_AND_MEAN
93#endif
94#include <windows.h>
95#include <windowsx.h> // GET_X_LPARAM(), GET_Y_LPARAM()
96#include <tchar.h>
97#include <dwmapi.h>
98
99// Using XInput for gamepad (will load DLL dynamically)
100#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
101#include <xinput.h>
102typedef DWORD(WINAPI* PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
103typedef DWORD(WINAPI* PFN_XInputGetState)(DWORD, XINPUT_STATE*);
104#endif
105
106// Clang/GCC warnings with -Weverything
107#if defined(__clang__)
108#pragma clang diagnostic push
109#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
110#endif
111#if defined(__GNUC__)
112#pragma GCC diagnostic push
113#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
114#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
115#endif
116
117// Forward Declarations
118static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc);
119static void ImGui_ImplWin32_ShutdownMultiViewportSupport();
120static void ImGui_ImplWin32_UpdateMonitors();
121
122struct ImGui_ImplWin32_Data
123{
124 HWND hWnd;
125 HWND MouseHwnd;
126 int MouseTrackedArea; // 0: not tracked, 1: client area, 2: non-client area
127 int MouseButtonsDown;
128 INT64 Time;
129 INT64 TicksPerSecond;
130 ImGuiMouseCursor LastMouseCursor;
131 UINT32 KeyboardCodePage;
132 bool WantUpdateMonitors;
133
134#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
135 bool HasGamepad;
136 bool WantUpdateHasGamepad;
137 HMODULE XInputDLL;
138 PFN_XInputGetCapabilities XInputGetCapabilities;
139 PFN_XInputGetState XInputGetState;
140#endif
141
142 ImGui_ImplWin32_Data() { memset(s: (void*)this, c: 0, n: sizeof(*this)); }
143};
144
145// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
146// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
147// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
148// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
149static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData()
150{
151 return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
152}
153static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData(ImGuiIO& io)
154{
155 return (ImGui_ImplWin32_Data*)io.BackendPlatformUserData;
156}
157
158// Functions
159static void ImGui_ImplWin32_UpdateKeyboardCodePage(ImGuiIO& io)
160{
161 // Retrieve keyboard code page, required for handling of non-Unicode Windows.
162 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
163 HKL keyboard_layout = ::GetKeyboardLayout(0);
164 LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT);
165 if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0)
166 bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails.
167}
168
169static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc)
170{
171 ImGuiIO& io = ImGui::GetIO();
172 IMGUI_CHECKVERSION();
173 IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
174
175 INT64 perf_frequency, perf_counter;
176 if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency))
177 return false;
178 if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter))
179 return false;
180
181 // Setup backend capabilities flags
182 ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)();
183 io.BackendPlatformUserData = (void*)bd;
184 io.BackendPlatformName = "imgui_impl_win32";
185 io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
186 io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
187 io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
188 io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional)
189
190 bd->hWnd = (HWND)hwnd;
191 bd->TicksPerSecond = perf_frequency;
192 bd->Time = perf_counter;
193 bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
194 ImGui_ImplWin32_UpdateKeyboardCodePage(io);
195
196 // Update monitor a first time during init
197 ImGui_ImplWin32_UpdateMonitors();
198
199 // Our mouse update function expect PlatformHandle to be filled for the main viewport
200 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
201 main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd;
202
203 // Be aware that GetPropA()/SetPropA() may be accessed from other processes.
204 // So as we store a pointer in IMGUI_CONTEXT we need to make sure we only call GetPropA() on windows owned by our process.
205 ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
206 ImGui_ImplWin32_InitMultiViewportSupport(platform_has_own_dc);
207
208 // Dynamically load XInput library
209#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
210 bd->WantUpdateHasGamepad = true;
211 const char* xinput_dll_names[] =
212 {
213 "xinput1_4.dll", // Windows 8+
214 "xinput1_3.dll", // DirectX SDK
215 "xinput9_1_0.dll", // Windows Vista, Windows 7
216 "xinput1_2.dll", // DirectX SDK
217 "xinput1_1.dll" // DirectX SDK
218 };
219 for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
220 if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
221 {
222 bd->XInputDLL = dll;
223 bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
224 bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
225 break;
226 }
227#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
228
229 return true;
230}
231
232IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd)
233{
234 return ImGui_ImplWin32_InitEx(hwnd, platform_has_own_dc: false);
235}
236
237IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd)
238{
239 // OpenGL needs CS_OWNDC
240 return ImGui_ImplWin32_InitEx(hwnd, platform_has_own_dc: true);
241}
242
243void ImGui_ImplWin32_Shutdown()
244{
245 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
246 IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
247 ImGuiIO& io = ImGui::GetIO();
248
249 ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", nullptr);
250 ImGui_ImplWin32_ShutdownMultiViewportSupport();
251
252 // Unload XInput library
253#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
254 if (bd->XInputDLL)
255 ::FreeLibrary(bd->XInputDLL);
256#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
257
258 io.BackendPlatformName = nullptr;
259 io.BackendPlatformUserData = nullptr;
260 io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
261 IM_DELETE(p: bd);
262}
263
264static bool ImGui_ImplWin32_UpdateMouseCursor(ImGuiIO& io, ImGuiMouseCursor imgui_cursor)
265{
266 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
267 return false;
268
269 if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
270 {
271 // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
272 ::SetCursor(nullptr);
273 }
274 else
275 {
276 // Show OS mouse cursor
277 LPTSTR win32_cursor = IDC_ARROW;
278 switch (imgui_cursor)
279 {
280 case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
281 case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
282 case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
283 case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
284 case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
285 case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
286 case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
287 case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
288 case ImGuiMouseCursor_Wait: win32_cursor = IDC_WAIT; break;
289 case ImGuiMouseCursor_Progress: win32_cursor = IDC_APPSTARTING; break;
290 case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
291 }
292 ::SetCursor(::LoadCursor(nullptr, win32_cursor));
293 }
294 return true;
295}
296
297static bool IsVkDown(int vk)
298{
299 return (::GetKeyState(vk) & 0x8000) != 0;
300}
301
302static void ImGui_ImplWin32_AddKeyEvent(ImGuiIO& io, ImGuiKey key, bool down, int native_keycode, int native_scancode = -1)
303{
304 io.AddKeyEvent(key, down);
305 io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code)
306 IM_UNUSED(native_scancode);
307}
308
309static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds(ImGuiIO& io)
310{
311 // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
312 if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT))
313 ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftShift, false, VK_LSHIFT);
314 if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT))
315 ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightShift, false, VK_RSHIFT);
316
317 // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW).
318 if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN))
319 ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftSuper, false, VK_LWIN);
320 if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN))
321 ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightSuper, false, VK_RWIN);
322}
323
324static void ImGui_ImplWin32_UpdateKeyModifiers(ImGuiIO& io)
325{
326 io.AddKeyEvent(ImGuiMod_Ctrl, IsVkDown(VK_CONTROL));
327 io.AddKeyEvent(ImGuiMod_Shift, IsVkDown(VK_SHIFT));
328 io.AddKeyEvent(ImGuiMod_Alt, IsVkDown(VK_MENU));
329 io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
330}
331
332static ImGuiViewport* ImGui_ImplWin32_FindViewportByPlatformHandle(ImGuiPlatformIO& platform_io, HWND hwnd)
333{
334 // We cannot use ImGui::FindViewportByPlatformHandle() because it doesn't take a context.
335 // When called from ImGui_ImplWin32_WndProcHandler_PlatformWindow() we don't assume that context is bound.
336 //return ImGui::FindViewportByPlatformHandle((void*)hwnd);
337 for (ImGuiViewport* viewport : platform_io.Viewports)
338 if (viewport->PlatformHandle == hwnd)
339 return viewport;
340 return nullptr;
341}
342
343// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
344// Because of that, it is a little more complicated than your typical single-viewport binding code!
345static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io, ImGuiPlatformIO& platform_io)
346{
347 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
348 IM_ASSERT(bd->hWnd != 0);
349
350 POINT mouse_screen_pos;
351 bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;
352
353 HWND focused_window = ::GetForegroundWindow();
354 const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, focused_window)));
355 if (is_app_focused)
356 {
357 // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
358 // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.
359 if (io.WantSetMousePos)
360 {
361 POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
362 if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
363 ::ClientToScreen(focused_window, &pos);
364 ::SetCursorPos(pos.x, pos.y);
365 }
366
367 // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
368 // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE
369 if (!io.WantSetMousePos && bd->MouseTrackedArea == 0 && has_mouse_screen_pos)
370 {
371 // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
372 // (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
373 // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
374 // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
375 POINT mouse_pos = mouse_screen_pos;
376 if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
377 ::ScreenToClient(bd->hWnd, &mouse_pos);
378 io.AddMousePosEvent(x: (float)mouse_pos.x, y: (float)mouse_pos.y);
379 }
380 }
381
382 // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
383 // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
384 // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that)
385 // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
386 // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
387 // by the backend, and use its flawed heuristic to guess the viewport behind.
388 // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
389 ImGuiID mouse_viewport_id = 0;
390 if (has_mouse_screen_pos)
391 if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
392 if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hovered_hwnd))
393 mouse_viewport_id = viewport->ID;
394 io.AddMouseViewportEvent(id: mouse_viewport_id);
395}
396
397// Gamepad navigation mapping
398static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io)
399{
400#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
401 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
402
403 // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
404 // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
405 if (bd->WantUpdateHasGamepad)
406 {
407 XINPUT_CAPABILITIES caps = {};
408 bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
409 bd->WantUpdateHasGamepad = false;
410 }
411
412 io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
413 XINPUT_STATE xinput_state;
414 XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
415 if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS)
416 return;
417 io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
418
419 #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
420 #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); }
421 #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); }
422 MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START);
423 MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK);
424 MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X);
425 MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B);
426 MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y);
427 MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A);
428 MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT);
429 MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT);
430 MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP);
431 MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN);
432 MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER);
433 MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER);
434 MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
435 MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255);
436 MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB);
437 MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB);
438 MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
439 MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
440 MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
441 MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
442 MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
443 MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
444 MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
445 MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
446 #undef MAP_BUTTON
447 #undef MAP_ANALOG
448#else // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
449 IM_UNUSED(io);
450#endif
451}
452
453static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM)
454{
455 MONITORINFO info = {};
456 info.cbSize = sizeof(MONITORINFO);
457 if (!::GetMonitorInfo(monitor, &info))
458 return TRUE;
459 ImGuiPlatformMonitor imgui_monitor;
460 imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top);
461 imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top));
462 imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
463 imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top));
464 imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
465 imgui_monitor.PlatformHandle = (void*)monitor;
466 if (imgui_monitor.DpiScale <= 0.0f)
467 return TRUE; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902.
468 ImGuiPlatformIO& io = ImGui::GetPlatformIO();
469 if (info.dwFlags & MONITORINFOF_PRIMARY)
470 io.Monitors.push_front(imgui_monitor);
471 else
472 io.Monitors.push_back(imgui_monitor);
473 return TRUE;
474}
475
476static void ImGui_ImplWin32_UpdateMonitors()
477{
478 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
479 ImGui::GetPlatformIO().Monitors.resize(new_size: 0);
480 ::EnumDisplayMonitors(nullptr, nullptr, ImGui_ImplWin32_UpdateMonitors_EnumFunc, 0);
481 bd->WantUpdateMonitors = false;
482}
483
484void ImGui_ImplWin32_NewFrame()
485{
486 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
487 IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplWin32_Init()?");
488 ImGuiIO& io = ImGui::GetIO();
489 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
490
491 // Setup display size (every frame to accommodate for window resizing)
492 RECT rect = { 0, 0, 0, 0 };
493 ::GetClientRect(bd->hWnd, &rect);
494 io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
495 if (bd->WantUpdateMonitors)
496 ImGui_ImplWin32_UpdateMonitors();
497
498 // Setup time step
499 INT64 current_time = 0;
500 ::QueryPerformanceCounter((LARGE_INTEGER*)&current_time);
501 io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond;
502 bd->Time = current_time;
503
504 // Update OS mouse position
505 ImGui_ImplWin32_UpdateMouseData(io, platform_io);
506
507 // Process workarounds for known Windows key handling issues
508 ImGui_ImplWin32_ProcessKeyEventsWorkarounds(io);
509
510 // Update OS mouse cursor with the cursor requested by imgui
511 ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
512 if (bd->LastMouseCursor != mouse_cursor)
513 {
514 bd->LastMouseCursor = mouse_cursor;
515 ImGui_ImplWin32_UpdateMouseCursor(io, imgui_cursor: mouse_cursor);
516 }
517
518 // Update game controllers (if enabled and available)
519 ImGui_ImplWin32_UpdateGamepads(io);
520}
521
522// Map VK_xxx to ImGuiKey_xxx.
523// Not static to allow third-party code to use that if they want to (but undocumented)
524ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam);
525ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
526{
527 // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED.
528 if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
529 return ImGuiKey_KeypadEnter;
530
531 const int scancode = (int)LOBYTE(HIWORD(lParam));
532 //IMGUI_DEBUG_LOG("scancode %3d, keycode = 0x%02X\n", scancode, wParam);
533 switch (wParam)
534 {
535 case VK_TAB: return ImGuiKey_Tab;
536 case VK_LEFT: return ImGuiKey_LeftArrow;
537 case VK_RIGHT: return ImGuiKey_RightArrow;
538 case VK_UP: return ImGuiKey_UpArrow;
539 case VK_DOWN: return ImGuiKey_DownArrow;
540 case VK_PRIOR: return ImGuiKey_PageUp;
541 case VK_NEXT: return ImGuiKey_PageDown;
542 case VK_HOME: return ImGuiKey_Home;
543 case VK_END: return ImGuiKey_End;
544 case VK_INSERT: return ImGuiKey_Insert;
545 case VK_DELETE: return ImGuiKey_Delete;
546 case VK_BACK: return ImGuiKey_Backspace;
547 case VK_SPACE: return ImGuiKey_Space;
548 case VK_RETURN: return ImGuiKey_Enter;
549 case VK_ESCAPE: return ImGuiKey_Escape;
550 //case VK_OEM_7: return ImGuiKey_Apostrophe;
551 case VK_OEM_COMMA: return ImGuiKey_Comma;
552 //case VK_OEM_MINUS: return ImGuiKey_Minus;
553 case VK_OEM_PERIOD: return ImGuiKey_Period;
554 //case VK_OEM_2: return ImGuiKey_Slash;
555 //case VK_OEM_1: return ImGuiKey_Semicolon;
556 //case VK_OEM_PLUS: return ImGuiKey_Equal;
557 //case VK_OEM_4: return ImGuiKey_LeftBracket;
558 //case VK_OEM_5: return ImGuiKey_Backslash;
559 //case VK_OEM_6: return ImGuiKey_RightBracket;
560 //case VK_OEM_3: return ImGuiKey_GraveAccent;
561 case VK_CAPITAL: return ImGuiKey_CapsLock;
562 case VK_SCROLL: return ImGuiKey_ScrollLock;
563 case VK_NUMLOCK: return ImGuiKey_NumLock;
564 case VK_SNAPSHOT: return ImGuiKey_PrintScreen;
565 case VK_PAUSE: return ImGuiKey_Pause;
566 case VK_NUMPAD0: return ImGuiKey_Keypad0;
567 case VK_NUMPAD1: return ImGuiKey_Keypad1;
568 case VK_NUMPAD2: return ImGuiKey_Keypad2;
569 case VK_NUMPAD3: return ImGuiKey_Keypad3;
570 case VK_NUMPAD4: return ImGuiKey_Keypad4;
571 case VK_NUMPAD5: return ImGuiKey_Keypad5;
572 case VK_NUMPAD6: return ImGuiKey_Keypad6;
573 case VK_NUMPAD7: return ImGuiKey_Keypad7;
574 case VK_NUMPAD8: return ImGuiKey_Keypad8;
575 case VK_NUMPAD9: return ImGuiKey_Keypad9;
576 case VK_DECIMAL: return ImGuiKey_KeypadDecimal;
577 case VK_DIVIDE: return ImGuiKey_KeypadDivide;
578 case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
579 case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
580 case VK_ADD: return ImGuiKey_KeypadAdd;
581 case VK_LSHIFT: return ImGuiKey_LeftShift;
582 case VK_LCONTROL: return ImGuiKey_LeftCtrl;
583 case VK_LMENU: return ImGuiKey_LeftAlt;
584 case VK_LWIN: return ImGuiKey_LeftSuper;
585 case VK_RSHIFT: return ImGuiKey_RightShift;
586 case VK_RCONTROL: return ImGuiKey_RightCtrl;
587 case VK_RMENU: return ImGuiKey_RightAlt;
588 case VK_RWIN: return ImGuiKey_RightSuper;
589 case VK_APPS: return ImGuiKey_Menu;
590 case '0': return ImGuiKey_0;
591 case '1': return ImGuiKey_1;
592 case '2': return ImGuiKey_2;
593 case '3': return ImGuiKey_3;
594 case '4': return ImGuiKey_4;
595 case '5': return ImGuiKey_5;
596 case '6': return ImGuiKey_6;
597 case '7': return ImGuiKey_7;
598 case '8': return ImGuiKey_8;
599 case '9': return ImGuiKey_9;
600 case 'A': return ImGuiKey_A;
601 case 'B': return ImGuiKey_B;
602 case 'C': return ImGuiKey_C;
603 case 'D': return ImGuiKey_D;
604 case 'E': return ImGuiKey_E;
605 case 'F': return ImGuiKey_F;
606 case 'G': return ImGuiKey_G;
607 case 'H': return ImGuiKey_H;
608 case 'I': return ImGuiKey_I;
609 case 'J': return ImGuiKey_J;
610 case 'K': return ImGuiKey_K;
611 case 'L': return ImGuiKey_L;
612 case 'M': return ImGuiKey_M;
613 case 'N': return ImGuiKey_N;
614 case 'O': return ImGuiKey_O;
615 case 'P': return ImGuiKey_P;
616 case 'Q': return ImGuiKey_Q;
617 case 'R': return ImGuiKey_R;
618 case 'S': return ImGuiKey_S;
619 case 'T': return ImGuiKey_T;
620 case 'U': return ImGuiKey_U;
621 case 'V': return ImGuiKey_V;
622 case 'W': return ImGuiKey_W;
623 case 'X': return ImGuiKey_X;
624 case 'Y': return ImGuiKey_Y;
625 case 'Z': return ImGuiKey_Z;
626 case VK_F1: return ImGuiKey_F1;
627 case VK_F2: return ImGuiKey_F2;
628 case VK_F3: return ImGuiKey_F3;
629 case VK_F4: return ImGuiKey_F4;
630 case VK_F5: return ImGuiKey_F5;
631 case VK_F6: return ImGuiKey_F6;
632 case VK_F7: return ImGuiKey_F7;
633 case VK_F8: return ImGuiKey_F8;
634 case VK_F9: return ImGuiKey_F9;
635 case VK_F10: return ImGuiKey_F10;
636 case VK_F11: return ImGuiKey_F11;
637 case VK_F12: return ImGuiKey_F12;
638 case VK_F13: return ImGuiKey_F13;
639 case VK_F14: return ImGuiKey_F14;
640 case VK_F15: return ImGuiKey_F15;
641 case VK_F16: return ImGuiKey_F16;
642 case VK_F17: return ImGuiKey_F17;
643 case VK_F18: return ImGuiKey_F18;
644 case VK_F19: return ImGuiKey_F19;
645 case VK_F20: return ImGuiKey_F20;
646 case VK_F21: return ImGuiKey_F21;
647 case VK_F22: return ImGuiKey_F22;
648 case VK_F23: return ImGuiKey_F23;
649 case VK_F24: return ImGuiKey_F24;
650 case VK_BROWSER_BACK: return ImGuiKey_AppBack;
651 case VK_BROWSER_FORWARD: return ImGuiKey_AppForward;
652 default: break;
653 }
654
655 // Fallback to scancode
656 // https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names
657 switch (scancode)
658 {
659 case 41: return ImGuiKey_GraveAccent; // VK_OEM_8 in EN-UK, VK_OEM_3 in EN-US, VK_OEM_7 in FR, VK_OEM_5 in DE, etc.
660 case 12: return ImGuiKey_Minus;
661 case 13: return ImGuiKey_Equal;
662 case 26: return ImGuiKey_LeftBracket;
663 case 27: return ImGuiKey_RightBracket;
664 case 86: return ImGuiKey_Oem102;
665 case 43: return ImGuiKey_Backslash;
666 case 39: return ImGuiKey_Semicolon;
667 case 40: return ImGuiKey_Apostrophe;
668 case 51: return ImGuiKey_Comma;
669 case 52: return ImGuiKey_Period;
670 case 53: return ImGuiKey_Slash;
671 default: break;
672 }
673
674 return ImGuiKey_None;
675}
676
677// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
678#ifndef WM_MOUSEHWHEEL
679#define WM_MOUSEHWHEEL 0x020E
680#endif
681#ifndef DBT_DEVNODES_CHANGED
682#define DBT_DEVNODES_CHANGED 0x0007
683#endif
684
685// Helper to obtain the source of mouse messages.
686// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages
687// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this.
688static ImGuiMouseSource ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo()
689{
690 LPARAM extra_info = ::GetMessageExtraInfo();
691 if ((extra_info & 0xFFFFFF80) == 0xFF515700)
692 return ImGuiMouseSource_Pen;
693 if ((extra_info & 0xFFFFFF80) == 0xFF515780)
694 return ImGuiMouseSource_TouchScreen;
695 return ImGuiMouseSource_Mouse;
696}
697
698// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
699// Call from your application's message handler. Keep calling your message handler unless this function returns TRUE.
700// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
701// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
702// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
703// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
704// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
705
706// Copy either line into your .cpp file to forward declare the function:
707extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Use ImGui::GetCurrentContext()
708extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io); // Doesn't use ImGui::GetCurrentContext()
709
710#ifndef WM_DPICHANGED
711#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers
712#endif
713
714IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
715{
716 // Most backends don't have silent checks like this one, but we need it because WndProc are called early in CreateWindow().
717 // We silently allow both context or just only backend data to be nullptr.
718 if (ImGui::GetCurrentContext() == nullptr)
719 return 0;
720 return ImGui_ImplWin32_WndProcHandlerEx(hwnd, msg, wParam, lParam, ImGui::GetIO());
721}
722
723// This version is in theory thread-safe in the sense that no path should access ImGui::GetCurrentContext().
724IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, ImGuiIO& io)
725{
726 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io);
727 if (bd == nullptr)
728 return 0;
729 switch (msg)
730 {
731 case WM_MOUSEMOVE:
732 case WM_NCMOUSEMOVE:
733 {
734 // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
735 ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
736 const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
737 bd->MouseHwnd = hwnd;
738 if (bd->MouseTrackedArea != area)
739 {
740 TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
741 TRACKMOUSEEVENT tme_track = { sizeof(tme_track), (DWORD)((area == 2) ? (TME_LEAVE | TME_NONCLIENT) : TME_LEAVE), hwnd, 0 };
742 if (bd->MouseTrackedArea != 0)
743 ::TrackMouseEvent(&tme_cancel);
744 ::TrackMouseEvent(&tme_track);
745 bd->MouseTrackedArea = area;
746 }
747 POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) };
748 bool want_absolute_pos = (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0;
749 if (msg == WM_MOUSEMOVE && want_absolute_pos) // WM_MOUSEMOVE are client-relative coordinates.
750 ::ClientToScreen(hwnd, &mouse_pos);
751 if (msg == WM_NCMOUSEMOVE && !want_absolute_pos) // WM_NCMOUSEMOVE are absolute coordinates.
752 ::ScreenToClient(hwnd, &mouse_pos);
753 io.AddMouseSourceEvent(source: mouse_source);
754 io.AddMousePosEvent(x: (float)mouse_pos.x, y: (float)mouse_pos.y);
755 return 0;
756 }
757 case WM_MOUSELEAVE:
758 case WM_NCMOUSELEAVE:
759 {
760 const int area = (msg == WM_MOUSELEAVE) ? 1 : 2;
761 if (bd->MouseTrackedArea == area)
762 {
763 if (bd->MouseHwnd == hwnd)
764 bd->MouseHwnd = nullptr;
765 bd->MouseTrackedArea = 0;
766 io.AddMousePosEvent(x: -FLT_MAX, y: -FLT_MAX);
767 }
768 return 0;
769 }
770 case WM_DESTROY:
771 if (bd->MouseHwnd == hwnd && bd->MouseTrackedArea != 0)
772 {
773 TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
774 ::TrackMouseEvent(&tme_cancel);
775 bd->MouseHwnd = nullptr;
776 bd->MouseTrackedArea = 0;
777 io.AddMousePosEvent(x: -FLT_MAX, y: -FLT_MAX);
778 }
779 return 0;
780 case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
781 case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
782 case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
783 case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
784 {
785 ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
786 int button = 0;
787 if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
788 if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
789 if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
790 if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
791 HWND hwnd_with_capture = ::GetCapture();
792 if (bd->MouseButtonsDown != 0 && hwnd_with_capture != hwnd) // Did we externally lost capture?
793 bd->MouseButtonsDown = 0;
794 if (bd->MouseButtonsDown == 0 && hwnd_with_capture == nullptr)
795 ::SetCapture(hwnd); // Allow us to read mouse coordinates when dragging mouse outside of our window bounds.
796 bd->MouseButtonsDown |= 1 << button;
797 io.AddMouseSourceEvent(source: mouse_source);
798 io.AddMouseButtonEvent(button, down: true);
799 return 0;
800 }
801 case WM_LBUTTONUP:
802 case WM_RBUTTONUP:
803 case WM_MBUTTONUP:
804 case WM_XBUTTONUP:
805 {
806 ImGuiMouseSource mouse_source = ImGui_ImplWin32_GetMouseSourceFromMessageExtraInfo();
807 int button = 0;
808 if (msg == WM_LBUTTONUP) { button = 0; }
809 if (msg == WM_RBUTTONUP) { button = 1; }
810 if (msg == WM_MBUTTONUP) { button = 2; }
811 if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
812 bd->MouseButtonsDown &= ~(1 << button);
813 if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd)
814 ::ReleaseCapture();
815 io.AddMouseSourceEvent(source: mouse_source);
816 io.AddMouseButtonEvent(button, down: false);
817 return 0;
818 }
819 case WM_MOUSEWHEEL:
820 io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
821 return 0;
822 case WM_MOUSEHWHEEL:
823 io.AddMouseWheelEvent(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f);
824 return 0;
825 case WM_KEYDOWN:
826 case WM_KEYUP:
827 case WM_SYSKEYDOWN:
828 case WM_SYSKEYUP:
829 {
830 const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
831 if (wParam < 256)
832 {
833 // Submit modifiers
834 ImGui_ImplWin32_UpdateKeyModifiers(io);
835
836 // Obtain virtual key code and convert to ImGuiKey
837 const ImGuiKey key = ImGui_ImplWin32_KeyEventToImGuiKey(wParam, lParam);
838 const int vk = (int)wParam;
839 const int scancode = (int)LOBYTE(HIWORD(lParam));
840
841 // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.
842 if (key == ImGuiKey_PrintScreen && !is_key_down)
843 ImGui_ImplWin32_AddKeyEvent(io, key, down: true, native_keycode: vk, native_scancode: scancode);
844
845 // Submit key event
846 if (key != ImGuiKey_None)
847 ImGui_ImplWin32_AddKeyEvent(io, key, down: is_key_down, native_keycode: vk, native_scancode: scancode);
848
849 // Submit individual left/right modifier events
850 if (vk == VK_SHIFT)
851 {
852 // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
853 if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
854 if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
855 }
856 else if (vk == VK_CONTROL)
857 {
858 if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); }
859 if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); }
860 }
861 else if (vk == VK_MENU)
862 {
863 if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
864 if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(io, ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
865 }
866 }
867 return 0;
868 }
869 case WM_SETFOCUS:
870 case WM_KILLFOCUS:
871 io.AddFocusEvent(msg == WM_SETFOCUS);
872 return 0;
873 case WM_INPUTLANGCHANGE:
874 ImGui_ImplWin32_UpdateKeyboardCodePage(io);
875 return 0;
876 case WM_CHAR:
877 if (::IsWindowUnicode(hwnd))
878 {
879 // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
880 if (wParam > 0 && wParam < 0x10000)
881 io.AddInputCharacterUTF16(c: (unsigned short)wParam);
882 }
883 else
884 {
885 wchar_t wch = 0;
886 ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1);
887 io.AddInputCharacter(c: wch);
888 }
889 return 0;
890 case WM_SETCURSOR:
891 // This is required to restore cursor when transitioning from e.g resize borders to client area.
892 if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor(io, bd->LastMouseCursor))
893 return 1;
894 return 0;
895 case WM_DEVICECHANGE:
896#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
897 if ((UINT)wParam == DBT_DEVNODES_CHANGED)
898 bd->WantUpdateHasGamepad = true;
899#endif
900 return 0;
901 case WM_DISPLAYCHANGE:
902 bd->WantUpdateMonitors = true;
903 return 0;
904 case WM_SETTINGCHANGE:
905 if (wParam == SPI_SETWORKAREA)
906 bd->WantUpdateMonitors = true;
907 return 0;
908 case WM_DPICHANGED:
909 {
910 const RECT* suggested_rect = (RECT*)lParam;
911 if (io.ConfigDpiScaleViewports)
912 ::SetWindowPos(hwnd, nullptr, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
913 return 0;
914 }
915 }
916 return 0;
917}
918
919
920//--------------------------------------------------------------------------------------------------------
921// DPI-related helpers (optional)
922//--------------------------------------------------------------------------------------------------------
923// - Use to enable DPI awareness without having to create an application manifest.
924// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
925// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
926// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
927// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
928//---------------------------------------------------------------------------------------------------------
929// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
930// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
931// If you are trying to implement your own backend for your own engine, you may ignore that noise.
932//---------------------------------------------------------------------------------------------------------
933
934// Perform our own check with RtlVerifyVersionInfo() instead of using functions from <VersionHelpers.h> as they
935// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200
936static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD)
937{
938 typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG);
939 static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = nullptr;
940 if (RtlVerifyVersionInfoFn == nullptr)
941 if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll"))
942 RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo");
943 if (RtlVerifyVersionInfoFn == nullptr)
944 return FALSE;
945
946 RTL_OSVERSIONINFOEXW versionInfo = { };
947 ULONGLONG conditionMask = 0;
948 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
949 versionInfo.dwMajorVersion = major;
950 versionInfo.dwMinorVersion = minor;
951 VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
952 VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
953 return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE;
954}
955
956#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
957#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
958#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
959#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10
960
961#ifndef DPI_ENUMS_DECLARED
962typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
963typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
964#endif
965#ifndef _DPI_AWARENESS_CONTEXTS_
966DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
967#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
968#endif
969#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
970#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
971#endif
972typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
973typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
974typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
975
976// Helper function to enable DPI awareness without setting up a manifest
977void ImGui_ImplWin32_EnableDpiAwareness()
978{
979 // Make sure monitors will be updated with latest correct scaling
980 if (ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData())
981 bd->WantUpdateMonitors = true;
982
983 if (_IsWindows10OrGreater())
984 {
985 static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
986 if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
987 {
988 SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
989 return;
990 }
991 }
992 if (_IsWindows8Point1OrGreater())
993 {
994 static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
995 if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
996 {
997 SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
998 return;
999 }
1000 }
1001#if _WIN32_WINNT >= 0x0600
1002 ::SetProcessDPIAware();
1003#endif
1004}
1005
1006#if defined(_MSC_VER) && !defined(NOGDI)
1007#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32'
1008#endif
1009
1010float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
1011{
1012 UINT xdpi = 96, ydpi = 96;
1013 if (_IsWindows8Point1OrGreater())
1014 {
1015 static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
1016 static PFN_GetDpiForMonitor GetDpiForMonitorFn = nullptr;
1017 if (GetDpiForMonitorFn == nullptr && shcore_dll != nullptr)
1018 GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor");
1019 if (GetDpiForMonitorFn != nullptr)
1020 {
1021 GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
1022 IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
1023 return xdpi / 96.0f;
1024 }
1025 }
1026#ifndef NOGDI
1027 const HDC dc = ::GetDC(nullptr);
1028 xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
1029 ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
1030 IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
1031 ::ReleaseDC(nullptr, dc);
1032#endif
1033 return xdpi / 96.0f;
1034}
1035
1036float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
1037{
1038 HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
1039 return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
1040}
1041
1042//---------------------------------------------------------------------------------------------------------
1043// Transparency related helpers (optional)
1044//--------------------------------------------------------------------------------------------------------
1045
1046#if defined(_MSC_VER)
1047#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi'
1048#endif
1049
1050// [experimental]
1051// Borrowed from GLFW's function updateFramebufferTransparency() in src/win32_window.c
1052// (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW)
1053void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd)
1054{
1055 if (!_IsWindowsVistaOrGreater())
1056 return;
1057
1058 BOOL composition;
1059 if (FAILED(::DwmIsCompositionEnabled(&composition)) || !composition)
1060 return;
1061
1062 BOOL opaque;
1063 DWORD color;
1064 if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque))
1065 {
1066 HRGN region = ::CreateRectRgn(0, 0, -1, -1);
1067 DWM_BLURBEHIND bb = {};
1068 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
1069 bb.hRgnBlur = region;
1070 bb.fEnable = TRUE;
1071 ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
1072 ::DeleteObject(region);
1073 }
1074 else
1075 {
1076 DWM_BLURBEHIND bb = {};
1077 bb.dwFlags = DWM_BB_ENABLE;
1078 ::DwmEnableBlurBehindWindow((HWND)hwnd, &bb);
1079 }
1080}
1081
1082//---------------------------------------------------------------------------------------------------------
1083// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
1084// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
1085// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
1086//--------------------------------------------------------------------------------------------------------
1087
1088// Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data.
1089struct ImGui_ImplWin32_ViewportData
1090{
1091 HWND Hwnd; // Stored in ImGuiViewport::PlatformHandle + PlatformHandleRaw
1092 HWND HwndParent;
1093 bool HwndOwned;
1094 DWORD DwStyle;
1095 DWORD DwExStyle;
1096
1097 ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; }
1098 ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); }
1099};
1100
1101static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style)
1102{
1103 if (flags & ImGuiViewportFlags_NoDecoration)
1104 *out_style = WS_POPUP;
1105 else
1106 *out_style = WS_OVERLAPPEDWINDOW;
1107
1108 if (flags & ImGuiViewportFlags_NoTaskBarIcon)
1109 *out_ex_style = WS_EX_TOOLWINDOW;
1110 else
1111 *out_ex_style = WS_EX_APPWINDOW;
1112
1113 if (flags & ImGuiViewportFlags_TopMost)
1114 *out_ex_style |= WS_EX_TOPMOST;
1115}
1116
1117static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id)
1118{
1119 if (viewport_id != 0)
1120 if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id))
1121 return (HWND)viewport->PlatformHandle;
1122 return nullptr;
1123}
1124
1125static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
1126{
1127 ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
1128 viewport->PlatformUserData = vd;
1129
1130 // Select style and parent window
1131 ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle);
1132 vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
1133
1134 // Create window
1135 RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
1136 ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
1137 vd->Hwnd = ::CreateWindowExW(
1138 vd->DwExStyle, L"ImGui Platform", L"Untitled", vd->DwStyle, // Style, class name, window name
1139 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
1140 vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param
1141 vd->HwndOwned = true;
1142 viewport->PlatformRequestResize = false;
1143 viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd;
1144
1145 // Secondary viewports store their imgui context
1146 ::SetPropA(vd->Hwnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext());
1147}
1148
1149static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
1150{
1151 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
1152 if (ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData)
1153 {
1154 if (::GetCapture() == vd->Hwnd)
1155 {
1156 // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
1157 ::ReleaseCapture();
1158 ::SetCapture(bd->hWnd);
1159 }
1160 if (vd->Hwnd && vd->HwndOwned)
1161 ::DestroyWindow(vd->Hwnd);
1162 vd->Hwnd = nullptr;
1163 IM_DELETE(p: vd);
1164 }
1165 viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
1166}
1167
1168static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
1169{
1170 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1171 IM_ASSERT(vd->Hwnd != 0);
1172
1173 // ShowParent() also brings parent to front, which is not always desirable,
1174 // so we temporarily disable parenting. (#7354)
1175 if (vd->HwndParent != NULL)
1176 ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)nullptr);
1177
1178 if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
1179 ::ShowWindow(vd->Hwnd, SW_SHOWNA);
1180 else
1181 ::ShowWindow(vd->Hwnd, SW_SHOW);
1182
1183 // Restore
1184 if (vd->HwndParent != NULL)
1185 ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent);
1186}
1187
1188static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport)
1189{
1190 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1191 IM_ASSERT(vd->Hwnd != 0);
1192
1193 // Update Win32 parent if it changed _after_ creation
1194 // Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually.
1195 HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId);
1196 if (new_parent != vd->HwndParent)
1197 {
1198 // Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner).
1199 // Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner".
1200 // The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD.
1201 // Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping.
1202 // Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented.
1203 // https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613
1204 vd->HwndParent = new_parent;
1205 ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent);
1206 }
1207
1208 // (Optional) Update Win32 style if it changed _after_ creation.
1209 // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful.
1210 DWORD new_style;
1211 DWORD new_ex_style;
1212 ImGui_ImplWin32_GetWin32StyleFromViewportFlags(flags: viewport->Flags, out_style: &new_style, out_ex_style: &new_ex_style);
1213
1214 // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows)
1215 if (vd->DwStyle != new_style || vd->DwExStyle != new_ex_style)
1216 {
1217 // (Optional) Update TopMost state if it changed _after_ creation
1218 bool top_most_changed = (vd->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST);
1219 HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0;
1220 UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER;
1221
1222 // Apply flags and position (since it is affected by flags)
1223 vd->DwStyle = new_style;
1224 vd->DwExStyle = new_ex_style;
1225 ::SetWindowLong(vd->Hwnd, GWL_STYLE, vd->DwStyle);
1226 ::SetWindowLong(vd->Hwnd, GWL_EXSTYLE, vd->DwExStyle);
1227 RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
1228 ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
1229 ::SetWindowPos(vd->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED);
1230 ::ShowWindow(vd->Hwnd, SW_SHOWNA); // This is necessary when we alter the style
1231 viewport->PlatformRequestMove = viewport->PlatformRequestResize = true;
1232 }
1233}
1234
1235static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
1236{
1237 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1238 IM_ASSERT(vd->Hwnd != 0);
1239 POINT pos = { 0, 0 };
1240 ::ClientToScreen(vd->Hwnd, &pos);
1241 return ImVec2((float)pos.x, (float)pos.y);
1242}
1243
1244static void ImGui_ImplWin32_UpdateWin32StyleFromWindow(ImGuiViewport* viewport)
1245{
1246 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1247 vd->DwStyle = ::GetWindowLongW(vd->Hwnd, GWL_STYLE);
1248 vd->DwExStyle = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE);
1249}
1250
1251static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
1252{
1253 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1254 IM_ASSERT(vd->Hwnd != 0);
1255 RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
1256 if (viewport->Flags & ImGuiViewportFlags_OwnedByApp)
1257 ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using
1258 ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle);
1259 ::SetWindowPos(vd->Hwnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1260}
1261
1262static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
1263{
1264 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1265 IM_ASSERT(vd->Hwnd != 0);
1266 RECT rect;
1267 ::GetClientRect(vd->Hwnd, &rect);
1268 return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
1269}
1270
1271static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
1272{
1273 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1274 IM_ASSERT(vd->Hwnd != 0);
1275 RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
1276 if (viewport->Flags & ImGuiViewportFlags_OwnedByApp)
1277 ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using
1278 ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen
1279 ::SetWindowPos(vd->Hwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1280}
1281
1282static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport)
1283{
1284 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1285 IM_ASSERT(vd->Hwnd != 0);
1286 ::BringWindowToTop(vd->Hwnd);
1287 ::SetForegroundWindow(vd->Hwnd);
1288 ::SetFocus(vd->Hwnd);
1289}
1290
1291static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport)
1292{
1293 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1294 IM_ASSERT(vd->Hwnd != 0);
1295 return ::GetForegroundWindow() == vd->Hwnd;
1296}
1297
1298static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport)
1299{
1300 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1301 IM_ASSERT(vd->Hwnd != 0);
1302 return ::IsIconic(vd->Hwnd) != 0;
1303}
1304
1305static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
1306{
1307 // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string.
1308 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1309 IM_ASSERT(vd->Hwnd != 0);
1310 int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0);
1311 ImVector<wchar_t> title_w;
1312 title_w.resize(new_size: n);
1313 ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n);
1314
1315 // Calling SetWindowTextW() in a project where UNICODE is not set doesn't work but there's a trick
1316 // which is to pass it directly to the DefWindowProcW() handler.
1317 // See: https://stackoverflow.com/questions/9410681/setwindowtextw-in-an-ansi-project
1318 //::SetWindowTextW(vd->Hwnd, title_w.Data);
1319 ::DefWindowProcW(vd->Hwnd, WM_SETTEXT, 0, (LPARAM)title_w.Data);
1320}
1321
1322static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
1323{
1324 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1325 IM_ASSERT(vd->Hwnd != 0);
1326 IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
1327 if (alpha < 1.0f)
1328 {
1329 DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
1330 ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style);
1331 ::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
1332 }
1333 else
1334 {
1335 DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
1336 ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style);
1337 }
1338}
1339
1340static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport)
1341{
1342 ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData;
1343 IM_ASSERT(vd->Hwnd != 0);
1344 return ImGui_ImplWin32_GetDpiScaleForHwnd(vd->Hwnd);
1345}
1346
1347// FIXME-DPI: Testing DPI related ideas
1348static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
1349{
1350 (void)viewport;
1351#if 0
1352 ImGuiStyle default_style;
1353 //default_style.WindowPadding = ImVec2(0, 0);
1354 //default_style.WindowBorderSize = 0.0f;
1355 //default_style.ItemSpacing.y = 3.0f;
1356 //default_style.FramePadding = ImVec2(0, 0);
1357 default_style.ScaleAllSizes(viewport->DpiScale);
1358 ImGuiStyle& style = ImGui::GetStyle();
1359 style = default_style;
1360#endif
1361}
1362
1363namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); extern ImGuiPlatformIO& GetPlatformIO(ImGuiContext*); }
1364
1365static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1366{
1367 // Allow secondary viewport WndProc to be called regardless of current context
1368 ImGuiContext* ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT");
1369 if (ctx == NULL)
1370 return DefWindowProc(hWnd, msg, wParam, lParam); // unlike ImGui_ImplWin32_WndProcHandler() we are called directly by Windows, we can't just return 0.
1371
1372 ImGuiIO& io = ImGui::GetIO(ctx);
1373 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(ctx);
1374 LRESULT result = 0;
1375 if (ImGui_ImplWin32_WndProcHandlerEx(hWnd, msg, wParam, lParam, io))
1376 result = 1;
1377 else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hWnd))
1378 {
1379 switch (msg)
1380 {
1381 case WM_CLOSE:
1382 viewport->PlatformRequestClose = true;
1383 return 0; // 0 = Operating system will ignore the message and not destroy the window. We close ourselves.
1384 case WM_MOVE:
1385 viewport->PlatformRequestMove = true;
1386 break;
1387 case WM_SIZE:
1388 viewport->PlatformRequestResize = true;
1389 break;
1390 case WM_MOUSEACTIVATE:
1391 if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
1392 result = MA_NOACTIVATE;
1393 break;
1394 case WM_NCHITTEST:
1395 // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional).
1396 // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
1397 // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
1398 // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
1399 if (viewport->Flags & ImGuiViewportFlags_NoInputs)
1400 result = HTTRANSPARENT;
1401 break;
1402 }
1403 }
1404 if (result == 0)
1405 result = DefWindowProc(hWnd, msg, wParam, lParam);
1406 return result;
1407}
1408
1409static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc)
1410{
1411 WNDCLASSEXW wcex;
1412 wcex.cbSize = sizeof(WNDCLASSEXW);
1413 wcex.style = CS_HREDRAW | CS_VREDRAW | (platform_has_own_dc ? CS_OWNDC : 0);
1414 wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
1415 wcex.cbClsExtra = 0;
1416 wcex.cbWndExtra = 0;
1417 wcex.hInstance = ::GetModuleHandle(nullptr);
1418 wcex.hIcon = nullptr;
1419 wcex.hCursor = nullptr;
1420 wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
1421 wcex.lpszMenuName = nullptr;
1422 wcex.lpszClassName = L"ImGui Platform";
1423 wcex.hIconSm = nullptr;
1424 ::RegisterClassExW(&wcex);
1425
1426 ImGui_ImplWin32_UpdateMonitors();
1427
1428 // Register platform interface (will be coupled with a renderer interface)
1429 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1430 platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow;
1431 platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow;
1432 platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow;
1433 platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos;
1434 platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos;
1435 platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize;
1436 platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize;
1437 platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus;
1438 platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus;
1439 platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized;
1440 platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
1441 platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
1442 platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow;
1443 platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI
1444 platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI
1445
1446 // Register main window handle (which is owned by the main application, not by us)
1447 // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
1448 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1449 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
1450 ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)();
1451 vd->Hwnd = bd->hWnd;
1452 vd->HwndOwned = false;
1453 main_viewport->PlatformUserData = vd;
1454}
1455
1456static void ImGui_ImplWin32_ShutdownMultiViewportSupport()
1457{
1458 ::UnregisterClassW(L"ImGui Platform", ::GetModuleHandle(nullptr));
1459 ImGui::DestroyPlatformWindows();
1460}
1461
1462//---------------------------------------------------------------------------------------------------------
1463
1464#if defined(__GNUC__)
1465#pragma GCC diagnostic pop
1466#endif
1467#if defined(__clang__)
1468#pragma clang diagnostic pop
1469#endif
1470
1471#endif // #ifndef IMGUI_DISABLE
1472

source code of imgui/backends/imgui_impl_win32.cpp