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.