Explorar o código

Added visible OS mouse cursor mode. When used with an external window, this is the only supported mode, as SDL does not control the cursor visibility of an external window.

Lasse Öörni %!s(int64=13) %!d(string=hai) anos
pai
achega
481fbfa220

+ 2 - 2
Docs/Reference.dox

@@ -387,7 +387,7 @@ Graphics implements the low-level functionality:
 - Performing primitive rendering operations
 - Handling lost device
 
-Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling level are all set at once by calling Graphics's \ref Graphics::SetMode "SetMode()" function.
+Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling level are all set at once by calling Graphics's \ref Graphics::SetMode "SetMode()" function.  There is also an experimental option of rendering to an existing window by passing its OS-specific handle to \ref Graphics::SetExternalWindow "SetExternalWindow()" before setting the initial screen mode.
 
 When setting the initial screen mode, Graphics does a few checks:
 
@@ -795,7 +795,7 @@ Rendering detailed auxiliary views can easily have a large performance impact. S
 
 \page Input %Input
 
-The Input subsystem provides keyboard, mouse, joystick and touch input via both a polled interface and events. It is always instantiated, even in headless mode, but is active only once the application window has been created. Once active, the subsystem takes over the application mouse cursor. It will be hidden, so the UI should be used to render a software cursor if necessary.
+The Input subsystem provides keyboard, mouse, joystick and touch input via both a polled interface and events. It is always instantiated, even in headless mode, but is active only once the application window has been created. Once active, the subsystem takes over the operating system mouse cursor. It will be hidden by default, so the UI should be used to render a software cursor if necessary. For editor-like applications the operating system cursor can be made visible by calling \ref Input::SetMouseVisible "SetMouseVisible()".
 
 The input events include:
 

+ 2 - 0
Docs/ScriptAPI.dox

@@ -2559,6 +2559,7 @@ Methods:<br>
 Properties:<br>
 - ShortStringHash type (readonly)
 - String typeName (readonly)
+- bool mouseVisible
 - bool toggleFullscreen
 - bool[] keyDown (readonly)
 - bool[] keyPress (readonly)
@@ -2567,6 +2568,7 @@ Properties:<br>
 - bool[] qualifierDown (readonly)
 - bool[] qualifierPress (readonly)
 - int qualifiers (readonly)
+- IntVector2 mousePosition (readonly)
 - IntVector2 mouseMove (readonly)
 - int mouseMoveX (readonly)
 - int mouseMoveY (readonly)

+ 3 - 0
Engine/Engine/InputAPI.cpp

@@ -142,6 +142,8 @@ static void RegisterInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool OpenJoystick(uint)", asMETHOD(Input, OpenJoystick), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void CloseJoystick(uint)", asMETHOD(Input, CloseJoystick), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool DetectJoysticks()", asMETHOD(Input, DetectJoysticks), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "void set_mouseVisible(bool)", asMETHOD(Input, SetMouseVisible), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "bool get_mouseVisible() const", asMETHOD(Input, IsMouseVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void set_toggleFullscreen(bool)", asMETHOD(Input, SetToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool get_toggleFullscreen() const", asMETHOD(Input, GetToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool get_keyDown(int) const", asMETHOD(Input, GetKeyDown), asCALL_THISCALL);
@@ -151,6 +153,7 @@ static void RegisterInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool get_qualifierDown(int) const", asMETHOD(Input, GetQualifierDown), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool get_qualifierPress(int) const", asMETHOD(Input, GetQualifierPress), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int get_qualifiers() const", asMETHOD(Input, GetQualifiers), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "IntVector2 get_mousePosition() const", asMETHOD(Input, GetMousePosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "const IntVector2& get_mouseMove() const", asMETHOD(Input, GetMouseMove), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int get_mouseMoveX() const", asMETHOD(Input, GetMouseMoveX), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int get_mouseMoveY() const", asMETHOD(Input, GetMouseMoveY), asCALL_THISCALL);

+ 1 - 1
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -77,7 +77,7 @@ public:
     /// Destruct. Close the window and release the Direct3D9 device.
     virtual ~Graphics();
     
-    /// %Set external window handle. Call before setting mode for the first time.
+    /// %Set external window handle. Only effective before setting the initial screen mode.
     void SetExternalWindow(void* window);
     /// %Set window title.
     void SetWindowTitle(const String& windowTitle);

