Implement relative mouse mode (raw input) for SDL platform

This commit is contained in:
2024-07-25 21:29:38 +03:00
parent 9cb3dcf1f5
commit 1bbfdc97f2
18 changed files with 276 additions and 23 deletions

View File

@@ -257,6 +257,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition)
INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&mousePosition);
}
void WindowBase::OnMouseMoveRelative(const Float2& mousePositionRelative)
{
PROFILE_CPU_NAMED("GUI.OnMouseMoveRelative");
MouseMoveRelative(mousePositionRelative);
INVOKE_EVENT_PARAMS_1(OnMouseMoveRelative, (void*)&mousePositionRelative);
}
void WindowBase::OnMouseLeave()
{
PROFILE_CPU_NAMED("GUI.OnMouseLeave");

View File

@@ -592,6 +592,12 @@ public:
MouseDelegate MouseMove;
void OnMouseMove(const Float2& mousePosition);
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
MouseDelegate MouseMoveRelative;
void OnMouseMoveRelative(const Float2& mousePositionRelative);
/// <summary>
/// Event fired when mouse leaves window.
/// </summary>

View File

@@ -617,7 +617,7 @@ void LinuxWindow::OnButtonPress(void* event)
}
// Handle double-click
if (buttonEvent->button == Button1)
if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative())
{
if (
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&

View File

@@ -507,7 +507,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
Float2 mousePos = GetMousePosition(Window, event);
mousePos = Window->ClientToScreen(mousePos);
MouseButton mouseButton = MouseButton::Left;
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
else
Input::Mouse->OnMouseDown(mousePos, mouseButton, Window);
@@ -544,7 +544,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (IsWindowInvalid(Window)) return;
Float2 mousePos = GetMousePosition(Window, event);
MouseButton mouseButton = MouseButton::Right;
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
@@ -582,7 +582,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
default:
return;
}
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);

View File

@@ -474,8 +474,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
{
case SDL_EVENT_MOUSE_MOTION:
{
const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y });
Input::Mouse->OnMouseMove(mousePos, window);
if (Input::Mouse->IsRelative())
{
const Float2 mouseDelta(event.motion.xrel, event.motion.yrel);
Input::Mouse->OnMouseMoveRelative(mouseDelta, window);
}
else
{
const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y });
Input::Mouse->OnMouseMove(mousePos, window);
}
return true;
}
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
@@ -486,7 +494,7 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
{
const Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y });
Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y });
MouseButton button = MouseButton::None;
if (event.button.button == SDL_BUTTON_LEFT)
button = MouseButton::Left;
@@ -499,9 +507,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
else if (event.button.button == SDL_BUTTON_X2)
button = MouseButton::Extended2;
if (Input::Mouse->IsRelative())
{
// Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition();
}
if (event.button.state == SDL_RELEASED)
Input::Mouse->OnMouseUp(mousePos, button, window);
// Prevent sending mouse down event when double-clicking
// Prevent sending multiple mouse down event when double-clicking UI elements
else if (event.button.clicks % 2 == 1)
Input::Mouse->OnMouseDown(mousePos, button, window);
else
@@ -511,9 +526,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
}
case SDL_EVENT_MOUSE_WHEEL:
{
const Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y });
Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y });
const float delta = event.wheel.y;
if (Input::Mouse->IsRelative())
{
// Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition();
}
Input::Mouse->OnMouseWheel(mousePos, delta, window);
return true;
}

View File

