Skip to content

Commit

Permalink
Implement relative mouse mode and mouse warping
Browse files Browse the repository at this point in the history
  • Loading branch information
tlgkccampbell committed Jun 28, 2019
1 parent f5f1e78 commit fa2947c
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 9 deletions.
88 changes: 80 additions & 8 deletions Source/Ultraviolet.SDL2/Shared/Input/SDL2MouseDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,51 @@ public override void Update(UltravioletTime time)

}

/// <summary>
/// Gets the mouse cursor's position within the specified window.
/// </summary>
/// <param name="window">The window to evaluate.</param>
/// <returns>The cursor's compositor-space position within the specified
/// window, or <see langword="null"/> if the cursor is outside of the window.</returns>
/// <inheritdoc/>
public override void WarpToWindow(IUltravioletWindow window, Int32 x, Int32 y)
{
Contract.EnsureNotDisposed(this, Disposed);
Contract.Require(window, nameof(window));

window.WarpMouseWithinWindow(x, y);
}

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
public override Point2? GetPositionInWindow(IUltravioletWindow window)
{
Contract.Require(window, nameof(window));
Expand Down Expand Up @@ -177,6 +216,30 @@ public override Boolean IsButtonDoubleClicked(MouseButton button)
return (buttonStateDoubleClicks & SDL_BUTTON(button)) != 0;
}

/// <inheritdoc/>
public override Boolean GetIsRelativeModeEnabled()
{
Contract.EnsureNotDisposed(this, Disposed);

return SDL_GetRelativeMouseMode();
}

/// <inheritdoc/>
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;
}

/// <inheritdoc/>
public override IUltravioletWindow Window => window;

Expand Down Expand Up @@ -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);
}
}

/// <summary>
Expand Down Expand Up @@ -404,5 +475,6 @@ private void SetMousePositionFromDevicePosition(UInt32 windowID)
private UInt32 buttonStateClicks;
private UInt32 buttonStateDoubleClicks;
private Boolean ignoredFirstMouseMotionEvent;
private Boolean relativeMode;
}
}
33 changes: 33 additions & 0 deletions Source/Ultraviolet.SDL2/Shared/Native/SDLNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,39 @@ public unsafe static partial class SDLNative
private static readonly SDL_freeDelegate pSDL_free = lib.LoadFunction<SDL_freeDelegate>("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_GetRelativeMouseModeDelegate>("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_SetRelativeMouseModeDelegate>("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_WarpMouseInWindowDelegate>("SDL_WarpMouseInWindow");
public static void SDL_WarpMouseInWindow(IntPtr window, Int32 x, Int32 y) => pSDL_WarpMouseInWindow(window, x, y);
#endif
}
#pragma warning restore 1591
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ void IMessageSubscriber<UltravioletMessageID>.ReceiveMessage(UltravioletMessageI
}
}

/// <inheritdoc/>
public void WarpMouseWithinWindow(Int32 x, Int32 y)
{
Contract.EnsureNotDisposed(this, Disposed);

SDL_WarpMouseInWindow(ptr, x, y);
}

/// <inheritdoc/>
public void SetFullscreenDisplayMode(DisplayMode displayMode)
{
Expand Down
5 changes: 4 additions & 1 deletion Source/Ultraviolet.SDL2/Shared/SDL2UltravioletContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
41 changes: 41 additions & 0 deletions Source/Ultraviolet/Shared/Input/MouseDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,32 @@ public MouseDevice(UltravioletContext uv)

}

/// <summary>
/// Sets the mouse cursor's position to the specified point within the specified window.
/// </summary>
/// <param name="window">The window within which to place the mouse cursor.</param>
/// <param name="x">The x-coordinate within <paramref name="window"/> at which to position the cursor.</param>
/// <param name="y">The y-coordinate within <paramref name="window"/> at which to position the cursor.</param>
public abstract void WarpToWindow(IUltravioletWindow window, Int32 x, Int32 y);

/// <summary>
/// Sets the mouse cursor's position to the center of the specified window.
/// </summary>
/// <param name="window">The window within which to place the mouse cursor.</param>
public abstract void WarpToWindowCenter(IUltravioletWindow window);

/// <summary>
/// Sets the mouse cursor's position to the specified point within the application's primary window.
/// </summary>
/// <param name="x">The x-coordinate within the primary window at which to position the cursor.</param>
/// <param name="y">The y-coordinate within the primary window at which to position the cursor.</param>
public abstract void WarpToPrimaryWindow(Int32 x, Int32 y);

/// <summary>
/// Sets the mouse cursor's position to the center of the application's primary window.
/// </summary>
public abstract void WarpToPrimaryWindowCenter();

/// <summary>
/// Gets the mouse cursor's position within the specified window.
/// </summary>
Expand All @@ -70,6 +96,21 @@ public MouseDevice(UltravioletContext uv)
/// <returns><see langword="true"/> if the button was double clicked this frame; otherwise, <see langword="false"/>.</returns>
public abstract Boolean IsButtonDoubleClicked(MouseButton button);

/// <summary>
/// Gets a value indicating whether relative mouse mode is currently enabled.
/// </summary>
/// <returns><see langword="true"/> if relative mouse mode is currently enabled; otherwise, <see langword="false"/>.</returns>
public abstract Boolean GetIsRelativeModeEnabled();

/// <summary>
/// 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.
/// </summary>
/// <param name="enabled"><see langword="true"/> to enable relative mode; otherwise, <see langword="false"/>.</param>
/// <returns><see langword="true"/> if relative mode was enabled or disabled; <see langword="false"/> if relative mode is not supported.</returns>
public abstract Boolean SetIsRelativeModeEnabled(Boolean enabled);

/// <summary>
/// Gets the window that currently contains the mouse cursor.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions Source/Ultraviolet/Shared/Platform/IUltravioletWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ namespace Ultraviolet.Platform
/// </summary>
public interface IUltravioletWindow
{
/// <summary>
/// Warps the cursor to the specified position within this window.
/// </summary>
/// <param name="x">The x-coordinate within the window to which the mouse will be warped.</param>
/// <param name="y">The y-coordinate within the window to which the mouse will be warped.</param>
void WarpMouseWithinWindow(Int32 x, Int32 y);

/// <summary>
/// Sets the window's fullscreen display mode.
/// </summary>
Expand Down

0 comments on commit fa2947c

Please sign in to comment.