diff --git a/Source/Engine/Engine/Screen.cpp b/Source/Engine/Engine/Screen.cpp
index 24b154573..2a658dd37 100644
--- a/Source/Engine/Engine/Screen.cpp
+++ b/Source/Engine/Engine/Screen.cpp
@@ -106,25 +106,11 @@ void Screen::SetCursorVisible(const bool value)
#else
const auto win = Engine::MainWindow;
#endif
- bool focused = false;
if (win && Engine::HasGameViewportFocus())
- {
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
- focused = true;
- }
else if (win)
win->SetCursor(CursorType::Default);
CursorVisible = value;
-
- // Just enable relative mode when cursor is constrained and not visible
- if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
- {
- Input::Mouse->SetRelativeMode(true, win);
- }
- else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
- {
- Input::Mouse->SetRelativeMode(false, win);
- }
}
CursorLockMode Screen::GetCursorLock()
@@ -136,34 +122,35 @@ void Screen::SetCursorLock(CursorLockMode mode)
{
#if USE_EDITOR
const auto win = Editor::Managed->GetGameWindow(true);
+ Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
#else
const auto win = Engine::MainWindow;
+ Rectangle bounds = win != nullptr ? win->GetClientBounds() : Rectangle();
#endif
+ bool inRelativeMode = Input::Mouse->IsRelative();
if (win && mode == CursorLockMode::Clipped)
- {
-#if USE_EDITOR
- Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
-#else
- Rectangle bounds = win->GetClientBounds();
-#endif
win->StartClippingCursor(bounds);
- }
- else if (win && CursorLock == CursorLockMode::Clipped)
+ else if (win && mode == CursorLockMode::Locked)
{
- win->EndClippingCursor();
+ // Use mouse clip region to restrict the cursor in one spot
+ Rectangle centerBounds;
+ auto mousePosition = win->GetMousePosition();
+ if (bounds.Contains(mousePosition))
+ centerBounds = Rectangle(mousePosition, Float2(1, 1));
+ else
+ centerBounds = Rectangle(bounds.GetCenter(), Float2(1, 1));
+ win->StartClippingCursor(centerBounds);
}
+ else if (win && (CursorLock == CursorLockMode::Locked || CursorLock == CursorLockMode::Clipped))
+ win->EndClippingCursor();
CursorLock = mode;
- // Just enable relative mode when cursor is constrained and not visible
+ // Enable relative mode when cursor is restricted
bool focused = win && Engine::HasGameViewportFocus();
- if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
- {
+ if (CursorLock != CursorLockMode::None)
Input::Mouse->SetRelativeMode(true, win);
- }
- else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
- {
+ else if (CursorLock == CursorLockMode::None && inRelativeMode)
Input::Mouse->SetRelativeMode(false, win);
- }
}
GameWindowMode Screen::GetGameWindowMode()
diff --git a/Source/Engine/Platform/SDL/SDLInput.cpp b/Source/Engine/Platform/SDL/SDLInput.cpp
index 67fa71804..ff3f362a1 100644
--- a/Source/Engine/Platform/SDL/SDLInput.cpp
+++ b/Source/Engine/Platform/SDL/SDLInput.cpp
@@ -357,9 +357,9 @@ public:
class SDLMouse : public Mouse
{
private:
- Float2 oldPosition = Float2::Zero;
- Window* relativeModeWindow = nullptr;
- const SDL_Rect* oldScreenRect = nullptr;
+ Float2 _oldPosition = Float2::Zero;
+ Window* _relativeModeWindow = nullptr;
+ const SDL_Rect* _oldScreenRect = nullptr;
public:
///
@@ -377,8 +377,8 @@ public:
///
Float2 GetOldMousePosition() const
{
- ASSERT(relativeModeWindow != nullptr);
- return relativeModeWindow->ClientToScreen(oldPosition);
+ ASSERT(_relativeModeWindow != nullptr);
+ return _relativeModeWindow->ClientToScreen(_oldPosition);
}
// [Mouse]
@@ -405,27 +405,27 @@ public:
auto windowHandle = static_cast(window)->_window;
if (relativeMode)
{
- relativeModeWindow = window;
- SDL_GetMouseState(&oldPosition.X, &oldPosition.Y);
+ _relativeModeWindow = window;
+ SDL_GetMouseState(&_oldPosition.X, &_oldPosition.Y);
if (!SDL_CursorVisible())
{
// Trap the cursor in current location
- SDL_Rect clipRect = { (int)oldPosition.X, (int)oldPosition.Y, 1, 1 };
- oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
+ SDL_Rect clipRect = { (int)_oldPosition.X, (int)_oldPosition.Y, 1, 1 };
+ _oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
SDL_SetWindowMouseRect(windowHandle, &clipRect);
}
}
else
{
- if (relativeModeWindow != window)
+ if (_relativeModeWindow != window)
{
// FIXME: When floating game window is focused and editor viewport activated, the relative mode gets stuck
return;
}
SDL_SetWindowMouseRect(windowHandle, nullptr);//oldScreenRect);
- SDL_WarpMouseInWindow(windowHandle, oldPosition.X, oldPosition.Y);
- oldScreenRect = nullptr;
- relativeModeWindow = nullptr;
+ SDL_WarpMouseInWindow(windowHandle, _oldPosition.X, _oldPosition.Y);
+ _oldScreenRect = nullptr;
+ _relativeModeWindow = nullptr;
}
Mouse::SetRelativeMode(relativeMode, window);
diff --git a/Source/Engine/Platform/SDL/SDLPlatform.cpp b/Source/Engine/Platform/SDL/SDLPlatform.cpp
index 84861752d..7e816992d 100644
--- a/Source/Engine/Platform/SDL/SDLPlatform.cpp
+++ b/Source/Engine/Platform/SDL/SDLPlatform.cpp
@@ -70,7 +70,7 @@ bool SDLPlatform::Init()
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "0");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // Needed for tracking mode
- SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0"); //
+ SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0"); // Relative mode can be active when cursor is shown and clipped
SDL_SetHint(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, "8"); // Reduce the default mouse double-click radius
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input
@@ -118,6 +118,7 @@ bool SDLPlatform::Init()
}
SDLInput::Init();
+ SDLWindow::Init();
SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp
index 15d3fa418..8624e69ca 100644
--- a/Source/Engine/Platform/SDL/SDLWindow.cpp
+++ b/Source/Engine/Platform/SDL/SDLWindow.cpp
@@ -73,6 +73,10 @@ void* GetNativeWindowPointer(SDL_Window* window)
return windowPtr;
}
+void SDLWindow::Init()
+{
+}
+
SDLWindow::SDLWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
, _handle(nullptr)
@@ -420,26 +424,29 @@ void SDLWindow::HandleEvent(SDL_Event& event)
case SDL_EVENT_WINDOW_FOCUS_GAINED:
{
OnGotFocus();
- if (IsPopupWindow(_settings.Type))
- _window = _window;
if (_settings.AllowInput && !SDLPlatform::UsesX11())
SDL_StartTextInput(_window);
- const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
- if (_isClippingCursor && currentClippingRect == nullptr)
+ if (_isClippingCursor)
{
+ // The relative mode needs to be disabled for clipping to take effect
+ bool inRelativeMode = Input::Mouse->IsRelative(this) || _restoreRelativeMode;
+ if (inRelativeMode)
+ Input::Mouse->SetRelativeMode(false, this);
+
+ // Restore previous clipping region
SDL_Rect rect{ (int)_clipCursorRect.GetX(), (int)_clipCursorRect.GetY(), (int)_clipCursorRect.GetWidth(), (int)_clipCursorRect.GetHeight() };
SDL_SetWindowMouseRect(_window, &rect);
+
+ if (inRelativeMode)
+ Input::Mouse->SetRelativeMode(true, this);
}
return;
}
case SDL_EVENT_WINDOW_FOCUS_LOST:
{
- if (IsPopupWindow(_settings.Type))
- _window = _window;
if (_settings.AllowInput && !SDLPlatform::UsesX11())
SDL_StopTextInput(_window);
- const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
- if (currentClippingRect != nullptr)
+ if (_isClippingCursor)
SDL_SetWindowMouseRect(_window, nullptr);
OnLostFocus();
return;
@@ -802,9 +809,23 @@ void SDLWindow::StartClippingCursor(const Rectangle& bounds)
if (!IsFocused())
return;
+#if PLATFORM_LINUX
+ {
+ auto oldValue = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION);
+ SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "1");
+
+ // The cursor is not fully constrained when positioned outside of the clip region...
+ Float2 center = bounds.GetCenter();
+ SDL_WarpMouseInWindow(_window, center.X, center.Y);
+
+ SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, oldValue);
+ }
+#endif
+
_isClippingCursor = true;
SDL_Rect rect{ (int)bounds.GetX(), (int)bounds.GetY(), (int)bounds.GetWidth(), (int)bounds.GetHeight() };
SDL_SetWindowMouseRect(_window, &rect);
+ _clipCursorRect = bounds;
}
void SDLWindow::EndClippingCursor()
diff --git a/Source/Engine/Platform/SDL/SDLWindow.h b/Source/Engine/Platform/SDL/SDLWindow.h
index 331f99266..d9bfd4de3 100644
--- a/Source/Engine/Platform/SDL/SDLWindow.h
+++ b/Source/Engine/Platform/SDL/SDLWindow.h
@@ -40,6 +40,9 @@ private:
Rectangle _clipCursorRect;
Rectangle _cachedClientRectangle;
+public:
+ static void Init();
+
public:
///
/// Initializes a new instance of the class.