Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/KappaDuck.Quack/Events/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace KappaDuck.Quack.Events;
private readonly ThemeChangedEvent _themeChangedEvent;
private readonly KeyboardAddedEvent _keyboardAddedEvent;
private readonly KeyboardRemovedEvent _keyboardRemovedEvent;
private readonly MouseAddedEvent _mouseAddedEvent;
private readonly MouseRemovedEvent _mouseRemovedEvent;

/// <summary>
/// Initializes a quit requested event.
Expand Down Expand Up @@ -67,6 +69,26 @@ public Event(KeyboardRemovedEvent e)
Type = SDL_EventType.KeyboardRemoved;
}

/// <summary>
/// Initializes a mouse added event.
/// </summary>
/// <param name="e">The mouse added event.</param>
public Event(MouseAddedEvent e)
{
_mouseAddedEvent = e;
Type = SDL_EventType.MouseAdded;
}

/// <summary>
/// Initializes a mouse removed event.
/// </summary>
/// <param name="e">The mouse removed event.</param>
public Event(MouseRemovedEvent e)
{
_mouseRemovedEvent = e;
Type = SDL_EventType.MouseRemoved;
}

internal SDL_EventType Type { get; }

/// <summary>
Expand All @@ -85,6 +107,8 @@ public Event(KeyboardRemovedEvent e)
SDL_EventType.SystemThemeChanged => _themeChangedEvent,
SDL_EventType.KeyboardAdded => _keyboardAddedEvent,
SDL_EventType.KeyboardRemoved => _keyboardRemovedEvent,
SDL_EventType.MouseAdded => _mouseAddedEvent,
SDL_EventType.MouseRemoved => _mouseRemovedEvent,
_ => null
};

Expand Down Expand Up @@ -163,4 +187,38 @@ public bool TryGetValue(out KeyboardRemovedEvent e)
e = _keyboardRemovedEvent;
return true;
}

/// <summary>
/// Attempts to retrieve this event as a <see cref="MouseAddedEvent"/>.
/// </summary>
/// <param name="e">The mouse added event.</param>
/// <returns><see langword="true"/> if this event holds a <see cref="MouseAddedEvent"/>; otherwise <see langword="false"/></returns>
public bool TryGetValue(out MouseAddedEvent e)
{
if (Type != SDL_EventType.MouseAdded)
{
e = default;
return false;
}

e = _mouseAddedEvent;
return true;
}

/// <summary>
/// Attempts to retrieve this event as a <see cref="MouseRemovedEvent"/>.
/// </summary>
/// <param name="e">The mouse removed event.</param>
/// <returns><see langword="true"/> if this event holds a <see cref="MouseRemovedEvent"/>; otherwise <see langword="false"/></returns>
public bool TryGetValue(out MouseRemovedEvent e)
{
if (Type != SDL_EventType.MouseRemoved)
{
e = default;
return false;
}

e = _mouseRemovedEvent;
return true;
}
}
31 changes: 31 additions & 0 deletions src/KappaDuck.Quack/Events/EventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ internal static SDL_EventType Of<TEvent>() where TEvent : IEvent
if (type == typeof(KeyboardRemovedEvent))
return SDL_EventType.KeyboardRemoved;

if (type == typeof(MouseAddedEvent))
return SDL_EventType.MouseAdded;

if (type == typeof(MouseRemovedEvent))
return SDL_EventType.MouseRemoved;

return None;
}

Expand All @@ -41,6 +47,8 @@ internal static SDL_EventType Of<TEvent>() where TEvent : IEvent
SDL_EventType.SystemThemeChanged => new ThemeChangedEvent(),
SDL_EventType.KeyboardAdded => new KeyboardAddedEvent(e.KeyboardDevice.Which),
SDL_EventType.KeyboardRemoved => new KeyboardAddedEvent(e.KeyboardDevice.Which),
SDL_EventType.MouseAdded => new MouseAddedEvent(e.MouseDevice.Which),
SDL_EventType.MouseRemoved => new MouseRemovedEvent(e.MouseDevice.Which),
_ => default
};

Expand All @@ -49,18 +57,41 @@ internal static SDL_Event Convert(Event e)
SDL_Event native = new() { Type = e.Type };

if (e.Type == SDL_EventType.Quit)
{
native.Quit = new SDL_QuitEvent();
return native;
}

if (e.Type == SDL_EventType.KeyboardAdded)
{
e.TryGetValue(out KeyboardAddedEvent keyboardAddedEvent);
native.KeyboardDevice = new SDL_KeyboardDeviceEvent(keyboardAddedEvent.Device.Id, SDL_EventType.KeyboardAdded);

return native;
}

if (e.Type == SDL_EventType.KeyboardAdded)
{
e.TryGetValue(out KeyboardRemovedEvent keyboardRemovedEvent);
native.KeyboardDevice = new SDL_KeyboardDeviceEvent(keyboardRemovedEvent.Device.Id, SDL_EventType.KeyboardRemoved);

return native;
}

if (e.Type == SDL_EventType.MouseAdded)
{
e.TryGetValue(out MouseAddedEvent mouseAddedEvent);
native.MouseDevice = new SDL_MouseDeviceEvent(mouseAddedEvent.Device.Id, SDL_EventType.MouseAdded);

return native;
}

