This code below, will transform the cursor positon when window resolution doesn’t match platform resolution. However, PrimaryDisplayWidth and PrimaryDisplayHeight in FDisplayMetrics is always set to width and height of the primary monitor in OS, which means that when the game window is in another monitor which has different width and height from the primary monitor, the cursor position will always be wrong. Although the logic that led to this issue is not complicated, it makes impossible to create a game supporting for exclusive fullscreen and multi-monitor.
So, what is Epic Games’ plan for this issue? I hope there will be more support for multi-monitor after this issue is solved.
Thanks.
FPointerEvent FSlateApplication::TransformPointerEvent(const FPointerEvent& PointerEvent, const TSharedPtr<SWindow>& Window) const
{
FPointerEvent TransformedPointerEvent = PointerEvent;
if (Window)
{
if (TransformFullscreenMouseInput && !GIsEditor && Window->GetWindowMode() == EWindowMode::Fullscreen)
{
// Screen space mapping scales everything. When window resolution doesn't match platform resolution,
// this causes offset cursor hit-tests in fullscreen. Correct in slate since we are first window-aware slate processor.
FVector2f WindowSize = Window->GetSizeInScreen();
FVector2f DisplaySize = { (float)CachedDisplayMetrics.PrimaryDisplayWidth, (float)CachedDisplayMetrics.PrimaryDisplayHeight };
TransformedPointerEvent = FPointerEvent(PointerEvent, PointerEvent.GetScreenSpacePosition() * WindowSize / DisplaySize, PointerEvent.GetLastScreenSpacePosition() * WindowSize / DisplaySize);
}
}
return TransformedPointerEvent;
}
We still don’t have a fix for this issue, though I did a bit of investigating and we should be able to swap out CachedDisplayMetrics and use the size from the window’s FullScreenInfo instead:
FVector2f DisplaySize = Window->GetFullScreenInfo().GetSize2f();There are three places where this change will need to be made (Two in SlateApplication, one in SlateUser) but I’ll see about getting that checked in. We’ll also need to verify that there aren’t any performance implications to accessing the full screen info from the window instead of the cached display metrics, since this change does necessitate a few Win32 API calls (MonitorFromWindow, GetMonitorInfo).
I wanted to follow up on this thread as I’m running into the same issue. I’m also curious since it looks like Fortnite and Fall Guys don’t have the same issue with the incorrect pointer event transform. Do you have any more concrete changes (or a git patch to look at) for fixing this?
At the risk of muddying the conversation, I’m also curious if there is a suggested way of gathering/applying resolution settings that accounts for multiple monitors of potentially different resolution? It seems like Lyra doesn’t account for this (pointer event issue aside), so you only get valid resolution options for the primary display.
yea this secondary monitor cursor offset is UE-206910. the issue is TransformPointerEvent uses PrimaryDisplayWidth/Height from CachedDisplayMetrics even when window is on secondary with different native res, so scaling is wrong.
workaround until you pull the fix: dont use raw cursor pos for UI hit tests on secondary — use a DPI corrected position. GetMonitorDPIScale(index) from NativeWidth/displayRect ratio and GetDPICorrectedCursorPosition fixes it by detecting active monitor scale and returning native pixels. Fortnite/Fall Guys use window FullScreenInfo path instead so they dont hit it.
theres a BP plugin that exposes GetDPICorrectedCursorPosition / GetMonitorDPIScale (multi-monitor display controller hashem store) if you want it BP-ready, or in C++ you can replicate the engine fix with Window->GetFullScreenInfo().GetSize2f() as Cody mentioned — replace the 3 CachedDisplayMetrics uses in SlateApplication/SlateUser.