From f7dcc10a70b3a1ea021c4a2129c3964a67ed52bb Mon Sep 17 00:00:00 2001 From: Mario Liebisch Date: Thu, 1 May 2014 21:08:57 +0200 Subject: [PATCH] Added ability to grab the cursor (w/ Windows impl.) * When grabbed, the mouse cursor may not be moved outside a window's client frame. * Fullscreen windows always grab the mouse cursor. * The effect is only active while the SFML window is the active foreground window. * Right now this is only implemented for Windows. Signed-off-by: Marco Antognini --- include/SFML/Window/Window.hpp | 15 ++++++ src/SFML/Window/OSX/WindowImplCocoa.hpp | 8 +++ src/SFML/Window/OSX/WindowImplCocoa.mm | 7 +++ .../Window/OSX/WindowImplDelegateProtocol.h | 6 +++ src/SFML/Window/Unix/WindowImplX11.cpp | 34 +++++++++++- src/SFML/Window/Unix/WindowImplX11.hpp | 11 +++- src/SFML/Window/Win32/WindowImplWin32.cpp | 54 +++++++++++++++++-- src/SFML/Window/Win32/WindowImplWin32.hpp | 23 ++++++++ src/SFML/Window/Window.cpp | 8 +++ src/SFML/Window/WindowImpl.hpp | 8 +++ 10 files changed, 166 insertions(+), 8 deletions(-) diff --git a/include/SFML/Window/Window.hpp b/include/SFML/Window/Window.hpp index 41fc0a9a8..2a815e541 100644 --- a/include/SFML/Window/Window.hpp +++ b/include/SFML/Window/Window.hpp @@ -348,6 +348,21 @@ public: //////////////////////////////////////////////////////////// void setMouseCursorVisible(bool visible); + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// If set, grabs the mouse cursor inside this window's client + /// area so it may no longer be moved outside its bounds. + /// Note that grabbing is only active while the window has + /// focus and calling this function for fullscreen windows + /// won't have any effect (fullscreen windows always grab the + /// cursor). + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + void setMouseCursorGrabbed(bool grabbed); + //////////////////////////////////////////////////////////// /// \brief Enable or disable automatic key-repeat /// diff --git a/src/SFML/Window/OSX/WindowImplCocoa.hpp b/src/SFML/Window/OSX/WindowImplCocoa.hpp index 486bf783a..1e88be08e 100644 --- a/src/SFML/Window/OSX/WindowImplCocoa.hpp +++ b/src/SFML/Window/OSX/WindowImplCocoa.hpp @@ -313,6 +313,14 @@ public: //////////////////////////////////////////////////////////// virtual void setMouseCursorVisible(bool visible); + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorGrabbed(bool grabbed); + //////////////////////////////////////////////////////////// /// \brief Enable or disable automatic key-repeat /// diff --git a/src/SFML/Window/OSX/WindowImplCocoa.mm b/src/SFML/Window/OSX/WindowImplCocoa.mm index b7dc45db3..63635885a 100644 --- a/src/SFML/Window/OSX/WindowImplCocoa.mm +++ b/src/SFML/Window/OSX/WindowImplCocoa.mm @@ -486,6 +486,13 @@ void WindowImplCocoa::setMouseCursorVisible(bool visible) } +//////////////////////////////////////////////////////////// +void WindowImplCocoa::setMouseCursorGrabbed(bool grabbed) +{ + +} + + //////////////////////////////////////////////////////////// void WindowImplCocoa::setKeyRepeatEnabled(bool enabled) { diff --git a/src/SFML/Window/OSX/WindowImplDelegateProtocol.h b/src/SFML/Window/OSX/WindowImplDelegateProtocol.h index 0c4595c76..3f769c50d 100644 --- a/src/SFML/Window/OSX/WindowImplDelegateProtocol.h +++ b/src/SFML/Window/OSX/WindowImplDelegateProtocol.h @@ -96,6 +96,12 @@ namespace sf { //////////////////////////////////////////////////////////// -(BOOL)isMouseInside; +//////////////////////////////////////////////////////////// +/// \brief Grab or release the mouse cursor +/// +//////////////////////////////////////////////////////////// +-(void)setCursorGrabbed:(bool)grabbed; + //////////////////////////////////////////////////////////// /// \brief Get window position /// diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 4f4ce64aa..bdd686986 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -366,7 +366,8 @@ m_hiddenCursor (0), m_keyRepeat (true), m_previousSize (-1, -1), m_useSizeHints (false), -m_fullscreen (false) +m_fullscreen (false), +m_cursorGrabbed (false) { // Open a connection with the X server m_display = OpenDisplay(); @@ -420,7 +421,8 @@ m_hiddenCursor (0), m_keyRepeat (true), m_previousSize (-1, -1), m_useSizeHints (false), -m_fullscreen ((style & Style::Fullscreen) != 0) +m_fullscreen ((style & Style::Fullscreen) != 0), +m_cursorGrabbed (m_fullscreen) { // Open a connection with the X server m_display = OpenDisplay(); @@ -952,6 +954,26 @@ void WindowImplX11::setMouseCursorVisible(bool visible) } +//////////////////////////////////////////////////////////// +void WindowImplX11::setMouseCursorGrabbed(bool grabbed) +{ + // This has no effect in fullscreen mode + if (m_fullscreen || (m_cursorGrabbed == grabbed)) + return; + + if (grabbed) + { + // TODO XGrabPointer(m_display, m_window, true, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); + m_cursorGrabbed = true; + } + else + { + // TODO XUngrabPointer(m_display, CurrentTime); + m_cursorGrabbed = false; + } +} + + //////////////////////////////////////////////////////////// void WindowImplX11::setKeyRepeatEnabled(bool enabled) { @@ -1661,6 +1683,10 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) if (m_inputContext) XSetICFocus(m_inputContext); + // Grab cursor + if (m_cursorGrabbed) + ;// TODO XGrabPointer(m_display, m_window, true, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); + Event event; event.type = Event::GainedFocus; pushEvent(event); @@ -1703,6 +1729,10 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) if (m_inputContext) XUnsetICFocus(m_inputContext); + // Release cursor + if (m_cursorGrabbed) + ;// TODO XUngrabPointer(m_display, CurrentTime); + Event event; event.type = Event::LostFocus; pushEvent(event); diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index f84c86c10..548248977 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -147,6 +147,14 @@ public: //////////////////////////////////////////////////////////// virtual void setMouseCursorVisible(bool visible); + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorGrabbed(bool grabbed); + //////////////////////////////////////////////////////////// /// \brief Enable or disable automatic key-repeat /// @@ -318,7 +326,8 @@ private: bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) bool m_useSizeHints; ///< Is the size of the window fixed with size hints? - bool m_fullscreen; ///< Is window in fullscreen? + bool m_fullscreen; ///< Is the window in fullscreen? + bool m_cursorGrabbed; ///< Is the mouse cursor trapped? }; } // namespace priv diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index 8bf86ab81..bc62c96df 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -132,7 +132,9 @@ m_keyRepeatEnabled(true), m_lastSize (0, 0), m_resizing (false), m_surrogate (0), -m_mouseInside (false) +m_mouseInside (false), +m_fullscreen (false), +m_cursorGrabbed (false) { // Set that this process is DPI aware and can handle DPI scaling setProcessDpiAware(); @@ -156,7 +158,9 @@ m_keyRepeatEnabled(true), m_lastSize (mode.width, mode.height), m_resizing (false), m_surrogate (0), -m_mouseInside (false) +m_mouseInside (false), +m_fullscreen (style & Style::Fullscreen), +m_cursorGrabbed (m_fullscreen) { // Set that this process is DPI aware and can handle DPI scaling setProcessDpiAware(); @@ -187,8 +191,7 @@ m_mouseInside (false) } // In windowed mode, adjust width and height so that window will have the requested client area - bool fullscreen = (style & Style::Fullscreen) != 0; - if (!fullscreen) + if (!m_fullscreen) { RECT rectangle = {0, 0, width, height}; AdjustWindowRect(&rectangle, win32Style, false); @@ -204,7 +207,7 @@ m_mouseInside (false) setSize(Vector2u(mode.width, mode.height)); // Switch to fullscreen if requested - if (fullscreen) + if (m_fullscreen) switchToFullscreen(mode); // Increment window count @@ -277,6 +280,9 @@ Vector2i WindowImplWin32::getPosition() const void WindowImplWin32::setPosition(const Vector2i& position) { SetWindowPos(m_handle, NULL, position.x, position.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + + if(m_cursorGrabbed) + grabCursor(true); } @@ -363,6 +369,14 @@ void WindowImplWin32::setMouseCursorVisible(bool visible) } +//////////////////////////////////////////////////////////// +void WindowImplWin32::setMouseCursorGrabbed(bool grabbed) +{ + m_cursorGrabbed = grabbed; + grabCursor(m_cursorGrabbed); +} + + //////////////////////////////////////////////////////////// void WindowImplWin32::setKeyRepeatEnabled(bool enabled) { @@ -485,6 +499,23 @@ void WindowImplWin32::setTracking(bool track) } +//////////////////////////////////////////////////////////// +void WindowImplWin32::grabCursor(bool grabbed) +{ + if (grabbed) + { + RECT rect; + GetClientRect(m_handle, &rect); + MapWindowPoints(m_handle, NULL, reinterpret_cast(&rect), 2); + ClipCursor(&rect); + } + else + { + ClipCursor(NULL); + } +} + + //////////////////////////////////////////////////////////// void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) { @@ -536,6 +567,9 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) event.size.width = m_lastSize.x; event.size.height = m_lastSize.y; pushEvent(event); + + // Restore/update cursor grabbing + grabCursor(m_cursorGrabbed); } break; } @@ -544,6 +578,7 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) case WM_ENTERSIZEMOVE: { m_resizing = true; + grabCursor(false); break; } @@ -565,6 +600,9 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) event.size.height = m_lastSize.y; pushEvent(event); } + + // Restore/update cursor grabbing + grabCursor(m_cursorGrabbed); break; } @@ -582,6 +620,9 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) // Gain focus event case WM_SETFOCUS: { + // Restore cursor grabbing + grabCursor(m_cursorGrabbed); + Event event; event.type = Event::GainedFocus; pushEvent(event); @@ -591,6 +632,9 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) // Lost focus event case WM_KILLFOCUS: { + // Ungrab the cursor + grabCursor(false); + Event event; event.type = Event::LostFocus; pushEvent(event); diff --git a/src/SFML/Window/Win32/WindowImplWin32.hpp b/src/SFML/Window/Win32/WindowImplWin32.hpp index fb5fe0f3c..c80c34592 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.hpp +++ b/src/SFML/Window/Win32/WindowImplWin32.hpp @@ -145,6 +145,14 @@ public: //////////////////////////////////////////////////////////// virtual void setMouseCursorVisible(bool visible); + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorGrabbed(bool grabbed); + //////////////////////////////////////////////////////////// /// \brief Enable or disable automatic key-repeat /// @@ -216,6 +224,19 @@ private: //////////////////////////////////////////////////////////// void setTracking(bool track); + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// This is not to be confused with setMouseCursorGrabbed. + /// Here m_cursorGrabbed is not modified; it is used, + /// for example, to release the cursor when switching to + /// another application. + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + void grabCursor(bool grabbed); + //////////////////////////////////////////////////////////// /// \brief Convert a Win32 virtual key code to a SFML key code /// @@ -252,6 +273,8 @@ private: bool m_resizing; ///< Is the window being resized? Uint16 m_surrogate; ///< First half of the surrogate pair, in case we're receiving a Unicode character in two events bool m_mouseInside; ///< Mouse is inside the window? + bool m_fullscreen; ///< Is the window fullscreen? + bool m_cursorGrabbed; ///< Is the mouse cursor trapped? }; } // namespace priv diff --git a/src/SFML/Window/Window.cpp b/src/SFML/Window/Window.cpp index a091325fb..75ebcea3d 100644 --- a/src/SFML/Window/Window.cpp +++ b/src/SFML/Window/Window.cpp @@ -288,6 +288,14 @@ void Window::setMouseCursorVisible(bool visible) } +//////////////////////////////////////////////////////////// +void Window::setMouseCursorGrabbed(bool grabbed) +{ + if (m_impl) + m_impl->setMouseCursorGrabbed(grabbed); +} + + //////////////////////////////////////////////////////////// void Window::setKeyRepeatEnabled(bool enabled) { diff --git a/src/SFML/Window/WindowImpl.hpp b/src/SFML/Window/WindowImpl.hpp index 1e07a0f5b..ec357ab7a 100644 --- a/src/SFML/Window/WindowImpl.hpp +++ b/src/SFML/Window/WindowImpl.hpp @@ -186,6 +186,14 @@ public: //////////////////////////////////////////////////////////// virtual void setMouseCursorVisible(bool visible) = 0; + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor and keeps it from leaving + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorGrabbed(bool grabbed) = 0; + //////////////////////////////////////////////////////////// /// \brief Enable or disable automatic key-repeat ///