@@ -957,6 +957,10 @@ void SDLWindow::StartTrackingMouse(bool useMouseScreenOffset)
{
if (SDL_CaptureMouse(SDL_TRUE) != 0)
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
// For viewport camera mouse tracking we want to use relative mode for best precision
if (_cursor == CursorType::Hidden)
Input::Mouse->SetRelativeMode(true);
}
}
@@ -973,6 +977,7 @@ void SDLWindow::EndTrackingMouse()
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
//SDL_SetWindowGrab(_window, SDL_FALSE);
Input::Mouse->SetRelativeMode(false);
}
void SDLWindow::StartClippingCursor(const Rectangle& bounds)
@@ -1038,9 +1043,14 @@ void SDLWindow::UpdateCursor() const
if (_cursor == CursorType::Hidden)
{
SDL_HideCursor();
if (_isTrackingMouse)
Input::Mouse->SetRelativeMode(true);
return;
}
SDL_ShowCursor();
//if (_isTrackingMouse)
// Input::Mouse->SetRelativeMode(false);
int32 index = SDL_SYSTEM_CURSOR_DEFAULT;
switch (_cursor)

View File

@@ -17,31 +17,37 @@ namespace FlaxEngine
/// <summary>
/// Perform window hit test delegate.
/// </summary>
/// <param name="mouse">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
/// <param name="mousePosition">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
/// <returns>Hit result.</returns>
public delegate WindowHitCodes HitTestDelegate(ref Float2 mouse);
public delegate WindowHitCodes HitTestDelegate(ref Float2 mousePosition);
/// <summary>
/// Perform mouse buttons action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
/// <param name="mousePosition">The mouse position.</param>
/// <param name="button">The mouse buttons state.</param>
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
public delegate void MouseButtonDelegate(ref Float2 mouse, MouseButton button, ref bool handled);
public delegate void MouseButtonDelegate(ref Float2 mousePosition, MouseButton button, ref bool handled);
/// <summary>
/// Perform mouse move action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
public delegate void MouseMoveDelegate(ref Float2 mouse);
/// <param name="mousePosition">The mouse position.</param>
public delegate void MouseMoveDelegate(ref Float2 mousePosition);
/// <summary>
/// Perform mouse move action in relative mode.
/// </summary>
/// <param name="mouseMotion">The relative mouse motion.</param>
public delegate void MouseMoveRelativeDelegate(ref Float2 mouseMotion);
/// <summary>
/// Perform mouse wheel action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
/// <param name="mousePosition">The mouse position.</param>
/// <param name="delta">The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range).</param>
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
public delegate void MouseWheelDelegate(ref Float2 mouse, float delta, ref bool handled);
public delegate void MouseWheelDelegate(ref Float2 mousePosition, float delta, ref bool handled);
/// <summary>
/// Perform touch action.
@@ -99,9 +105,14 @@ namespace FlaxEngine
public event MouseWheelDelegate MouseWheel;
/// <summary>
/// Event fired when mouse moves
/// Event fired when mouse moves.
/// </summary>
public event MouseMoveDelegate MouseMove;
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
public event MouseMoveRelativeDelegate MouseMoveRelative;
/// <summary>
/// Event fired when mouse leaves window.
@@ -273,6 +284,12 @@ namespace FlaxEngine
MouseMove?.Invoke(ref pos);
GUI.OnMouseMove(pos);
}
internal void Internal_OnMouseMoveRelative(ref Float2 mouseMotion)
{
MouseMoveRelative?.Invoke(ref mouseMotion);
GUI.OnMouseMoveRelative(mouseMotion);
}
internal void Internal_OnMouseLeave()
{

View File

@@ -265,19 +265,38 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
}
case WM_LBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Left, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Left, window);
else
OnMouseDown(mousePos, MouseButton::Left, window);
result = true;
break;
}
case WM_RBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Right, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Right, window);
else
OnMouseDown(mousePos, MouseButton::Right, window);
result = true;
break;
}
case WM_MBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Middle, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Middle, window);
else
OnMouseDown(mousePos, MouseButton::Middle, window);
result = true;
break;
}
case WM_XBUTTONDBLCLK:
{
const auto button = (HIWORD(wParam) & XBUTTON1) ? MouseButton::Extended1 : MouseButton::Extended2;
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, button, window);
else
OnMouseDown(mousePos, button, window);
result = true;
break;
}