+ 1 - 1
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -82,7 +82,7 @@ public:
     /// Destruct. Release the OpenGL context and close the window.
     virtual ~Graphics();
     
-    /// %Set external window handle. Call before setting mode for the first time.
+    /// %Set external window handle. Only effective before setting the initial screen mode. On Windows it is necessary to set up OpenGL pixel format manually for the window.
     void SetExternalWindow(void* window);
     /// %Set window title.
     void SetWindowTitle(const String& windowTitle);

+ 66 - 26
Engine/Input/Input.cpp

@@ -67,6 +67,7 @@ Input::Input(Context* context) :
     Object(context),
     windowID_(0),
     toggleFullscreen_(true),
+    mouseVisible_(false),
     active_(false),
     minimized_(false),
     activated_(false),
@@ -145,7 +146,8 @@ void Input::Update()
     if (window)
     {
         unsigned flags = SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS);
-        if (!active_ && graphics_->GetFullscreen() && flags == (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS))
+        if (!active_ && (graphics_->GetFullscreen() || mouseVisible_) && flags == (SDL_WINDOW_INPUT_FOCUS |
+            SDL_WINDOW_MOUSE_FOCUS))
             activated_ = true;
         else if (active_ && (flags & SDL_WINDOW_INPUT_FOCUS) == 0)
             MakeInactive();
@@ -160,13 +162,18 @@ void Input::Update()
     // Check for mouse move
     if (active_)
     {
-        IntVector2 mousePos = GetCursorPosition();
-        mouseMove_ = mousePos - lastCursorPosition_;
+        IntVector2 mousePosition = GetMousePosition();
+        mouseMove_ = mousePosition - lastMousePosition_;
         
         // Recenter the mouse cursor manually
-        IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
-        SetCursorPosition(center);
-        lastCursorPosition_ = center;
+        if (!mouseVisible_)
+        {
+            IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
+            SetMousePosition(center);
+            lastMousePosition_ = center;
+        }
+        else
+            lastMousePosition_ = mousePosition;
         
         if (mouseMove_ != IntVector2::ZERO && suppressNextMouseMove_)
         {
@@ -180,6 +187,11 @@ void Input::Update()
             using namespace MouseMove;
             
             VariantMap eventData;
+            if (mouseVisible_)
+            {
+                eventData[P_X] = mousePosition.x_;
+                eventData[P_Y] = mousePosition.y_;
+            }
             eventData[P_DX] = mouseMove_.x_;
             eventData[P_DY] = mouseMove_.y_;
             eventData[P_BUTTONS] = mouseButtonDown_;
@@ -189,6 +201,25 @@ void Input::Update()
     }
 }
 
+void Input::SetMouseVisible(bool enable)
+{
+    // External windows can only support visible mouse cursor
+    if (graphics_ && graphics_->GetExternalWindow())
+        enable = true;
+    
+    if (enable != mouseVisible_)
+    {
+        mouseVisible_ = enable;
+        if (initialized_)
+        {
+            if (!mouseVisible_ && active_)
+                SDL_ShowCursor(SDL_FALSE);
+            else
+                SDL_ShowCursor(SDL_TRUE);
+        }
+    }
+}
+
 void Input::SetToggleFullscreen(bool enable)
 {
     toggleFullscreen_ = enable;
@@ -318,6 +349,18 @@ int Input::GetQualifiers() const
     return ret;
 }
 
+IntVector2 Input::GetMousePosition() const
+{
+    IntVector2 ret = IntVector2::ZERO;
+    
+    if (!graphics_ || !graphics_->IsInitialized())
+        return ret;
+    
+    SDL_GetMouseState(&ret.x_, &ret.y_);
+    
+    return ret;
+}
+
 TouchState* Input::GetTouch(unsigned index) const
 {
     unsigned cmpIndex = 0;
@@ -371,6 +414,10 @@ void Input::Initialize()
     
     graphics_ = graphics;
     
+    // In external window mode only visible mouse is supported
+    if (graphics_->GetExternalWindow())
+        mouseVisible_ = true;
+    
     // Set the initial activation
     activated_ = true;
     initialized_ = true;
@@ -407,8 +454,13 @@ void Input::MakeActive()
     activated_ = false;
     
     // Re-establish mouse cursor hiding as necessary
-    SDL_ShowCursor(SDL_FALSE);
-    suppressNextMouseMove_ = true;
+    if (!mouseVisible_)
+    {
+        SDL_ShowCursor(SDL_FALSE);
+        suppressNextMouseMove_ = true;
+    }
+    else
+        lastMousePosition_ = GetMousePosition();
     
     SendActivationEvent();
 }
