Reduce the likelyhood that the mouse will hover over the taskbar or toast notification while in relative mode, which causes a mouse leave event.

This will still happen occasionally as the mouse is whipped around, if there is a window overlapping the game window, but it should happen less often now. This could even happen with the original code that warped the mouse every frame, so this should be a good compromise where we don't warp the mouse continously and we still keep the mouse in the safe area of the game window.

Note that notifications can be any size, so the safe area may need to be adjusted or even dynamically defined via a hint.
This commit is contained in:
Sam Lantinga 2021-09-24 10:49:44 -07:00
parent ce66051b0a
commit db68af8032
3 changed files with 135 additions and 30 deletions

View File

@ -248,6 +248,24 @@ WIN_IsEqualIID(REFIID a, REFIID b)
return (SDL_memcmp(a, b, sizeof (*a)) == 0);
}
void
WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect)
{
sdlrect->x = winrect->left;
sdlrect->w = (winrect->right - winrect->left) + 1;
sdlrect->y = winrect->top;
sdlrect->h = (winrect->bottom - winrect->top) + 1;
}
void
WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect)
{
winrect->left = sdlrect->x;
winrect->right = sdlrect->x + sdlrect->w - 1;
winrect->top = sdlrect->y;
winrect->bottom = sdlrect->y + sdlrect->h - 1;
}
#endif /* __WIN32__ || __WINRT__ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -37,6 +37,8 @@
#include <windows.h>
#include <basetyps.h> /* for REFIID with broken mingw.org headers */
#include "SDL_rect.h"
/* Routines to convert from UTF8 to native Windows text */
#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
#define WIN_UTF8ToStringW(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
@ -81,6 +83,10 @@ extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
extern BOOL WIN_IsEqualGUID(const GUID * a, const GUID * b);
extern BOOL WIN_IsEqualIID(REFIID a, REFIID b);
/* Convert between SDL_rect and RECT */
extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect);
extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
#endif /* _INCLUDED_WINDOWS_H */
/* vi: set ts=4 sw=4 expandtab: */

View File

