1 // dear imgui: Platform Binding for Windows (standard windows API for 32 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 cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
7 // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
8 // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
9 // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
10
11 #ifdef _WIN32 /* [Bruno Levy] */
12
13 #include "imgui.h"
14 #include "imgui_impl_win32.h"
15 #ifndef WIN32_LEAN_AND_MEAN
16 #define WIN32_LEAN_AND_MEAN
17 #endif
18 #include <windows.h>
19 #include <tchar.h>
20
21 // Using XInput library for gamepad (with recent Windows SDK this may leads to executables which won't run on Windows 7)
22 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
23 #include <XInput.h>
24 #else
25 #define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT
26 #endif
27 #if defined(_MSC_VER) && !defined(IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT)
28 #pragma comment(lib, "xinput")
29 //#pragma comment(lib, "Xinput9_1_0")
30 #endif
31
32 // CHANGELOG
33 // (minor and older changes stripped away, please see git history for details)
34 // 2020-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
35 // 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
36 // 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
37 // 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
38 // 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
39 // 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
40 // 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
41 // 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
42 // 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
43 // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
44 // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
45 // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
46 // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
47 // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
48 // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
49 // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
50 // 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
51 // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
52 // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
53 // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
54 // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
55 // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
56 // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
57 // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
58
59 // Win32 Data
60 static HWND g_hWnd = NULL;
61 static INT64 g_Time = 0;
62 static INT64 g_TicksPerSecond = 0;
63 static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
64 static bool g_HasGamepad = false;
65 static bool g_WantUpdateHasGamepad = true;
66 static bool g_WantUpdateMonitors = true;
67
68 // Forward Declarations
69 static void ImGui_ImplWin32_InitPlatformInterface();
70 static void ImGui_ImplWin32_ShutdownPlatformInterface();
71 static void ImGui_ImplWin32_UpdateMonitors();
72
73 // Functions
ImGui_ImplWin32_Init(void * hwnd)74 bool ImGui_ImplWin32_Init(void* hwnd)
75 {
76 if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond))
77 return false;
78 if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time))
79 return false;
80
81 // Setup back-end capabilities flags
82 ImGuiIO& io = ImGui::GetIO();
83 io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
84 io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
85 io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
86 io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
87 io.BackendPlatformName = "imgui_impl_win32";
88
89 // Our mouse update function expect PlatformHandle to be filled for the main viewport
90 g_hWnd = (HWND)hwnd;
91 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
92 main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)g_hWnd;
93 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
94 ImGui_ImplWin32_InitPlatformInterface();
95
96 // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
97 io.KeyMap[ImGuiKey_Tab] = VK_TAB;
98 io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
99 io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
100 io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
101 io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
102 io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
103 io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
104 io.KeyMap[ImGuiKey_Home] = VK_HOME;
105 io.KeyMap[ImGuiKey_End] = VK_END;
106 io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
107 io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
108 io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
109 io.KeyMap[ImGuiKey_Space] = VK_SPACE;
110 io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
111 io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
112 io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
113 io.KeyMap[ImGuiKey_A] = 'A';
114 io.KeyMap[ImGuiKey_C] = 'C';
115 io.KeyMap[ImGuiKey_V] = 'V';
116 io.KeyMap[ImGuiKey_X] = 'X';
117 io.KeyMap[ImGuiKey_Y] = 'Y';
118 io.KeyMap[ImGuiKey_Z] = 'Z';
119
120 return true;
121 }
122
ImGui_ImplWin32_Shutdown()123 void ImGui_ImplWin32_Shutdown()
124 {
125 ImGui_ImplWin32_ShutdownPlatformInterface();
126 g_hWnd = (HWND)0;
127 }
128
ImGui_ImplWin32_UpdateMouseCursor()129 static bool ImGui_ImplWin32_UpdateMouseCursor()
130 {
131 ImGuiIO& io = ImGui::GetIO();
132 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
133 return false;
134
135 ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
136 if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
137 {
138 // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
139 ::SetCursor(NULL);
140 }
141 else
142 {
143 // Show OS mouse cursor
144 LPTSTR win32_cursor = IDC_ARROW;
145 switch (imgui_cursor)
146 {
147 case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
148 case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
149 case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
150 case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
151 case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
152 case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
153 case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
154 case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
155 case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
156 }
157 ::SetCursor(::LoadCursor(NULL, win32_cursor));
158 }
159 return true;
160 }
161
162 // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
163 // Because of that, it is a little more complicated than your typical single-viewport binding code!
ImGui_ImplWin32_UpdateMousePos()164 static void ImGui_ImplWin32_UpdateMousePos()
165 {
166 ImGuiIO& io = ImGui::GetIO();
167
168 // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
169 // (When multi-viewports are enabled, all imgui positions are same as OS positions)
170 if (io.WantSetMousePos)
171 {
172 POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
173 if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
174 ::ClientToScreen(g_hWnd, &pos);
175 ::SetCursorPos(pos.x, pos.y);
176 }
177
178 io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
179 io.MouseHoveredViewport = 0;
180
181 // Set imgui mouse position
182 POINT mouse_screen_pos;
183 if (!::GetCursorPos(&mouse_screen_pos))
184 return;
185 if (HWND focused_hwnd = ::GetForegroundWindow())
186 {
187 if (::IsChild(focused_hwnd, g_hWnd))
188 focused_hwnd = g_hWnd;
189 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
190 {
191 // 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)
192 // This is the position you can get with GetCursorPos(). In theory adding viewport->Pos is also the reverse operation of doing ScreenToClient().
193 if (ImGui::FindViewportByPlatformHandle((void*)focused_hwnd) != NULL)
194 io.MousePos = ImVec2((float)mouse_screen_pos.x, (float)mouse_screen_pos.y);
195 }
196 else
197 {
198 // 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.)
199 // This is the position you can get with GetCursorPos() + ScreenToClient() or from WM_MOUSEMOVE.
200 if (focused_hwnd == g_hWnd)
201 {
202 POINT mouse_client_pos = mouse_screen_pos;
203 ::ScreenToClient(focused_hwnd, &mouse_client_pos);
204 io.MousePos = ImVec2((float)mouse_client_pos.x, (float)mouse_client_pos.y);
205 }
206 }
207 }
208
209 // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering.
210 // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because
211 // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows).
212 // - This is _regardless_ of whether another viewport is focused or being dragged from.
213 // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the back-end, imgui will ignore this field and infer the information by relying on the
214 // rectangles and last focused time of every viewports it knows about. It will be unaware of foreign windows that may be sitting between or over your windows.
215 if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
216 if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
217 if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated?
218 io.MouseHoveredViewport = viewport->ID;
219 }
220
221 // Gamepad navigation mapping
ImGui_ImplWin32_UpdateGamepads()222 static void ImGui_ImplWin32_UpdateGamepads()
223 {
224 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
225 ImGuiIO& io = ImGui::GetIO();
226 memset(io.NavInputs, 0, sizeof(io.NavInputs));
227 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
228 return;
229
230 // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
231 // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
232 if (g_WantUpdateHasGamepad)
233 {
234 XINPUT_CAPABILITIES caps;
235 g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
236 g_WantUpdateHasGamepad = false;
237 }
238
239 XINPUT_STATE xinput_state;
240 io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
241 if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
242 {
243 const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
244 io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
245
246 #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
247 #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
248 MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
249 MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
250 MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
251 MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
252 MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
253 MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
254 MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
255 MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
256 MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
257 MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
258 MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
259 MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
260 MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
261 MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
262 MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
263 MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
264 #undef MAP_BUTTON
265 #undef MAP_ANALOG
266 }
267 #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
268 }
269
ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor,HDC,LPRECT,LPARAM)270 static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM)
271 {
272 MONITORINFO info = { 0 };
273 info.cbSize = sizeof(MONITORINFO);
274 if (!::GetMonitorInfo(monitor, &info))
275 return TRUE;
276 ImGuiPlatformMonitor imgui_monitor;
277 imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top);
278 imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top));
279 imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
280 imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top));
281 imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
282 ImGuiPlatformIO& io = ImGui::GetPlatformIO();
283 if (info.dwFlags & MONITORINFOF_PRIMARY)
284 io.Monitors.push_front(imgui_monitor);
285 else
286 io.Monitors.push_back(imgui_monitor);
287 return TRUE;
288 }
289
ImGui_ImplWin32_UpdateMonitors()290 static void ImGui_ImplWin32_UpdateMonitors()
291 {
292 ImGui::GetPlatformIO().Monitors.resize(0);
293 // [Bruno Levy] fourth parameter: NULL -> (LPARAM)(0)
294 ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, (LPARAM)(0));
295 g_WantUpdateMonitors = false;
296 }
297
ImGui_ImplWin32_NewFrame()298 void ImGui_ImplWin32_NewFrame()
299 {
300 ImGuiIO& io = ImGui::GetIO();
301 IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
302
303 // Setup display size (every frame to accommodate for window resizing)
304 RECT rect;
305 ::GetClientRect(g_hWnd, &rect);
306 io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
307 if (g_WantUpdateMonitors)
308 ImGui_ImplWin32_UpdateMonitors();
309
310 // Setup time step
311 INT64 current_time;
312 ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time);
313 io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
314 g_Time = current_time;
315
316 // Read keyboard modifiers inputs
317 io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
318 io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
319 io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
320 io.KeySuper = false;
321 // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
322
323 // Update OS mouse position
324 ImGui_ImplWin32_UpdateMousePos();
325
326 // Update OS mouse cursor with the cursor requested by imgui
327 ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
328 if (g_LastMouseCursor != mouse_cursor)
329 {
330 g_LastMouseCursor = mouse_cursor;
331 ImGui_ImplWin32_UpdateMouseCursor();
332 }
333
334 // Update game controllers (if enabled and available)
335 ImGui_ImplWin32_UpdateGamepads();
336 }
337
338 // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
339 #ifndef WM_MOUSEHWHEEL
340 #define WM_MOUSEHWHEEL 0x020E
341 #endif
342 #ifndef DBT_DEVNODES_CHANGED
343 #define DBT_DEVNODES_CHANGED 0x0007
344 #endif
345
346 // Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
347 // Call from your application's message handler.
348 // When implementing your own back-end, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
349 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
350 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
351 // Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
352 // PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
353 // 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.
354 #if 0
355 // Copy this line into your .cpp file to forward declare the function.
356 extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
357 #endif
ImGui_ImplWin32_WndProcHandler(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)358 IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
359 {
360 if (ImGui::GetCurrentContext() == NULL)
361 return 0;
362
363 ImGuiIO& io = ImGui::GetIO();
364 switch (msg)
365 {
366 case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
367 case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
368 case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
369 case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
370 {
371 int button = 0;
372 if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
373 if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
374 if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
375 if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
376 if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
377 ::SetCapture(hwnd);
378 io.MouseDown[button] = true;
379 return 0;
380 }
381 case WM_LBUTTONUP:
382 case WM_RBUTTONUP:
383 case WM_MBUTTONUP:
384 case WM_XBUTTONUP:
385 {
386 int button = 0;
387 if (msg == WM_LBUTTONUP) { button = 0; }
388 if (msg == WM_RBUTTONUP) { button = 1; }
389 if (msg == WM_MBUTTONUP) { button = 2; }
390 if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
391 io.MouseDown[button] = false;
392 if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
393 ::ReleaseCapture();
394 return 0;
395 }
396 case WM_MOUSEWHEEL:
397 io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
398 return 0;
399 case WM_MOUSEHWHEEL:
400 io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
401 return 0;
402 case WM_KEYDOWN:
403 case WM_SYSKEYDOWN:
404 if (wParam < 256)
405 io.KeysDown[wParam] = 1;
406 return 0;
407 case WM_KEYUP:
408 case WM_SYSKEYUP:
409 if (wParam < 256)
410 io.KeysDown[wParam] = 0;
411 return 0;
412 case WM_CHAR:
413 // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
414 if (wParam > 0 && wParam < 0x10000)
415 io.AddInputCharacterUTF16((unsigned short)wParam);
416 return 0;
417 case WM_SETCURSOR:
418 if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
419 return 1;
420 return 0;
421 case WM_DEVICECHANGE:
422 if ((UINT)wParam == DBT_DEVNODES_CHANGED)
423 g_WantUpdateHasGamepad = true;
424 return 0;
425 case WM_DISPLAYCHANGE:
426 g_WantUpdateMonitors = true;
427 return 0;
428 }
429 return 0;
430 }
431
432
433 //--------------------------------------------------------------------------------------------------------
434 // DPI-related helpers (optional)
435 //--------------------------------------------------------------------------------------------------------
436 // - Use to enable DPI awareness without having to create an application manifest.
437 // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
438 // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
439 // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
440 // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
441 //---------------------------------------------------------------------------------------------------------
442 // This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
443 // ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
444 // If you are trying to implement your own back-end for your own engine, you may ignore that noise.
445 //---------------------------------------------------------------------------------------------------------
446
447 // Implement some of the functions and types normally declared in recent Windows SDK.
448 #if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS)
IsWindowsVersionOrGreater(WORD major,WORD minor,WORD sp)449 static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
450 {
451 OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp, 0, 0, 0, 0 };
452 DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
453 ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
454 cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
455 cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
456 return ::VerifyVersionInfoW(&osvi, mask, cond);
457 }
458 #define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WINBLUE
459 #endif
460
461 #ifndef DPI_ENUMS_DECLARED
462 typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
463 typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
464 #endif
465 #ifndef _DPI_AWARENESS_CONTEXTS_
466 DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
467 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
468 #endif
469 #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
470 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
471 #endif
472 typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
473 typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
474 typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
475
476 // Helper function to enable DPI awareness without setting up a manifest
ImGui_ImplWin32_EnableDpiAwareness()477 void ImGui_ImplWin32_EnableDpiAwareness()
478 {
479 // Make sure monitors will be updated with latest correct scaling
480 g_WantUpdateMonitors = true;
481
482 // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer!
483 {
484 static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
485 if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
486 {
487 SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
488 return;
489 }
490 }
491 if (IsWindows8Point1OrGreater())
492 {
493 static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
494 if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
495 {
496 SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
497 return;
498 }
499 }
500 #if _WIN32_WINNT >= 0x0600
501 ::SetProcessDPIAware();
502 #endif
503 }
504
505 #if defined(_MSC_VER) && !defined(NOGDI)
506 #pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps()
507 #endif
508
ImGui_ImplWin32_GetDpiScaleForMonitor(void * monitor)509 float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
510 {
511 UINT xdpi = 96, ydpi = 96;
512 static BOOL bIsWindows8Point1OrGreater = IsWindows8Point1OrGreater();
513 if (bIsWindows8Point1OrGreater)
514 {
515 static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
516 if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"))
517 GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
518 }
519 #ifndef NOGDI
520 else
521 {
522 const HDC dc = ::GetDC(NULL);
523 xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
524 ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
525 ::ReleaseDC(NULL, dc);
526 }
527 #endif
528 IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
529 return xdpi / 96.0f;
530 }
531
ImGui_ImplWin32_GetDpiScaleForHwnd(void * hwnd)532 float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
533 {
534 HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
535 return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
536 }
537
538
539 //--------------------------------------------------------------------------------------------------------
540 // IME (Input Method Editor) basic support for e.g. Asian language users
541 //--------------------------------------------------------------------------------------------------------
542
543 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
544 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
545 #endif
546
547 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(__GNUC__)
548 #define HAS_WIN32_IME 1
549 #include <imm.h>
550 #ifdef _MSC_VER
551 #pragma comment(lib, "imm32")
552 #endif
ImGui_ImplWin32_SetImeInputPos(ImGuiViewport * viewport,ImVec2 pos)553 static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos)
554 {
555 COMPOSITIONFORM cf = { CFS_FORCE_POSITION,{ (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) },{ 0, 0, 0, 0 } };
556 if (HWND hwnd = (HWND)viewport->PlatformHandle)
557 if (HIMC himc = ::ImmGetContext(hwnd))
558 {
559 ::ImmSetCompositionWindow(himc, &cf);
560 ::ImmReleaseContext(hwnd, himc);
561 }
562 }
563 #else
564 #define HAS_WIN32_IME 0
565 #endif
566
567 //--------------------------------------------------------------------------------------------------------
568 // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
569 // This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously.
570 // 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..
571 //--------------------------------------------------------------------------------------------------------
572
573 // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
574 struct ImGuiViewportDataWin32
575 {
576 HWND Hwnd;
577 bool HwndOwned;
578 DWORD DwStyle;
579 DWORD DwExStyle;
580
ImGuiViewportDataWin32ImGuiViewportDataWin32581 ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; }
~ImGuiViewportDataWin32ImGuiViewportDataWin32582 ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); }
583 };
584
ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags,DWORD * out_style,DWORD * out_ex_style)585 static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style)
586 {
587 if (flags & ImGuiViewportFlags_NoDecoration)
588 *out_style = WS_POPUP;
589 else
590 *out_style = WS_OVERLAPPEDWINDOW;
591
592 if (flags & ImGuiViewportFlags_NoTaskBarIcon)
593 *out_ex_style = WS_EX_TOOLWINDOW;
594 else
595 *out_ex_style = WS_EX_APPWINDOW;
596
597 if (flags & ImGuiViewportFlags_TopMost)
598 *out_ex_style |= WS_EX_TOPMOST;
599 }
600
ImGui_ImplWin32_CreateWindow(ImGuiViewport * viewport)601 static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
602 {
603 ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
604 viewport->PlatformUserData = data;
605
606 // Select style and parent window
607 ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &data->DwStyle, &data->DwExStyle);
608 HWND parent_window = NULL;
609 if (viewport->ParentViewportId != 0)
610 if (ImGuiViewport* parent_viewport = ImGui::FindViewportByID(viewport->ParentViewportId))
611 parent_window = (HWND)parent_viewport->PlatformHandle;
612
613 // Create window
614 RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
615 ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
616 data->Hwnd = ::CreateWindowEx(
617 data->DwExStyle, _T("ImGui Platform"), _T("Untitled"), data->DwStyle, // Style, class name, window name
618 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
619 parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param
620 data->HwndOwned = true;
621 viewport->PlatformRequestResize = false;
622 viewport->PlatformHandle = viewport->PlatformHandleRaw = data->Hwnd;
623 }
624
ImGui_ImplWin32_DestroyWindow(ImGuiViewport * viewport)625 static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
626 {
627 if (ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData)
628 {
629 if (::GetCapture() == data->Hwnd)
630 {
631 // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
632 ::ReleaseCapture();
633 ::SetCapture(g_hWnd);
634 }
635 if (data->Hwnd && data->HwndOwned)
636 ::DestroyWindow(data->Hwnd);
637 data->Hwnd = NULL;
638 IM_DELETE(data);
639 }
640 viewport->PlatformUserData = viewport->PlatformHandle = NULL;
641 }
642
ImGui_ImplWin32_ShowWindow(ImGuiViewport * viewport)643 static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
644 {
645 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
646 IM_ASSERT(data->Hwnd != 0);
647 if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
648 ::ShowWindow(data->Hwnd, SW_SHOWNA);
649 else
650 ::ShowWindow(data->Hwnd, SW_SHOW);
651 }
652
ImGui_ImplWin32_UpdateWindow(ImGuiViewport * viewport)653 static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport)
654 {
655 // (Optional) Update Win32 style if it changed _after_ creation.
656 // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful.
657 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
658 IM_ASSERT(data->Hwnd != 0);
659 DWORD new_style;
660 DWORD new_ex_style;
661 ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style);
662
663 // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows)
664 if (data->DwStyle != new_style || data->DwExStyle != new_ex_style)
665 {
666 data->DwStyle = new_style;
667 data->DwExStyle = new_ex_style;
668 ::SetWindowLong(data->Hwnd, GWL_STYLE, data->DwStyle);
669 ::SetWindowLong(data->Hwnd, GWL_EXSTYLE, data->DwExStyle);
670 RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
671 ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen
672 ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
673 ::ShowWindow(data->Hwnd, SW_SHOWNA); // This is necessary when we alter the style
674 viewport->PlatformRequestMove = viewport->PlatformRequestResize = true;
675 }
676 }
677
ImGui_ImplWin32_GetWindowPos(ImGuiViewport * viewport)678 static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
679 {
680 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
681 IM_ASSERT(data->Hwnd != 0);
682 POINT pos = { 0, 0 };
683 ::ClientToScreen(data->Hwnd, &pos);
684 return ImVec2((float)pos.x, (float)pos.y);
685 }
686
ImGui_ImplWin32_SetWindowPos(ImGuiViewport * viewport,ImVec2 pos)687 static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
688 {
689 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
690 IM_ASSERT(data->Hwnd != 0);
691 RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
692 ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
693 ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
694 }
695
ImGui_ImplWin32_GetWindowSize(ImGuiViewport * viewport)696 static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
697 {
698 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
699 IM_ASSERT(data->Hwnd != 0);
700 RECT rect;
701 ::GetClientRect(data->Hwnd, &rect);
702 return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
703 }
704
ImGui_ImplWin32_SetWindowSize(ImGuiViewport * viewport,ImVec2 size)705 static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
706 {
707 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
708 IM_ASSERT(data->Hwnd != 0);
709 RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
710 ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen
711 ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
712 }
713
ImGui_ImplWin32_SetWindowFocus(ImGuiViewport * viewport)714 static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport)
715 {
716 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
717 IM_ASSERT(data->Hwnd != 0);
718 ::BringWindowToTop(data->Hwnd);
719 ::SetForegroundWindow(data->Hwnd);
720 ::SetFocus(data->Hwnd);
721 }
722
ImGui_ImplWin32_GetWindowFocus(ImGuiViewport * viewport)723 static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport)
724 {
725 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
726 IM_ASSERT(data->Hwnd != 0);
727 return ::GetForegroundWindow() == data->Hwnd;
728 }
729
ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport * viewport)730 static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport)
731 {
732 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
733 IM_ASSERT(data->Hwnd != 0);
734 return ::IsIconic(data->Hwnd) != 0;
735 }
736
ImGui_ImplWin32_SetWindowTitle(ImGuiViewport * viewport,const char * title)737 static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
738 {
739 // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string.
740 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
741 IM_ASSERT(data->Hwnd != 0);
742 int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
743 ImVector<wchar_t> title_w;
744 title_w.resize(n);
745 ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n);
746 ::SetWindowTextW(data->Hwnd, title_w.Data);
747 }
748
ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport * viewport,float alpha)749 static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
750 {
751 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
752 IM_ASSERT(data->Hwnd != 0);
753 IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
754 if (alpha < 1.0f)
755 {
756 DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
757 ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style);
758 ::SetLayeredWindowAttributes(data->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
759 }
760 else
761 {
762 DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
763 ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style);
764 }
765 }
766
ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport * viewport)767 static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport)
768 {
769 ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
770 IM_ASSERT(data->Hwnd != 0);
771 return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd);
772 }
773
774 // FIXME-DPI: Testing DPI related ideas
ImGui_ImplWin32_OnChangedViewport(ImGuiViewport * viewport)775 static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
776 {
777 (void)viewport;
778 #if 0
779 ImGuiStyle default_style;
780 //default_style.WindowPadding = ImVec2(0, 0);
781 //default_style.WindowBorderSize = 0.0f;
782 //default_style.ItemSpacing.y = 3.0f;
783 //default_style.FramePadding = ImVec2(0, 0);
784 default_style.ScaleAllSizes(viewport->DpiScale);
785 ImGuiStyle& style = ImGui::GetStyle();
786 style = default_style;
787 #endif
788 }
789
ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)790 static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
791 {
792 if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
793 return true;
794
795 if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
796 {
797 switch (msg)
798 {
799 case WM_CLOSE:
800 viewport->PlatformRequestClose = true;
801 return 0;
802 case WM_MOVE:
803 viewport->PlatformRequestMove = true;
804 break;
805 case WM_SIZE:
806 viewport->PlatformRequestResize = true;
807 break;
808 case WM_MOUSEACTIVATE:
809 if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
810 return MA_NOACTIVATE;
811 break;
812 case WM_NCHITTEST:
813 // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL).
814 // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
815 // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
816 // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
817 if (viewport->Flags & ImGuiViewportFlags_NoInputs)
818 return HTTRANSPARENT;
819 break;
820 }
821 }
822
823 return DefWindowProc(hWnd, msg, wParam, lParam);
824 }
825
ImGui_ImplWin32_InitPlatformInterface()826 static void ImGui_ImplWin32_InitPlatformInterface()
827 {
828 WNDCLASSEX wcex;
829 wcex.cbSize = sizeof(WNDCLASSEX);
830 wcex.style = CS_HREDRAW | CS_VREDRAW;
831 wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
832 wcex.cbClsExtra = 0;
833 wcex.cbWndExtra = 0;
834 wcex.hInstance = ::GetModuleHandle(NULL);
835 wcex.hIcon = NULL;
836 wcex.hCursor = NULL;
837 wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
838 wcex.lpszMenuName = NULL;
839 wcex.lpszClassName = _T("ImGui Platform");
840 wcex.hIconSm = NULL;
841 ::RegisterClassEx(&wcex);
842
843 ImGui_ImplWin32_UpdateMonitors();
844
845 // Register platform interface (will be coupled with a renderer interface)
846 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
847 platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow;
848 platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow;
849 platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow;
850 platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos;
851 platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos;
852 platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize;
853 platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize;
854 platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus;
855 platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus;
856 platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized;
857 platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
858 platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
859 platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow;
860 platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI
861 platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI
862 #if HAS_WIN32_IME
863 platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos;
864 #endif
865
866 // Register main window handle (which is owned by the main application, not by us)
867 // 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.
868 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
869 ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
870 data->Hwnd = g_hWnd;
871 data->HwndOwned = false;
872 main_viewport->PlatformUserData = data;
873 main_viewport->PlatformHandle = (void*)g_hWnd;
874 }
875
ImGui_ImplWin32_ShutdownPlatformInterface()876 static void ImGui_ImplWin32_ShutdownPlatformInterface()
877 {
878 ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL));
879 }
880
881 //---------------------------------------------------------------------------------------------------------
882
883 #endif /* [Bruno Levy] */
884