@@ -480,8 +532,9 @@ void Input::SendActivationEvent()
 
 void Input::SetMouseButton(int button, bool newState)
 {
-    // After deactivation in windowed mode, activate by a left-click inside the window
-    if (initialized_ && !graphics_->GetFullscreen())
+    // After deactivation in windowed hidden mouse mode, activate only after a left-click inside the window
+    // This allows glitchfree window dragging on all operating systems
+    if (initialized_ && !graphics_->GetFullscreen() && !mouseVisible_)
     {
         if (!active_ && newState && button == MOUSEB_LEFT)
             activated_ = true;
@@ -573,7 +626,7 @@ void Input::SetMouseWheel(int delta)
     }
 }
 
-void Input::SetCursorPosition(const IntVector2& position)
+void Input::SetMousePosition(const IntVector2& position)
 {
     if (!graphics_)
         return;
@@ -581,19 +634,6 @@ void Input::SetCursorPosition(const IntVector2& position)
     SDL_WarpMouseInWindow(graphics_->GetImpl()->GetWindow(), position.x_, position.y_);
 }
 
-
-IntVector2 Input::GetCursorPosition() const
-{
-    IntVector2 ret = lastCursorPosition_;
-    
-    if (!graphics_ || !graphics_->IsInitialized())
-        return ret;
-    
-    SDL_GetMouseState(&ret.x_, &ret.y_);
-    
-    return ret;
-}
-
 void Input::HandleSDLEvent(void* sdlEvent)
 {
     SDL_Event& evt = *static_cast<SDL_Event*>(sdlEvent);
@@ -864,8 +904,8 @@ void Input::HandleScreenMode(StringHash eventType, VariantMap& eventData)
         windowID_ = newWindowID;
     }
     IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
-    SetCursorPosition(center);
-    lastCursorPosition_ = center;
+    SetMousePosition(center);
+    lastMousePosition_ = center;
     activated_ = true;
     
     // After setting a new screen mode we should not be minimized

+ 10 - 4
Engine/Input/Input.h

@@ -125,6 +125,8 @@ public:
     void Update();
     /// %Set whether ALT-ENTER fullscreen toggle is enabled.
     void SetToggleFullscreen(bool enable);
+    /// %Set whether the operating system mouse cursor is visible. When not visible (default), is kept centered to prevent leaving the window.
+    void SetMouseVisible(bool enable);
     /// Open a joystick. Return true if successful.
     bool OpenJoystick(unsigned index);
     /// Close a joystick.
@@ -146,6 +148,8 @@ public:
     bool GetQualifierPress(int qualifier) const;
     /// Return the currently held down qualifiers.
     int GetQualifiers() const;
+    /// Return mouse position within window. Should only be used with a visible mouse cursor.
+    IntVector2 GetMousePosition() const;
     /// Return mouse movement since last frame.
     const IntVector2& GetMouseMove() const { return mouseMove_; }
     /// Return horizontal mouse movement since last frame.
@@ -166,6 +170,8 @@ public:
     JoystickState* GetJoystick(unsigned index);
     /// Return whether fullscreen toggle is enabled.
     bool GetToggleFullscreen() const { return toggleFullscreen_; }
+    /// Return whether the operating system mouse cursor is visible.
+    bool IsMouseVisible() const { return mouseVisible_; }
     /// Return whether application window is active.
     bool IsActive() { return active_; }
     /// Return whether application window is minimized.
@@ -191,9 +197,7 @@ private:
     /// Handle mousewheel change.
     void SetMouseWheel(int delta);
     /// Internal function to set the mouse cursor position.
-    void SetCursorPosition(const IntVector2& position);
-    /// Internal function to get the mouse cursor position.
-    IntVector2 GetCursorPosition() const;
+    void SetMousePosition(const IntVector2& position);
     /// Handle screen mode event.
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     /// Handle frame start event.
@@ -216,7 +220,7 @@ private:
     /// Mouse buttons' pressed state.
     unsigned mouseButtonPress_;
     /// Last mouse position for calculating movement.