if (e.Type == SDL_EventType.KeyboardAdded)
{
e.TryGetValue(out MouseRemovedEvent mouseRemovedEvent);
native.MouseDevice = new SDL_MouseDeviceEvent(mouseRemovedEvent.Device.Id, SDL_EventType.MouseRemoved);

return native;
}

return native;
Expand Down
18 changes: 18 additions & 0 deletions src/KappaDuck.Quack/Events/MouseAddedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) KappaDuck.
// Licensed under the MIT license.

using KappaDuck.Quack.Input.Devices;

namespace KappaDuck.Quack.Events;

/// <summary>
/// Represents an event which a new mouse device was connected.
/// </summary>
/// <param name="mouseId">The mouse id which was added.</param>
public readonly struct MouseAddedEvent(uint mouseId)
{
/// <summary>
/// Gets the mouse device which was added.
/// </summary>
public MouseDevice Device => MouseDevices.FromId(mouseId);
}
18 changes: 18 additions & 0 deletions src/KappaDuck.Quack/Events/MouseRemovedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) KappaDuck.
// Licensed under the MIT license.

using KappaDuck.Quack.Input.Devices;

namespace KappaDuck.Quack.Events;

/// <summary>
/// Represents an event which a mouse device was disconnected.
/// </summary>
/// <param name="mouseId">The mouse id which was removed.</param>
public readonly struct MouseRemovedEvent(uint mouseId)
{
/// <summary>
/// Gets the mouse device which was removed.
/// </summary>
public MouseDevice Device => MouseDevices.FromId(mouseId);
}
26 changes: 26 additions & 0 deletions src/KappaDuck.Quack/Input/Devices/MouseDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) KappaDuck.
// Licensed under the MIT license.

namespace KappaDuck.Quack.Input.Devices;

/// <summary>
/// Represents a mouse input device.
/// </summary>
public sealed record MouseDevice
{
internal MouseDevice(uint id)
{
Id = id;
Name = SDL3.GetMouseNameById(id);
}

/// <summary>
/// Gets the mouse device's id.
/// </summary>
public uint Id { get; }

/// <summary>
/// Gets the mouse device's name.
/// </summary>
public string Name { get; }
}
52 changes: 52 additions & 0 deletions src/KappaDuck.Quack/Input/Devices/MouseDevices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) KappaDuck.
// Licensed under the MIT license.

namespace KappaDuck.Quack.Input.Devices;

/// <summary>
/// Provides functionalities for mouse input devices.
/// </summary>
public static class MouseDevices
{
/// <summary>
/// Gets all connected mouse devices.
/// </summary>
/// <remarks>
/// It will include any device or virtual driver that provides mouse functionality,
/// including some game controllers, KVM switches, etc. You should wait for input from
/// a device before you consider it actively in use.
/// </remarks>
public static IEnumerable<MouseDevice> All
{
get
{
ReadOnlySpan<uint> ids = SDL3.GetMice(out _);

if (ids.IsEmpty)
return [];

MouseDevice[] devices = new MouseDevice[ids.Length];

for (int i = 0; i < ids.Length; i++)
devices[i] = new MouseDevice(ids[i]);

return devices;
}
}

/// <summary>
/// Gets a value indicating whether the system has a mouse.
/// </summary>
public static bool HasMouse => SDL3.HasMouse();

/// <summary>
/// Gets the mouse device with the specified identifier.
/// </summary>
/// <param name="id">The identifier of the mouse device.</param>
/// <returns>The mouse device with the specified identifier.</returns>
public static MouseDevice FromId(uint id)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id);
return new(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ internal struct SDL_Event

[field: FieldOffset(0)]
internal SDL_KeyboardDeviceEvent KeyboardDevice { get; set; }

[field: FieldOffset(0)]
internal SDL_MouseDeviceEvent MouseDevice { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) KappaDuck.
// Licensed under the MIT license.

namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;

[StructLayout(LayoutKind.Sequential)]
internal readonly struct SDL_MouseDeviceEvent(uint which, SDL_EventType type)
{
private readonly SDL_EventType _type = type;
private readonly uint _reserved;
private readonly ulong _timestamp;

internal readonly uint Which { get; } = which;
}
15 changes: 15 additions & 0 deletions src/KappaDuck.Quack/Interop/SDL/SDL3.Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ internal static partial class SDL3
[return: MarshalUsing(typeof(CallerArrayMarshaller<,>), CountElementName = "length")]
internal static partial Span<uint> GetKeyboards(out int length);

[LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetMice")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalUsing(typeof(CallerArrayMarshaller<,>), CountElementName = "length")]
internal static partial Span<uint> GetMice(out int length);

[LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetMouseNameForID")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalUsing(typeof(SDLStringMarshaller))]
internal static partial string GetMouseNameById(uint id);

[LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetScancodeFromKey")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static partial Scancode GetScancodeFromKey(Key key, Keymod* keymod);
Expand All @@ -49,6 +59,11 @@ internal static partial class SDL3
[return: MarshalAs(UnmanagedType.I1)]
internal static partial bool HasKeyboard();

[LibraryImport(nameof(SDL3), EntryPoint = "SDL_HasMouse")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool HasMouse();

[LibraryImport(nameof(SDL3), EntryPoint = "SDL_HasScreenKeyboardSupport")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.I1)]
Expand Down