diff --git a/Source/Ultraviolet.SDL2/Shared/Input/SDL2MouseDevice.cs b/Source/Ultraviolet.SDL2/Shared/Input/SDL2MouseDevice.cs
index 4591cf185..d75e78860 100644
--- a/Source/Ultraviolet.SDL2/Shared/Input/SDL2MouseDevice.cs
+++ b/Source/Ultraviolet.SDL2/Shared/Input/SDL2MouseDevice.cs
@@ -110,12 +110,51 @@ public override void Update(UltravioletTime time)
}
- ///
- /// Gets the mouse cursor's position within the specified window.
- ///
- /// The window to evaluate.
- /// The cursor's compositor-space position within the specified
- /// window, or if the cursor is outside of the window.
+ ///
+ public override void WarpToWindow(IUltravioletWindow window, Int32 x, Int32 y)
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+ Contract.Require(window, nameof(window));
+
+ window.WarpMouseWithinWindow(x, y);
+ }
+
+ ///
+ public override void WarpToWindowCenter(IUltravioletWindow window)
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+ Contract.Require(window, nameof(window));
+
+ var size = window.ClientSize;
+ window.WarpMouseWithinWindow(size.Width / 2, size.Height / 2);
+ }
+
+ ///
+ public override void WarpToPrimaryWindow(Int32 x, Int32 y)
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+
+ var primary = Ultraviolet.GetPlatform().Windows.GetPrimary();
+ if (primary == null)
+ throw new InvalidOperationException(UltravioletStrings.NoPrimaryWindow);
+
+ primary.WarpMouseWithinWindow(x, y);
+ }
+
+ ///
+ public override void WarpToPrimaryWindowCenter()
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+
+ var primary = Ultraviolet.GetPlatform().Windows.GetPrimary();
+ if (primary == null)
+ throw new InvalidOperationException(UltravioletStrings.NoPrimaryWindow);
+
+ var size = primary.ClientSize;
+ primary.WarpMouseWithinWindow(size.Width / 2, size.Height / 2);
+ }
+
+ ///
public override Point2? GetPositionInWindow(IUltravioletWindow window)
{
Contract.Require(window, nameof(window));
@@ -177,6 +216,30 @@ public override Boolean IsButtonDoubleClicked(MouseButton button)
return (buttonStateDoubleClicks & SDL_BUTTON(button)) != 0;
}
+ ///
+ public override Boolean GetIsRelativeModeEnabled()
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+
+ return SDL_GetRelativeMouseMode();
+ }
+
+ ///
+ public override Boolean SetIsRelativeModeEnabled(Boolean enabled)
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+
+ var result = SDL_SetRelativeMouseMode(enabled);
+ if (result == -1)
+ return false;
+
+ if (result < 0)
+ throw new SDL2Exception();
+
+ relativeMode = enabled;
+ return true;
+ }
+
///
public override IUltravioletWindow Window => window;
@@ -284,8 +347,16 @@ private void OnMouseMotion(ref SDL_MouseMotionEvent evt)
if (!Ultraviolet.GetInput().EmulateMouseWithTouchInput && evt.which == SDL_TOUCH_MOUSEID)
return;
- SetMousePosition(evt.windowID, evt.x, evt.y);
- OnMoved(window, evt.x, evt.y, evt.xrel, evt.yrel);
+ if (relativeMode)
+ {
+ SetMousePosition(evt.windowID, evt.x, evt.y);
+ OnMoved(window, evt.x, evt.y, evt.xrel, evt.yrel);
+ }
+ else
+ {
+ SetMousePosition(evt.windowID, evt.x, evt.y);
+ OnMoved(window, evt.x, evt.y, evt.xrel, evt.yrel);
+ }
}
///
@@ -404,5 +475,6 @@ private void SetMousePositionFromDevicePosition(UInt32 windowID)
private UInt32 buttonStateClicks;
private UInt32 buttonStateDoubleClicks;
private Boolean ignoredFirstMouseMotionEvent;
+ private Boolean relativeMode;
}
}
diff --git a/Source/Ultraviolet.SDL2/Shared/Native/SDLNative.cs b/Source/Ultraviolet.SDL2/Shared/Native/SDLNative.cs
index 2b04fd9d9..b9607c095 100644
--- a/Source/Ultraviolet.SDL2/Shared/Native/SDLNative.cs
+++ b/Source/Ultraviolet.SDL2/Shared/Native/SDLNative.cs
@@ -1322,6 +1322,39 @@ public unsafe static partial class SDLNative
private static readonly SDL_freeDelegate pSDL_free = lib.LoadFunction("SDL_free");
public static void SDL_free(IntPtr mem) => pSDL_free(mem);
#endif
+
+#if ANDROID || IOS
+ [DllImport(LIBRARY, EntryPoint="SDL_GetRelativeMouseMode", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Boolean SDL_GetRelativeMouseMode();
+#else
+ [MonoNativeFunctionWrapper]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate Boolean SDL_GetRelativeMouseModeDelegate();
+ private static readonly SDL_GetRelativeMouseModeDelegate pSDL_GetRelativeMouseMode = lib.LoadFunction("SDL_GetRelativeMouseMode");
+ public static Boolean SDL_GetRelativeMouseMode() => pSDL_GetRelativeMouseMode();
+#endif
+
+#if ANDROID || IOS
+ [DllImport(LIBRARY, EntryPoint="SDL_SetRelativeMouseMode", CallingConvention = CallingConvention.Cdecl)]
+ public static extern Int32 SDL_SetRelativeMouseMode(Boolean enabled);
+#else
+ [MonoNativeFunctionWrapper]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate Int32 SDL_SetRelativeMouseModeDelegate(Boolean enabled);
+ private static readonly SDL_SetRelativeMouseModeDelegate pSDL_SetRelativeMouseMode = lib.LoadFunction("SDL_SetRelativeMouseMode");
+ public static Int32 SDL_SetRelativeMouseMode(Boolean enabled) => pSDL_SetRelativeMouseMode(enabled);
+#endif
+
+#if ANDROID || IOS
+ [DllImport(LIBRARY, EntryPoint="SDL_WarpMouseInWindow", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void SDL_WarpMouseInWindow(IntPtr window, Int32 x, Int32 y);
+#else
+ [MonoNativeFunctionWrapper]
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void SDL_WarpMouseInWindowDelegate(IntPtr window, Int32 x, Int32 y);
+ private static readonly SDL_WarpMouseInWindowDelegate pSDL_WarpMouseInWindow = lib.LoadFunction("SDL_WarpMouseInWindow");
+ public static void SDL_WarpMouseInWindow(IntPtr window, Int32 x, Int32 y) => pSDL_WarpMouseInWindow(window, x, y);
+#endif
}
#pragma warning restore 1591
}
\ No newline at end of file
diff --git a/Source/Ultraviolet.SDL2/Shared/Platform/SDL2UltravioletWindow.cs b/Source/Ultraviolet.SDL2/Shared/Platform/SDL2UltravioletWindow.cs
index 28c51196d..71055a936 100644
--- a/Source/Ultraviolet.SDL2/Shared/Platform/SDL2UltravioletWindow.cs
+++ b/Source/Ultraviolet.SDL2/Shared/Platform/SDL2UltravioletWindow.cs
@@ -129,6 +129,14 @@ void IMessageSubscriber.ReceiveMessage(UltravioletMessageI
}
}
+ ///
+ public void WarpMouseWithinWindow(Int32 x, Int32 y)
+ {
+ Contract.EnsureNotDisposed(this, Disposed);
+
+ SDL_WarpMouseInWindow(ptr, x, y);
+ }
+
///
public void SetFullscreenDisplayMode(DisplayMode displayMode)
{
diff --git a/Source/Ultraviolet.SDL2/Shared/SDL2UltravioletContext.cs b/Source/Ultraviolet.SDL2/Shared/SDL2UltravioletContext.cs
index 35bd92dc9..a1ae5b96b 100644
--- a/Source/Ultraviolet.SDL2/Shared/SDL2UltravioletContext.cs
+++ b/Source/Ultraviolet.SDL2/Shared/SDL2UltravioletContext.cs
@@ -96,7 +96,10 @@ protected Boolean InitSDL(UltravioletConfiguration configuration)
SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS;
if (Platform == UltravioletPlatform.Windows)
- SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
+ {
+ if (!SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1"))
+ throw new SDL2Exception();
+ }
return SDL_Init(sdlFlags) == 0;
}
diff --git a/Source/Ultraviolet/Shared/Input/MouseDevice.cs b/Source/Ultraviolet/Shared/Input/MouseDevice.cs
index 49129fe49..0e55a90aa 100644
--- a/Source/Ultraviolet/Shared/Input/MouseDevice.cs
+++ b/Source/Ultraviolet/Shared/Input/MouseDevice.cs
@@ -48,6 +48,32 @@ public MouseDevice(UltravioletContext uv)
}
+ ///
+ /// Sets the mouse cursor's position to the specified point within the specified window.
+ ///
+ /// The window within which to place the mouse cursor.
+ /// The x-coordinate within at which to position the cursor.
+ /// The y-coordinate within at which to position the cursor.
+ public abstract void WarpToWindow(IUltravioletWindow window, Int32 x, Int32 y);
+
+ ///
+ /// Sets the mouse cursor's position to the center of the specified window.
+ ///
+ /// The window within which to place the mouse cursor.
+ public abstract void WarpToWindowCenter(IUltravioletWindow window);
+
+ ///
+ /// Sets the mouse cursor's position to the specified point within the application's primary window.
+ ///
+ /// The x-coordinate within the primary window at which to position the cursor.
+ /// The y-coordinate within the primary window at which to position the cursor.
+ public abstract void WarpToPrimaryWindow(Int32 x, Int32 y);
+
+ ///
+ /// Sets the mouse cursor's position to the center of the application's primary window.
+ ///
+ public abstract void WarpToPrimaryWindowCenter();
+
///
/// Gets the mouse cursor's position within the specified window.
///
@@ -70,6 +96,21 @@ public MouseDevice(UltravioletContext uv)
/// if the button was double clicked this frame; otherwise, .
public abstract Boolean IsButtonDoubleClicked(MouseButton button);
+ ///
+ /// Gets a value indicating whether relative mouse mode is currently enabled.
+ ///
+ /// if relative mouse mode is currently enabled; otherwise, .
+ public abstract Boolean GetIsRelativeModeEnabled();
+
+ ///
+ /// Enables or disables relative mouse mode. In relative mode, the cursor is hidden, and the driver will try
+ /// to report continuous motion in the current window. Only relative motion events will be delivered, and
+ /// the mouse position will not change.
+ ///
+ /// to enable relative mode; otherwise, .
+ /// if relative mode was enabled or disabled; if relative mode is not supported.
+ public abstract Boolean SetIsRelativeModeEnabled(Boolean enabled);
+
///
/// Gets the window that currently contains the mouse cursor.
///
diff --git a/Source/Ultraviolet/Shared/Platform/IUltravioletWindow.cs b/Source/Ultraviolet/Shared/Platform/IUltravioletWindow.cs
index 89f46e0bb..b00f05735 100644
--- a/Source/Ultraviolet/Shared/Platform/IUltravioletWindow.cs
+++ b/Source/Ultraviolet/Shared/Platform/IUltravioletWindow.cs
@@ -21,6 +21,13 @@ namespace Ultraviolet.Platform
///
public interface IUltravioletWindow
{
+ ///
+ /// Warps the cursor to the specified position within this window.
+ ///
+ /// The x-coordinate within the window to which the mouse will be warped.
+ /// The y-coordinate within the window to which the mouse will be warped.
+ void WarpMouseWithinWindow(Int32 x, Int32 y);
+
///
/// Sets the window's fullscreen display mode.
///