Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ImGui mouse position when render resolution is different than window resolution #979

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions src/imgui_impl/win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,16 +325,39 @@ static void ImGui_ImplWin32_UpdateKeyModifiers()
io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN));
}

// CET: Scale to make sure coords are correct (fixes issues in fullscreen).
static ImVec2 CET_GetScaleFactor()
{
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(bd->hWnd != 0);

if (io.DisplaySize.x == 0.0f || io.DisplaySize.y == 0.0f)
return {-1.0f, -1.0f};

RECT clientRect;
::GetClientRect(bd->hWnd, &clientRect);
return {io.DisplaySize.x / (clientRect.right - clientRect.left), io.DisplaySize.y / (clientRect.bottom - clientRect.top)};
}

// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
// Because of that, it is a little more complicated than your typical single-viewport binding code!
static void ImGui_ImplWin32_UpdateMouseData(SIZE aOutSize)
static void ImGui_ImplWin32_UpdateMouseData()
{
ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(bd->hWnd != 0);

POINT mouse_screen_pos;
bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0;

// CET: Get and verify scale factor. If any scalar in the scale factor is <= 0, apply -FLT_MAX as mouse position event. Otherwise, process normally.
const auto scale = CET_GetScaleFactor();
if (scale.x <= 0.0f || scale.y <= 0.0f)
{
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
return;
}

HWND focused_window = ::GetForegroundWindow();
const bool is_app_focused =
Expand All @@ -345,7 +368,7 @@ static void ImGui_ImplWin32_UpdateMouseData(SIZE aOutSize)
// When multi-viewports are enabled, all Dear ImGui positions are same as OS positions.
if (io.WantSetMousePos)
{
POINT pos = {(int)io.MousePos.x, (int)io.MousePos.y};
POINT pos = {static_cast<int>(io.MousePos.x / scale.x), static_cast<int>(io.MousePos.y / scale.y)};
if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0)
::ClientToScreen(focused_window, &pos);
::SetCursorPos(pos.x, pos.y);
Expand All @@ -363,17 +386,7 @@ static void ImGui_ImplWin32_UpdateMouseData(SIZE aOutSize)
POINT mouse_pos = mouse_screen_pos;
if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
::ScreenToClient(bd->hWnd, &mouse_pos);

static float xScale = 1.0;
static float yScale = 1.0;
if(aOutSize.cx && aOutSize.cy)
{
RECT clientRect;
::GetClientRect(bd->hWnd, &clientRect);
xScale = static_cast<float>(aOutSize.cx) / (clientRect.right - clientRect.left);
yScale = static_cast<float>(aOutSize.cy) / (clientRect.bottom - clientRect.top);
}
io.AddMousePosEvent((float)mouse_pos.x * xScale, (float)mouse_pos.y * yScale);
io.AddMousePosEvent((float)mouse_pos.x * scale.x, (float)mouse_pos.y * scale.y);
}
}

Expand Down Expand Up @@ -494,8 +507,7 @@ void ImGui_ImplWin32_NewFrame(SIZE aOutSize)
ImGuiIO& io = ImGui::GetIO();

// Setup display size (every frame to accommodate for window resizing)
// RECT rect = {0, 0, 0, 0};
// ::GetClientRect(bd->hWnd, &rect);
// CET: Use size provided by the game to match DLSS and similar.
io.DisplaySize = ImVec2(static_cast<float>(aOutSize.cx), static_cast<float>(aOutSize.cy));
if (bd->WantUpdateMonitors)
ImGui_ImplWin32_UpdateMonitors();
Expand All @@ -507,7 +519,7 @@ void ImGui_ImplWin32_NewFrame(SIZE aOutSize)
bd->Time = current_time;

// Update OS mouse position
ImGui_ImplWin32_UpdateMouseData(aOutSize);
ImGui_ImplWin32_UpdateMouseData();

// Process workarounds for known Windows key handling issues
ImGui_ImplWin32_ProcessKeyEventsWorkarounds();
Expand Down Expand Up @@ -702,6 +714,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
{
// CET: Get and verify scale factor. If any scalar in the scale factor is <= 0, apply -FLT_MAX as mouse position event. Otherwise, process normally.
const auto scale = CET_GetScaleFactor();
if (scale.x <= 0.0f || scale.y <= 0.0f)
{
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
return 0;
}

// We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo();
const int area = (msg == WM_MOUSEMOVE) ? 1 : 2;
Expand All @@ -722,7 +742,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
if (msg == WM_NCMOUSEMOVE && !want_absolute_pos) // WM_NCMOUSEMOVE are absolute coordinates.
::ScreenToClient(hwnd, &mouse_pos);
io.AddMouseSourceEvent(mouse_source);
io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y);
io.AddMousePosEvent((float)mouse_pos.x * scale.x, (float)mouse_pos.y * scale.y);
return 0;
}
case WM_MOUSELEAVE:
Expand Down