imgui_impl_win32.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  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. // Implemented features:
  4. // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
  5. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
  6. // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
  7. // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
  8. // Missing features:
  9. // [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice).
  10. #include "imgui.h"
  11. #include "imgui_impl_win32.h"
  12. #ifndef WIN32_LEAN_AND_MEAN
  13. #define WIN32_LEAN_AND_MEAN
  14. #endif
  15. #include <windows.h>
  16. #include <tchar.h>
  17. // CHANGELOG
  18. // (minor and older changes stripped away, please see git history for details)
  19. // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
  20. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
  21. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
  22. // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
  23. // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
  24. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
  25. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
  26. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
  27. // 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
  28. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
  29. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
  30. // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
  31. // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
  32. // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
  33. // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
  34. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
  35. // Win32 Data
  36. static HWND g_hWnd = 0;
  37. static INT64 g_Time = 0;
  38. static INT64 g_TicksPerSecond = 0;
  39. static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
  40. static bool g_WantUpdateMonitors = true;
  41. // Forward Declarations
  42. static void ImGui_ImplWin32_InitPlatformInterface();
  43. static void ImGui_ImplWin32_ShutdownPlatformInterface();
  44. static void ImGui_ImplWin32_UpdateMonitors();
  45. // Functions
  46. bool ImGui_ImplWin32_Init(void* hwnd)
  47. {
  48. if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
  49. return false;
  50. if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
  51. return false;
  52. // Setup back-end capabilities flags
  53. ImGuiIO& io = ImGui::GetIO();
  54. io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
  55. io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
  56. io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
  57. io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
  58. io.BackendPlatformName = "imgui_impl_win32";
  59. // Our mouse update function expect PlatformHandle to be filled for the main viewport
  60. g_hWnd = (HWND)hwnd;
  61. ImGuiViewport* main_viewport = ImGui::GetMainViewport();
  62. main_viewport->PlatformHandle = (void*)g_hWnd;
  63. if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
  64. ImGui_ImplWin32_InitPlatformInterface();
  65. // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
  66. io.KeyMap[ImGuiKey_Tab] = VK_TAB;
  67. io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
  68. io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
  69. io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
  70. io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
  71. io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
  72. io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
  73. io.KeyMap[ImGuiKey_Home] = VK_HOME;
  74. io.KeyMap[ImGuiKey_End] = VK_END;
  75. io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
  76. io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
  77. io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
  78. io.KeyMap[ImGuiKey_Space] = VK_SPACE;
  79. io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
  80. io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
  81. io.KeyMap[ImGuiKey_A] = 'A';
  82. io.KeyMap[ImGuiKey_C] = 'C';
  83. io.KeyMap[ImGuiKey_V] = 'V';
  84. io.KeyMap[ImGuiKey_X] = 'X';
  85. io.KeyMap[ImGuiKey_Y] = 'Y';
  86. io.KeyMap[ImGuiKey_Z] = 'Z';
  87. return true;
  88. }
  89. void ImGui_ImplWin32_Shutdown()
  90. {
  91. ImGui_ImplWin32_ShutdownPlatformInterface();
  92. g_hWnd = (HWND)0;
  93. }
  94. static bool ImGui_ImplWin32_UpdateMouseCursor()
  95. {
  96. ImGuiIO& io = ImGui::GetIO();
  97. if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
  98. return false;
  99. ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
  100. if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
  101. {
  102. // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
  103. ::SetCursor(NULL);
  104. }
  105. else
  106. {
  107. // Show OS mouse cursor
  108. LPTSTR win32_cursor = IDC_ARROW;
  109. switch (imgui_cursor)
  110. {
  111. case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
  112. case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
  113. case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
  114. case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
  115. case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
  116. case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
  117. case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
  118. case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
  119. }
  120. ::SetCursor(::LoadCursor(NULL, win32_cursor));
  121. }
  122. return true;
  123. }
  124. // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
  125. // Because of that, it is a little more complicated than your typical single-viewport binding code!
  126. static void ImGui_ImplWin32_UpdateMousePos()
  127. {
  128. ImGuiIO& io = ImGui::GetIO();
  129. // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
  130. // (When multi-viewports are enabled, all imgui positions are same as OS positions)
  131. if (io.WantSetMousePos)
  132. {
  133. POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
  134. if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
  135. ::ClientToScreen(g_hWnd, &pos);
  136. ::SetCursorPos(pos.x, pos.y);
  137. }
  138. io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
  139. io.MouseHoveredViewport = 0;
  140. // Set imgui mouse position
  141. POINT mouse_screen_pos;
  142. if (!::GetCursorPos(&mouse_screen_pos))
  143. return;
  144. if (HWND focused_hwnd = ::GetActiveWindow())
  145. {
  146. if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
  147. {
  148. // 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)
  149. // This is the position you can get with GetCursorPos(). In theory adding viewport->Pos is also the reverse operation of doing ScreenToClient().
  150. if (ImGui::FindViewportByPlatformHandle((void*)focused_hwnd) != NULL)
  151. io.MousePos = ImVec2((float)mouse_screen_pos.x, (float)mouse_screen_pos.y);
  152. }
  153. else
  154. {
  155. // 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.)
  156. // This is the position you can get with GetCursorPos() + ScreenToClient() or from WM_MOUSEMOVE.
  157. if (focused_hwnd == g_hWnd)
  158. {
  159. POINT mouse_client_pos = mouse_screen_pos;
  160. ::ScreenToClient(focused_hwnd, &mouse_client_pos);
  161. io.MousePos = ImVec2((float)mouse_client_pos.x, (float)mouse_client_pos.y);
  162. }
  163. }
  164. }
  165. // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering.
  166. // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because
  167. // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows).
  168. // - This is _regardless_ of whether another viewport is focused or being dragged from.
  169. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the back-end, imgui will ignore this field and infer the information by relying on the
  170. // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows.
  171. if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
  172. if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
  173. io.MouseHoveredViewport = viewport->ID;
  174. }
  175. void ImGui_ImplWin32_NewFrame()
  176. {
  177. ImGuiIO& io = ImGui::GetIO();
  178. // Setup display size (every frame to accommodate for window resizing)
  179. RECT rect;
  180. ::GetClientRect(g_hWnd, &rect);
  181. io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
  182. if (g_WantUpdateMonitors)
  183. ImGui_ImplWin32_UpdateMonitors();
  184. // Setup time step
  185. INT64 current_time;
  186. ::QueryPerformanceCounter((LARGE_INTEGER *)&current_time);
  187. io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
  188. g_Time = current_time;
  189. // Read keyboard modifiers inputs
  190. io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
  191. io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
  192. io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
  193. io.KeySuper = false;
  194. // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
  195. // Update OS mouse position
  196. ImGui_ImplWin32_UpdateMousePos();
  197. // Update OS mouse cursor with the cursor requested by imgui
  198. ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
  199. if (g_LastMouseCursor != mouse_cursor)
  200. {
  201. g_LastMouseCursor = mouse_cursor;
  202. ImGui_ImplWin32_UpdateMouseCursor();
  203. }
  204. }
  205. // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
  206. #ifndef WM_MOUSEHWHEEL
  207. #define WM_MOUSEHWHEEL 0x020E
  208. #endif
  209. // Process Win32 mouse/keyboard inputs.
  210. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
  211. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
  212. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
  213. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
  214. // PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds.
  215. // 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.
  216. IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  217. {
  218. if (ImGui::GetCurrentContext() == NULL)
  219. return 0;
  220. ImGuiIO& io = ImGui::GetIO();
  221. switch (msg)
  222. {
  223. case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
  224. case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
  225. case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
  226. {
  227. int button = 0;
  228. if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0;
  229. if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1;
  230. if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2;
  231. if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
  232. ::SetCapture(hwnd);
  233. io.MouseDown[button] = true;
  234. return 0;
  235. }
  236. case WM_LBUTTONUP:
  237. case WM_RBUTTONUP:
  238. case WM_MBUTTONUP:
  239. {
  240. int button = 0;
  241. if (msg == WM_LBUTTONUP) button = 0;
  242. if (msg == WM_RBUTTONUP) button = 1;
  243. if (msg == WM_MBUTTONUP) button = 2;
  244. io.MouseDown[button] = false;
  245. if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
  246. ::ReleaseCapture();
  247. return 0;
  248. }
  249. case WM_MOUSEWHEEL:
  250. io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
  251. return 0;
  252. case WM_MOUSEHWHEEL:
  253. io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
  254. return 0;
  255. case WM_KEYDOWN:
  256. case WM_SYSKEYDOWN:
  257. if (wParam < 256)
  258. io.KeysDown[wParam] = 1;
  259. return 0;
  260. case WM_KEYUP:
  261. case WM_SYSKEYUP:
  262. if (wParam < 256)
  263. io.KeysDown[wParam] = 0;
  264. return 0;
  265. case WM_CHAR:
  266. // You can also use ToAscii()+GetKeyboardState() to retrieve characters.
  267. if (wParam > 0 && wParam < 0x10000)
  268. io.AddInputCharacter((unsigned short)wParam);
  269. return 0;
  270. case WM_SETCURSOR:
  271. if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
  272. return 1;
  273. return 0;
  274. case WM_DISPLAYCHANGE:
  275. g_WantUpdateMonitors = true;
  276. return 0;
  277. }
  278. return 0;
  279. }
  280. //--------------------------------------------------------------------------------------------------------
  281. // DPI handling
  282. // Those in theory should be simple calls but Windows has multiple ways to handle DPI, and most of them
  283. // require recent Windows versions at runtime or recent Windows SDK at compile-time. Neither we want to depend on.
  284. // So we dynamically select and load those functions to avoid dependencies. This is the scheme successfully
  285. // used by GLFW (from which we borrowed some of the code here) and other applications aiming to be portable.
  286. //---------------------------------------------------------------------------------------------------------
  287. // At this point ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
  288. //---------------------------------------------------------------------------------------------------------
  289. static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
  290. {
  291. OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0,{ 0 }, sp };
  292. DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
  293. ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
  294. cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
  295. cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
  296. return VerifyVersionInfoW(&osvi, mask, cond);
  297. }
  298. #define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WINBLUE
  299. #define IsWindows10OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WIN10
  300. #ifndef DPI_ENUMS_DECLARED
  301. typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
  302. typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
  303. #endif
  304. #ifndef _DPI_AWARENESS_CONTEXTS_
  305. DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
  306. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
  307. #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
  308. #endif
  309. typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib+dll, Windows 8.1
  310. typedef HRESULT(WINAPI * PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib+dll, Windows 8.1
  311. typedef DPI_AWARENESS_CONTEXT(WINAPI * PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib+dll, Windows 10 v1607 (Creators Update)
  312. void ImGui_ImplWin32_EnableDpiAwareness()
  313. {
  314. // if (IsWindows10OrGreater()) // FIXME-DPI: This needs a manifest to succeed. Instead we try to grab the function pointer.
  315. {
  316. static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
  317. if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
  318. {
  319. SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
  320. return;
  321. }
  322. }
  323. if (IsWindows8Point1OrGreater())
  324. {
  325. static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
  326. if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
  327. SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
  328. }
  329. else
  330. {
  331. SetProcessDPIAware();
  332. }
  333. }
  334. float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
  335. {
  336. UINT xdpi = 96, ydpi = 96;
  337. if (IsWindows8Point1OrGreater())
  338. {
  339. static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
  340. if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"))
  341. GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
  342. }
  343. else
  344. {
  345. const HDC dc = ::GetDC(NULL);
  346. xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
  347. ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
  348. ::ReleaseDC(NULL, dc);
  349. }
  350. IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
  351. return xdpi / 96.0f;
  352. }
  353. float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
  354. {
  355. HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
  356. return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
  357. }
  358. float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2)
  359. {
  360. RECT viewport_rect = { (LONG)x1, (LONG)y1, (LONG)x2, (LONG)y2 };
  361. HMONITOR monitor = ::MonitorFromRect(&viewport_rect, MONITOR_DEFAULTTONEAREST);
  362. return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
  363. }
  364. //--------------------------------------------------------------------------------------------------------
  365. // IME (Input Method Editor) basic support for e.g. Asian language users
  366. //--------------------------------------------------------------------------------------------------------
  367. #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(__GNUC__)
  368. #define HAS_WIN32_IME 1
  369. #include <imm.h>
  370. #ifdef _MSC_VER
  371. #pragma comment(lib, "imm32")
  372. #endif
  373. static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos)
  374. {
  375. COMPOSITIONFORM cf = { CFS_FORCE_POSITION,{ (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) },{ 0, 0, 0, 0 } };
  376. if (HWND hwnd = (HWND)viewport->PlatformHandle)
  377. if (HIMC himc = ::ImmGetContext(hwnd))
  378. {
  379. ::ImmSetCompositionWindow(himc, &cf);
  380. ::ImmReleaseContext(hwnd, himc);
  381. }
  382. }
  383. #else
  384. #define HAS_WIN32_IME 0
  385. #endif
  386. //--------------------------------------------------------------------------------------------------------
  387. // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
  388. // This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously.
  389. // 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..
  390. //--------------------------------------------------------------------------------------------------------
  391. struct ImGuiViewportDataWin32
  392. {
  393. HWND Hwnd;
  394. bool HwndOwned;
  395. DWORD DwStyle;
  396. DWORD DwExStyle;
  397. ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; }
  398. ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); }
  399. };
  400. static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport)
  401. {
  402. ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
  403. viewport->PlatformUserData = data;
  404. bool no_decoration = (viewport->Flags & ImGuiViewportFlags_NoDecoration) != 0;
  405. bool no_task_bar_icon = (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) != 0;
  406. if (no_decoration)
  407. {
  408. data->DwStyle = WS_POPUP;
  409. data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW;
  410. }
  411. else
  412. {
  413. data->DwStyle = WS_OVERLAPPEDWINDOW;
  414. data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW;
  415. }
  416. if (viewport->Flags & ImGuiViewportFlags_TopMost)
  417. data->DwExStyle |= WS_EX_TOPMOST;
  418. // Create window
  419. RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) };
  420. ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
  421. data->Hwnd = ::CreateWindowEx(
  422. data->DwExStyle, _T("ImGui Platform"), _T("No Title Yet"), data->DwStyle, // Style, class name, window name
  423. rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
  424. g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param
  425. data->HwndOwned = true;
  426. viewport->PlatformRequestResize = false;
  427. viewport->PlatformHandle = data->Hwnd;
  428. }
  429. static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport)
  430. {
  431. if (ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData)
  432. {
  433. if (::GetCapture() == data->Hwnd)
  434. {
  435. // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event.
  436. ::ReleaseCapture();
  437. ::SetCapture(g_hWnd);
  438. }
  439. if (data->Hwnd && data->HwndOwned)
  440. ::DestroyWindow(data->Hwnd);
  441. data->Hwnd = NULL;
  442. IM_DELETE(data);
  443. }
  444. viewport->PlatformUserData = viewport->PlatformHandle = NULL;
  445. }
  446. static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
  447. {
  448. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  449. IM_ASSERT(data->Hwnd != 0);
  450. if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
  451. ::ShowWindow(data->Hwnd, SW_SHOWNA);
  452. else
  453. ::ShowWindow(data->Hwnd, SW_SHOW);
  454. }
  455. static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
  456. {
  457. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  458. IM_ASSERT(data->Hwnd != 0);
  459. POINT pos = { 0, 0 };
  460. ::ClientToScreen(data->Hwnd, &pos);
  461. return ImVec2((float)pos.x, (float)pos.y);
  462. }
  463. static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
  464. {
  465. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  466. IM_ASSERT(data->Hwnd != 0);
  467. RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
  468. ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
  469. ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  470. }
  471. static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
  472. {
  473. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  474. IM_ASSERT(data->Hwnd != 0);
  475. RECT rect;
  476. ::GetClientRect(data->Hwnd, &rect);
  477. return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
  478. }
  479. static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
  480. {
  481. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  482. IM_ASSERT(data->Hwnd != 0);
  483. RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
  484. ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen
  485. ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  486. }
  487. static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport)
  488. {
  489. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  490. IM_ASSERT(data->Hwnd != 0);
  491. ::BringWindowToTop(data->Hwnd);
  492. ::SetForegroundWindow(data->Hwnd);
  493. ::SetFocus(data->Hwnd);
  494. }
  495. static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport)
  496. {
  497. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  498. IM_ASSERT(data->Hwnd != 0);
  499. return ::GetActiveWindow() == data->Hwnd;
  500. }
  501. static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport)
  502. {
  503. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  504. IM_ASSERT(data->Hwnd != 0);
  505. return ::IsIconic(data->Hwnd) != 0;
  506. }
  507. static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
  508. {
  509. // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string.
  510. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  511. IM_ASSERT(data->Hwnd != 0);
  512. int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
  513. ImVector<wchar_t> title_w;
  514. title_w.resize(n);
  515. ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n);
  516. ::SetWindowTextW(data->Hwnd, title_w.Data);
  517. }
  518. static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
  519. {
  520. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  521. IM_ASSERT(data->Hwnd != 0);
  522. IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f);
  523. if (alpha < 1.0f)
  524. {
  525. DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED;
  526. ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style);
  527. ::SetLayeredWindowAttributes(data->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA);
  528. }
  529. else
  530. {
  531. DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED;
  532. ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style);
  533. }
  534. }
  535. static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport)
  536. {
  537. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData;
  538. if (data && data->Hwnd)
  539. return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd);
  540. // The first frame a viewport is created we don't have a window yet
  541. return ImGui_ImplWin32_GetDpiScaleForRect(
  542. (int)(viewport->Pos.x), (int)(viewport->Pos.y),
  543. (int)(viewport->Pos.x + viewport->Size.x), (int)(viewport->Pos.y + viewport->Size.y));
  544. }
  545. // FIXME-DPI: Testing DPI related ideas
  546. static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport)
  547. {
  548. (void)viewport;
  549. #if 0
  550. ImGuiStyle default_style;
  551. //default_style.WindowPadding = ImVec2(0, 0);
  552. //default_style.WindowBorderSize = 0.0f;
  553. //default_style.ItemSpacing.y = 3.0f;
  554. //default_style.FramePadding = ImVec2(0, 0);
  555. default_style.ScaleAllSizes(viewport->DpiScale);
  556. ImGuiStyle& style = ImGui::GetStyle();
  557. style = default_style;
  558. #endif
  559. }
  560. static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  561. {
  562. if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
  563. return true;
  564. if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
  565. {
  566. switch (msg)
  567. {
  568. case WM_CLOSE:
  569. viewport->PlatformRequestClose = true;
  570. return 0;
  571. case WM_MOVE:
  572. viewport->PlatformRequestMove = true;
  573. break;
  574. case WM_SIZE:
  575. viewport->PlatformRequestResize = true;
  576. break;
  577. case WM_NCHITTEST:
  578. // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL).
  579. // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
  580. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
  581. // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
  582. if (viewport->Flags & ImGuiViewportFlags_NoInputs)
  583. return HTTRANSPARENT;
  584. break;
  585. }
  586. }
  587. return DefWindowProc(hWnd, msg, wParam, lParam);
  588. }
  589. static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM)
  590. {
  591. MONITORINFO info = { 0 };
  592. info.cbSize = sizeof(MONITORINFO);
  593. if (!::GetMonitorInfo(monitor, &info))
  594. return TRUE;
  595. ImGuiPlatformMonitor imgui_monitor;
  596. imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top);
  597. imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top));
  598. imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top);
  599. imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top));
  600. imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
  601. ImGuiPlatformIO& io = ImGui::GetPlatformIO();
  602. if (info.dwFlags & MONITORINFOF_PRIMARY)
  603. io.Monitors.push_front(imgui_monitor);
  604. else
  605. io.Monitors.push_back(imgui_monitor);
  606. return TRUE;
  607. }
  608. static void ImGui_ImplWin32_UpdateMonitors()
  609. {
  610. ImGui::GetPlatformIO().Monitors.resize(0);
  611. ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL);
  612. g_WantUpdateMonitors = false;
  613. }
  614. static void ImGui_ImplWin32_InitPlatformInterface()
  615. {
  616. WNDCLASSEX wcex;
  617. wcex.cbSize = sizeof(WNDCLASSEX);
  618. wcex.style = CS_HREDRAW | CS_VREDRAW;
  619. wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
  620. wcex.cbClsExtra = 0;
  621. wcex.cbWndExtra = 0;
  622. wcex.hInstance = ::GetModuleHandle(NULL);
  623. wcex.hIcon = NULL;
  624. wcex.hCursor = NULL;
  625. wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
  626. wcex.lpszMenuName = NULL;
  627. wcex.lpszClassName = _T("ImGui Platform");
  628. wcex.hIconSm = NULL;
  629. ::RegisterClassEx(&wcex);
  630. ImGui_ImplWin32_UpdateMonitors();
  631. // Register platform interface (will be coupled with a renderer interface)
  632. ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
  633. platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow;
  634. platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow;
  635. platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow;
  636. platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos;
  637. platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos;
  638. platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize;
  639. platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize;
  640. platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus;
  641. platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus;
  642. platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized;
  643. platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
  644. platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha;
  645. platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale;
  646. platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI
  647. #if HAS_WIN32_IME
  648. platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos;
  649. #endif
  650. // Register main window handle (which is owned by the main application, not by us)
  651. ImGuiViewport* main_viewport = ImGui::GetMainViewport();
  652. ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)();
  653. data->Hwnd = g_hWnd;
  654. data->HwndOwned = false;
  655. main_viewport->PlatformUserData = data;
  656. main_viewport->PlatformHandle = (void*)g_hWnd;
  657. }
  658. static void ImGui_ImplWin32_ShutdownPlatformInterface()
  659. {
  660. ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL));
  661. }