// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. #include "imgui.h" #include "imgui_impl_win32.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2019-01-15: Inputs: Using GetForegroundWindow() instead of GetActiveWindow() to be compatible with windows created in a different thread. // 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. // Win32 Data static HWND g_hWnd = 0; static INT64 g_Time = 0; static INT64 g_TicksPerSecond = 0; static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; static bool g_HasGamepad = false; static bool g_WantUpdateHasGamepad = true; // Functions bool ImGui_ImplWin32_Init(void* hwnd) { if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) return false; if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) return false; // Setup back-end capabilities flags g_hWnd = (HWND)hwnd; ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendPlatformName = "imgui_impl_win32"; io.ImeWindowHandle = hwnd; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. io.KeyMap[ImGuiKey_Tab] = VK_TAB; io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = VK_UP; io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; io.KeyMap[ImGuiKey_Home] = VK_HOME; io.KeyMap[ImGuiKey_End] = VK_END; io.KeyMap[ImGuiKey_Insert] = VK_INSERT; io.KeyMap[ImGuiKey_Delete] = VK_DELETE; io.KeyMap[ImGuiKey_Backspace] = VK_BACK; io.KeyMap[ImGuiKey_Space] = VK_SPACE; io.KeyMap[ImGuiKey_Enter] = VK_RETURN; io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; io.KeyMap[ImGuiKey_A] = 'A'; io.KeyMap[ImGuiKey_C] = 'C'; io.KeyMap[ImGuiKey_V] = 'V'; io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; return true; } void ImGui_ImplWin32_Shutdown() { g_hWnd = (HWND)0; } static bool ImGui_ImplWin32_UpdateMouseCursor() { ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return false; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor ::SetCursor(NULL); } else { // Show OS mouse cursor LPTSTR win32_cursor = IDC_ARROW; switch (imgui_cursor) { case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; } ::SetCursor(::LoadCursor(NULL, win32_cursor)); } return true; } static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; ::ClientToScreen(g_hWnd, &pos); ::SetCursorPos(pos.x, pos.y); } // Set mouse position io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); POINT pos; if (::GetForegroundWindow() == g_hWnd && ::GetCursorPos(&pos)) if (::ScreenToClient(g_hWnd, &pos)) io.MousePos = ImVec2((float)pos.x, (float)pos.y); } #ifdef _MSC_VER #pragma comment(lib, "xinput") #endif // Gamepad navigation mapping void ImGui_ImplWin32_UpdateGameControllers() { ImGuiIO& io = ImGui::GetIO(); memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. if (g_WantUpdateHasGamepad) { XINPUT_CAPABILITIES caps; g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); g_WantUpdateHasGamepad = false; } XINPUT_STATE xinput_state; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) { const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } #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; } MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); #undef MAP_BUTTON #undef MAP_ANALOG } } void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); 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()."); // Setup display size (every frame to accommodate for window resizing) RECT rect; ::GetClientRect(g_hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step INT64 current_time; ::QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; g_Time = current_time; // Read keyboard modifiers inputs io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; io.KeySuper = false; // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. // Update OS mouse position ImGui_ImplWin32_UpdateMousePos(); // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); if (g_LastMouseCursor != mouse_cursor) { g_LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } // Update game controllers (if available) ImGui_ImplWin32_UpdateGameControllers(); } // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif #ifndef DBT_DEVNODES_CHANGED #define DBT_DEVNODES_CHANGED 0x0007 #endif // Process Win32 mouse/keyboard inputs. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // 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. // 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. IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui::GetCurrentContext() == NULL) return 0; ImGuiIO& io = ImGui::GetIO(); switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: { int button = 0; if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) ::SetCapture(hwnd); io.MouseDown[button] = true; return 0; } case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { int button = 0; if (msg == WM_LBUTTONUP) button = 0; if (msg == WM_RBUTTONUP) button = 1; if (msg == WM_MBUTTONUP) button = 2; io.MouseDown[button] = false; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) ::ReleaseCapture(); return 0; } case WM_MOUSEWHEEL: io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return 0; case WM_MOUSEHWHEEL: io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (wParam < 256) io.KeysDown[wParam] = 1; return 0; case WM_KEYUP: case WM_SYSKEYUP: if (wParam < 256) io.KeysDown[wParam] = 0; return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. if (wParam > 0 && wParam < 0x10000) io.AddInputCharacter((unsigned short)wParam); return 0; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) return 1; return 0; case WM_DEVICECHANGE: if ((UINT)wParam == DBT_DEVNODES_CHANGED) g_WantUpdateHasGamepad = true; return 0; } return 0; }