@ -446,6 +446,79 @@ static SDL_MOUSE_EVENT_SOURCE GetMouseMessageSource()
return SDL_MOUSE_EVENT_SOURCE_MOUSE;
}
static void
GetDisplayBoundsForPoint(int x, int y, RECT *bounds)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
int i, dist;
int closest = -1;
int closest_dist = 0x7FFFFFFF;
SDL_Point point;
SDL_Point delta;
SDL_Rect rect;
point.x = x;
point.y = y;
for (i = 0; i < _this->num_displays; ++i) {
SDL_GetDisplayBounds(i, &rect);
if (SDL_EnclosePoints(&point, 1, &rect, NULL)) {
WIN_RectToRECT(&rect, bounds);
return;
}
delta.x = point.x - (rect.x + rect.w / 2);
delta.y = point.y - (rect.y + rect.h / 2);
dist = (delta.x*delta.x + delta.y*delta.y);
if (dist < closest_dist) {
closest = i;
closest_dist = dist;
WIN_RectToRECT(&rect, bounds);
}
}
if (closest < 0) {
bounds->left = 0;
bounds->right = GetSystemMetrics(SM_CXSCREEN) - 1;
bounds->top = 0;
bounds->bottom = GetSystemMetrics(SM_CYSCREEN) - 1;
}
}
static void
WarpWithinBoundsRect(int x, int y, RECT *bounds)
{
if (x < bounds->left || x > bounds->right || y < bounds->top || y > bounds->bottom) {
const int MIN_BOUNDS_SIZE = 32;
int boundsWidth = (bounds->right - bounds->left) + 1;
int boundsHeight = (bounds->bottom - bounds->top) + 1;
if (boundsWidth >= MIN_BOUNDS_SIZE && boundsHeight >= MIN_BOUNDS_SIZE) {
/* Warp back to the opposite side, assuming more motion in the current direction */
int targetLeft = bounds->right - (boundsWidth * 3) / 4;
int targetRight = bounds->left + (boundsWidth * 3) / 4;
int targetTop = bounds->bottom - (boundsHeight * 3) / 4;
int targetBottom = bounds->top + (boundsHeight * 3) / 4;
int warpX;
int warpY;
if (x < bounds->left) {
warpX = targetRight;
} else if (x > bounds->right) {
warpX = targetLeft;
} else {
warpX = SDL_clamp(x, targetLeft, targetRight);
}
if (y < bounds->top) {
warpY = targetBottom;
} else if (y > bounds->bottom) {
warpY = targetTop;
} else {
warpY = SDL_clamp(y, targetTop, targetBottom);
}
SetCursorPos(warpX, warpY);
}
}
}
static SDL_WindowData *
WIN_GetWindowDataFromHWND(HWND hwnd)
{
@ -742,10 +815,42 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
}
mouseID = (SDL_MouseID)(uintptr_t)inp.header.hDevice;
if (isRelative) {
/* FIXME: Add a hint to control this? */
const int SAFE_AREA_X = 64;
const int SAFE_AREA_Y = 256;
RAWMOUSE* rawmouse = &inp.data.mouse;
if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
POINT pt;
SDL_SendMouseMotion(data->window, mouseID, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
/* Make sure that the mouse doesn't hover over notifications and so forth */
if (GetCursorPos(&pt)) {
int x = pt.x;
int y = pt.y;
RECT screenRect;
RECT hwndRect;
RECT boundsRect;
/* Calculate screen rect */
GetDisplayBoundsForPoint(x, y, &screenRect);
/* Calculate client rect */
GetClientRect(hwnd, &hwndRect);
ClientToScreen(hwnd, (LPPOINT) & hwndRect);
ClientToScreen(hwnd, (LPPOINT) & hwndRect + 1);
/* Calculate bounds rect */
IntersectRect(&boundsRect, &screenRect, &hwndRect);
InflateRect(&boundsRect, -SAFE_AREA_X, -SAFE_AREA_Y);
if (!data->in_title_click && !data->focus_click_pending) {
WarpWithinBoundsRect(x, y, &boundsRect);
}
}
} else if (rawmouse->lLastX || rawmouse->lLastY) {
/* This is absolute motion, either using a tablet or mouse over RDP
@ -781,10 +886,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
int boundsWidth, boundsHeight;
/* Calculate screen rect */
screenRect.left = 0;
screenRect.right = w;
screenRect.top = 0;
screenRect.bottom = h;
GetDisplayBoundsForPoint(x, y, &screenRect);
/* Calculate client rect */
GetClientRect(hwnd, &hwndRect);
@ -793,9 +895,9 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
/* Calculate bounds rect */
IntersectRect(&boundsRect, &screenRect, &hwndRect);
InflateRect(&boundsRect, -32, -32);
boundsWidth = (boundsRect.right - boundsRect.left);
boundsHeight = (boundsRect.bottom - boundsRect.top);
InflateRect(&boundsRect, -SAFE_AREA_X, -SAFE_AREA_Y);
boundsWidth = (boundsRect.right - boundsRect.left) + 1;
boundsHeight = (boundsRect.bottom - boundsRect.top) + 1;
if ((boundsWidth > 0 && SDL_abs(relX) > (boundsWidth / 2)) ||
(boundsHeight > 0 && SDL_abs(relY) > (boundsHeight / 2))) {
@ -803,29 +905,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} else {
SDL_SendMouseMotion(data->window, mouseID, 1, relX, relY);
if (!data->in_title_click && !data->focus_click_pending &&
(x < boundsRect.left || x > boundsRect.right ||
y < boundsRect.top || y > boundsRect.bottom)) {
/* Warp back to the opposite side, assuming more motion in the current direction */
int warpX;
int warpY;
if (x < boundsRect.left) {
warpX = boundsRect.right;
} else if (x > boundsRect.right) {
warpX = boundsRect.left;
} else {
warpX = x;
}
if (y < boundsRect.top) {
warpY = boundsRect.bottom;
} else if (y > boundsRect.bottom) {
warpY = boundsRect.top;
} else {
warpY = y;
}
SetCursorPos(warpX, warpY);
if (!data->in_title_click && !data->focus_click_pending) {
WarpWithinBoundsRect(x, y, &boundsRect);
}
}
} else {