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