-    IntVector2 lastCursorPosition_;
+    IntVector2 lastMousePosition_;
     /// Mouse movement since last frame.
     IntVector2 mouseMove_;
     /// Mouse wheel movement since last frame.
@@ -225,6 +229,8 @@ private:
     unsigned windowID_;
     /// Fullscreen toggle flag.
     bool toggleFullscreen_;
+    /// Operating system mouse cursor visible flag.
+    bool mouseVisible_;
     /// Active flag.
     bool active_;
     /// Minimized flag.

+ 2 - 0
Engine/Input/InputEvents.h

@@ -47,6 +47,8 @@ EVENT(E_MOUSEBUTTONUP, MouseButtonUp)
 /// Mouse moved.
 EVENT(E_MOUSEMOVE, MouseMove)
 {
+    PARAM(P_X, X);                          // int (only when mouse visible)
+    PARAM(P_Y, Y);                          // int (only when mouse visible)
     PARAM(P_DX, DX);                        // int
     PARAM(P_DY, DY);                        // int
     PARAM(P_BUTTONS, Buttons);              // int

+ 30 - 8
Engine/UI/UI.cpp

@@ -234,6 +234,15 @@ void UI::RenderUpdate()
     
     PROFILE(GetUIBatches);
     
+    // If the OS cursor is visible, do not render the UI's own cursor
+    bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
+    bool uiCursorVisible = false;
+    if (osCursorVisible && cursor_)
+    {
+        uiCursorVisible = cursor_->IsVisible();
+        cursor_->SetTempVisible(false);
+    }
+    
     // Get batches & quads from the UI elements
     batches_.Clear();
     quads_.Clear();
@@ -243,6 +252,10 @@ void UI::RenderUpdate()
     // If no drag, reset cursor shape for next frame
     if (cursor_ && !dragElement_)
         cursor_->SetShape(CS_NORMAL);
+    
+    // Restore UI cursor visibility state
+    if (osCursorVisible && cursor_)
+        cursor_->SetTempVisible(uiCursorVisible);
 }
 
 void UI::Render()
@@ -778,17 +791,26 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
     
     if (cursor_)
     {
+        Input* input = GetSubsystem<Input>();
         const IntVector2& rootSize = rootElement_->GetSize();
         
-        // Move cursor only when visible
-        if (cursor_->IsVisible())
+        if (!input->IsMouseVisible())
         {
-            IntVector2 pos = cursor_->GetPosition();
-            pos.x_ += eventData[P_DX].GetInt();
-            pos.y_ += eventData[P_DY].GetInt();
-            pos.x_ = Clamp(pos.x_, 0, rootSize.x_ - 1);
-            pos.y_ = Clamp(pos.y_, 0, rootSize.y_ - 1);
-            cursor_->SetPosition(pos);
+            // Relative mouse motion: move cursor only when visible
+            if (cursor_->IsVisible())
+            {
+                IntVector2 pos = cursor_->GetPosition();
+                pos.x_ += eventData[P_DX].GetInt();
+                pos.y_ += eventData[P_DY].GetInt();
+                pos.x_ = Clamp(pos.x_, 0, rootSize.x_ - 1);
+                pos.y_ = Clamp(pos.y_, 0, rootSize.y_ - 1);
+                cursor_->SetPosition(pos);
+            }
+        }
+        else
+        {
+            // Absolute mouse motion: move always
+            cursor_->SetPosition(IntVector2(eventData[P_X].GetInt(), eventData[P_Y].GetInt()));
         }
         
         if (dragElement_ && mouseButtons_)

+ 5 - 0
Engine/UI/UIElement.cpp

@@ -1094,6 +1094,11 @@ void UIElement::SetHovering(bool enable)
     hovering_ = enable;
 }
 
+void UIElement::SetTempVisible(bool enable)
+{
+    visible_ = enable;
+}
+
 void UIElement::AdjustScissor(IntRect& currentScissor)
 {
     if (clipChildren_)

+ 2 - 0
Engine/UI/UIElement.h

@@ -360,6 +360,8 @@ public:
     void SetChildOffset(const IntVector2& offset);
     /// %Set hovering state.
     void SetHovering(bool enable);
+    /// %Set temporary visibility status without updating layout or sending events. Used internally.
+    void SetTempVisible(bool enable);
     /// Adjust scissor for rendering.
     void AdjustScissor(IntRect& currentScissor);
     /// Get UI rendering batches with a specified offset. Also recurses to child elements.