diff --git a/analyzers/disabled.editorconfig b/analyzers/disabled.editorconfig
index 5664a2c..04d740c 100644
--- a/analyzers/disabled.editorconfig
+++ b/analyzers/disabled.editorconfig
@@ -1,5 +1,10 @@
is_global = true
+# CA1008: Enums should have zero value
+# Some enum doesn't need a zero value
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1008
+dotnet_diagnostic.CA1008.severity = none
+
# CA1033: Interface methods should be callable by child types
# Some childs should not have the parent methods
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1033
diff --git a/src/KappaDuck.Quack/Events/CultureChangedEvent.cs b/src/KappaDuck.Quack/Events/CultureChangedEvent.cs
index 83db5aa..679d813 100644
--- a/src/KappaDuck.Quack/Events/CultureChangedEvent.cs
+++ b/src/KappaDuck.Quack/Events/CultureChangedEvent.cs
@@ -4,6 +4,6 @@
namespace KappaDuck.Quack.Events;
///
-/// Represents an event where the user's culture preferences has been changed.
+/// Raised when the user's culture preferences has been changed.
///
public readonly struct CultureChangedEvent : IEvent;
diff --git a/src/KappaDuck.Quack/Events/Event.cs b/src/KappaDuck.Quack/Events/Event.cs
index 7e4ac2c..1a2926a 100644
--- a/src/KappaDuck.Quack/Events/Event.cs
+++ b/src/KappaDuck.Quack/Events/Event.cs
@@ -20,6 +20,10 @@ namespace KappaDuck.Quack.Events;
private readonly MouseRemovedEvent _mouseRemovedEvent;
private readonly KeyPressedEvent _keyPressedEvent;
private readonly KeyReleasedEvent _keyReleasedEvent;
+ private readonly MouseButtonPressedEvent _buttonPressedEvent;
+ private readonly MouseButtonReleasedEvent _buttonReleasedEvent;
+ private readonly MouseMovedEvent _mouseMovedEvent;
+ private readonly MouseWheelEvent _wheelEvent;
///
/// Initializes a quit requested event.
@@ -111,6 +115,46 @@ public Event(KeyReleasedEvent e)
Type = SDL_EventType.KeyUp;
}
+ ///
+ /// Initializes a mouse button pressed event.
+ ///
+ /// The mouse button pressed event.
+ public Event(MouseButtonPressedEvent e)
+ {
+ _buttonPressedEvent = e;
+ Type = SDL_EventType.MouseButtonDown;
+ }
+
+ ///
+ /// Initializes a mouse button released event.
+ ///
+ /// The mouse button released event.
+ public Event(MouseButtonReleasedEvent e)
+ {
+ _buttonReleasedEvent = e;
+ Type = SDL_EventType.MouseButtonUp;
+ }
+
+ ///
+ /// Initializes a mouse moved event.
+ ///
+ /// The mouse moved event.
+ public Event(MouseMovedEvent e)
+ {
+ _mouseMovedEvent = e;
+ Type = SDL_EventType.MouseMotion;
+ }
+
+ ///
+ /// Initializes a mouse wheel event.
+ ///
+ /// The mouse wheel event.
+ public Event(MouseWheelEvent e)
+ {
+ _wheelEvent = e;
+ Type = SDL_EventType.MouseWheel;
+ }
+
internal SDL_EventType Type { get; }
///
@@ -133,6 +177,10 @@ public Event(KeyReleasedEvent e)
SDL_EventType.MouseRemoved => _mouseRemovedEvent,
SDL_EventType.KeyDown => _keyPressedEvent,
SDL_EventType.KeyUp => _keyReleasedEvent,
+ SDL_EventType.MouseButtonDown => _buttonPressedEvent,
+ SDL_EventType.MouseButtonUp => _buttonReleasedEvent,
+ SDL_EventType.MouseMotion => _mouseMovedEvent,
+ SDL_EventType.MouseWheel => _wheelEvent,
_ => null
};
@@ -279,4 +327,72 @@ public bool TryGetValue(out KeyReleasedEvent e)
e = _keyReleasedEvent;
return true;
}
+
+ ///
+ /// Attempts to retrieve this event as a .
+ ///
+ /// The mouse button pressed event.
+ /// if this event holds a ; otherwise
+ public bool TryGetValue(out MouseButtonPressedEvent e)
+ {
+ if (Type != SDL_EventType.MouseButtonDown)
+ {
+ e = default;
+ return false;
+ }
+
+ e = _buttonPressedEvent;
+ return true;
+ }
+
+ ///
+ /// Attempts to retrieve this event as a .
+ ///
+ /// The mouse button released event.
+ /// if this event holds a ; otherwise
+ public bool TryGetValue(out MouseButtonReleasedEvent e)
+ {
+ if (Type != SDL_EventType.MouseButtonUp)
+ {
+ e = default;
+ return false;
+ }
+
+ e = _buttonReleasedEvent;
+ return true;
+ }
+
+ ///
+ /// Attempts to retrieve this event as a .
+ ///
+ /// The mouse moved event.
+ /// if this event holds a ; otherwise
+ public bool TryGetValue(out MouseMovedEvent e)
+ {
+ if (Type != SDL_EventType.MouseMotion)
+ {
+ e = default;
+ return false;
+ }
+
+ e = _mouseMovedEvent;
+ return true;
+ }
+
+ ///
+ /// Attempts to retrieve this event as a .
+ ///
+ /// The mouse wheel event.
+ /// if this event holds a ; otherwise
+ public bool TryGetValue(out MouseWheelEvent e)
+ {
+ if (Type != SDL_EventType.KeyUp)
+ {
+ e = default;
+ return false;
+ }
+
+ e = _wheelEvent;
+ return true;
+ }
}
diff --git a/src/KappaDuck.Quack/Events/EventManager.cs b/src/KappaDuck.Quack/Events/EventManager.cs
index 1d7906c..0fe8d86 100644
--- a/src/KappaDuck.Quack/Events/EventManager.cs
+++ b/src/KappaDuck.Quack/Events/EventManager.cs
@@ -58,8 +58,8 @@ public static void Enable() where TEvent : IEvent
/// updates the window's recorded size. This lets you selectively drop events as they arrive.
///
///
- /// Only events that would enter the queue are filtered. Events you
- /// pass through the filter; events disabled with never reach it.
+ /// Only events that would enter the queue are filtered;
+ /// events disabled with never reach it.
///
///
/// The filter may run on a background thread, so keep it fast and thread-safe. The exception is
@@ -83,7 +83,7 @@ public static EventWatcher Watch(Action callback)
{
unsafe
{
- callback(EventType.Convert(*e));
+ callback(EventType.Convert(in *e));
}
return true;
@@ -100,7 +100,7 @@ private static byte OnFilter(void* data, SDL_Event* e)
unsafe
{
SDL_Event native = *e;
- Event evt = EventType.Convert(native);
+ Event evt = EventType.Convert(in native);
return filter(evt) ? (byte)1 : (byte)0;
}
}
diff --git a/src/KappaDuck.Quack/Events/EventQueue.cs b/src/KappaDuck.Quack/Events/EventQueue.cs
index 33df6b7..90635ee 100644
--- a/src/KappaDuck.Quack/Events/EventQueue.cs
+++ b/src/KappaDuck.Quack/Events/EventQueue.cs
@@ -61,7 +61,7 @@ public static int Peek(Span events)
SDLThrowHelper.ThrowIfNegative(count);
for (int i = 0; i < count; i++)
- events[i] = EventType.Convert(buffer[i]);
+ events[i] = EventType.Convert(in buffer[i]);
return count;
}
@@ -96,7 +96,7 @@ public static int Peek(Span events) where TEvent : IEvent
SDLThrowHelper.ThrowIfNegative(count);
for (int i = 0; i < count; i++)
- events[i] = (TEvent)EventType.Convert(buffer[i]).Value!;
+ events[i] = (TEvent)EventType.Convert(in buffer[i]).Value!;
return count;
}
@@ -116,7 +116,7 @@ public static bool Poll(out Event e)
return false;
}
- e = EventType.Convert(native);
+ e = EventType.Convert(in native);
return true;
}
@@ -133,44 +133,6 @@ public static bool Poll(out Event e)
///
public static void Pump() => SDL3.PumpEvents();
- ///
- /// Adds the specified event to the event queue.
- ///
- /// The event to push onto the queue.
- /// if the event was pushed; otherwise, if the event was filtered or the event queue being full.
- public static bool Push(Event e)
- {
- SDL_Event native = EventType.Convert(e);
- return SDL3.PushEvent(&native);
- }
-
- ///
- /// Adds the events to the event queue.
- ///
- ///
- /// If is empty, it will return 0.
- ///
- /// The events to push onto the queue.
- /// The number of events successfully pushed onto the queue.
- /// Thrown when failing to push events.
- public static int Push(ReadOnlySpan events)
- {
- if (events.IsEmpty)
- return 0;
-
- Span buffer = events.Length <= 32
- ? stackalloc SDL_Event[events.Length]
- : new SDL_Event[events.Length];
-
- for (int i = 0; i < buffer.Length; i++)
- buffer[i] = EventType.Convert(events[i]);
-
- int count = SDL3.PeepEvents(buffer, buffer.Length, SDL_EventAction.Add, EventType.None, EventType.End);
- SDLThrowHelper.ThrowIfNegative(count);
-
- return count;
- }
-
///
/// Runs over the events currently in the queue, keeping those for
/// which it returns and removing the rest.
@@ -189,7 +151,7 @@ public static void Retain(Predicate match)
{
unsafe
{
- return match(EventType.Convert(*e));
+ return match(EventType.Convert(in *e));
}
});
}
@@ -221,7 +183,7 @@ public static int Retrieve(Span events)
SDLThrowHelper.ThrowIfNegative(count);
for (int i = 0; i < count; i++)
- events[i] = EventType.Convert(buffer[i]);
+ events[i] = EventType.Convert(in buffer[i]);
return count;
}
@@ -256,7 +218,7 @@ public static int Retrieve(Span events) where TEvent : IEvent
SDLThrowHelper.ThrowIfNegative(count);
for (int i = 0; i < count; i++)
- events[i] = (TEvent)EventType.Convert(buffer[i]).Value!;
+ events[i] = (TEvent)EventType.Convert(in buffer[i]).Value!;
return count;
}
@@ -277,13 +239,13 @@ public static bool Wait(out Event e, TimeSpan? timeout = null)
if (!timeout.HasValue || timeout == Timeout.InfiniteTimeSpan)
{
SDL3.WaitEvent(out native);
- e = EventType.Convert(native);
+ e = EventType.Convert(in native);
return e.HasValue;
}
SDL3.WaitEventTimeout(out native, (int)timeout.Value.TotalMilliseconds);
- e = EventType.Convert(native);
+ e = EventType.Convert(in native);
return e.HasValue;
}
diff --git a/src/KappaDuck.Quack/Events/EventType.cs b/src/KappaDuck.Quack/Events/EventType.cs
index 23b350e..24e8917 100644
--- a/src/KappaDuck.Quack/Events/EventType.cs
+++ b/src/KappaDuck.Quack/Events/EventType.cs
@@ -42,97 +42,37 @@ internal static SDL_EventType Of() where TEvent : IEvent
if (type == typeof(KeyReleasedEvent))
return SDL_EventType.KeyUp;
+ if (type == typeof(MouseButtonPressedEvent))
+ return SDL_EventType.MouseButtonDown;
+
+ if (type == typeof(MouseButtonReleasedEvent))
+ return SDL_EventType.MouseButtonUp;
+
+ if (type == typeof(MouseMovedEvent))
+ return SDL_EventType.MouseMotion;
+
+ if (type == typeof(MouseWheelEvent))
+ return SDL_EventType.MouseWheel;
+
return None;
}
[SuppressMessage("Style", "IDE0072:Add missing cases", Justification = "The remaining types will be implemented in the future")]
- internal static Event Convert(SDL_Event e) => e.Type switch
+ internal static Event Convert(in SDL_Event e) => e.Type switch
{
SDL_EventType.Quit => new QuitRequestedEvent(),
SDL_EventType.LocaleChanged => new CultureChangedEvent(),
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),
- SDL_EventType.KeyDown => new KeyPressedEvent(e),
- SDL_EventType.KeyUp => new KeyReleasedEvent(e),
+ SDL_EventType.KeyboardAdded => new KeyboardAddedEvent(e.KeyboardDevice),
+ SDL_EventType.KeyboardRemoved => new KeyboardRemovedEvent(e.KeyboardDevice),
+ SDL_EventType.MouseAdded => new MouseAddedEvent(e.MouseDevice),
+ SDL_EventType.MouseRemoved => new MouseRemovedEvent(e.MouseDevice),
+ SDL_EventType.KeyDown => new KeyPressedEvent(e.Keyboard),
+ SDL_EventType.KeyUp => new KeyReleasedEvent(e.Keyboard),
+ SDL_EventType.MouseButtonDown => new MouseButtonPressedEvent(e.Button),
+ SDL_EventType.MouseButtonUp => new MouseButtonReleasedEvent(e.Button),
+ SDL_EventType.MouseMotion => new MouseMovedEvent(e.Motion),
+ SDL_EventType.MouseWheel => new MouseWheelEvent(e.Wheel),
_ => default
};
-
- 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;
- }
-
- if (e.Type == SDL_EventType.KeyDown)
- {
- e.TryGetValue(out KeyPressedEvent keyPressedEvent);
- native.Keyboard = new SDL_KeyboardEvent()
- {
- Type = SDL_EventType.KeyDown,
- Which = keyPressedEvent.Which,
- Scancode = keyPressedEvent.Code,
- Key = keyPressedEvent.Key,
- Mod = keyPressedEvent.Modifier,
- Repeat = keyPressedEvent.Repeat ? (byte)1 : (byte)0
- };
-
- return native;
- }
-
- if (e.Type == SDL_EventType.KeyUp)
- {
- e.TryGetValue(out KeyReleasedEvent keyReleasedEvent);
- native.Keyboard = new SDL_KeyboardEvent()
- {
- Type = SDL_EventType.KeyUp,
- Which = keyReleasedEvent.Which,
- Scancode = keyReleasedEvent.Code,
- Key = keyReleasedEvent.Key,
- Mod = keyReleasedEvent.Modifier
- };
-
- return native;
- }
-
- return native;
- }
}
diff --git a/src/KappaDuck.Quack/Events/KeyPressedEvent.cs b/src/KappaDuck.Quack/Events/KeyPressedEvent.cs
index cdcc7d2..8999436 100644
--- a/src/KappaDuck.Quack/Events/KeyPressedEvent.cs
+++ b/src/KappaDuck.Quack/Events/KeyPressedEvent.cs
@@ -2,26 +2,33 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input;
+using KappaDuck.Quack.Input.Devices;
using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a key has been pressed.
+/// Raised when a key has been pressed.
///
public readonly struct KeyPressedEvent
{
- internal KeyPressedEvent(SDL_Event e)
+ internal KeyPressedEvent(SDL_KeyboardEvent e)
{
- Which = e.Keyboard.Which;
- Code = e.Keyboard.Scancode;
- Key = e.Keyboard.Key;
- Modifier = e.Keyboard.Mod;
- Repeat = e.Keyboard.Repeat != 0;
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Code = e.Scancode;
+ Key = e.Key;
+ Modifier = e.Mod;
+ Repeat = e.Repeat != 0;
}
///
- /// Gets the keyboard instance id.
+ /// Gets the window id on which the key is pressed.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the keyboard instance id which the key pressed.
///
public uint Which { get; init; }
@@ -44,4 +51,9 @@ internal KeyPressedEvent(SDL_Event e)
/// Gets a value indicating whether is a key repeat.
///
public bool Repeat { get; init; }
+
+ ///
+ /// Gets the keyboard device which the key is pressed.
+ ///
+ public KeyboardDevice Device => KeyboardDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/KeyReleasedEvent.cs b/src/KappaDuck.Quack/Events/KeyReleasedEvent.cs
index bd906d5..df38212 100644
--- a/src/KappaDuck.Quack/Events/KeyReleasedEvent.cs
+++ b/src/KappaDuck.Quack/Events/KeyReleasedEvent.cs
@@ -2,25 +2,32 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input;
+using KappaDuck.Quack.Input.Devices;
using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a key has been released.
+/// Raised when a key has been released.
///
public readonly struct KeyReleasedEvent
{
- internal KeyReleasedEvent(SDL_Event e)
+ internal KeyReleasedEvent(SDL_KeyboardEvent e)
{
- Which = e.Keyboard.Which;
- Code = e.Keyboard.Scancode;
- Key = e.Keyboard.Key;
- Modifier = e.Keyboard.Mod;
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Code = e.Scancode;
+ Key = e.Key;
+ Modifier = e.Mod;
}
///
- /// Gets the keyboard instance id.
+ /// Gets the window id on which the key is released.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the keyboard device id which the key released.
///
public uint Which { get; init; }
@@ -38,4 +45,9 @@ internal KeyReleasedEvent(SDL_Event e)
/// Gets the current modifiers.
///
public Keymod Modifier { get; init; }
+
+ ///
+ /// Gets the keyboard device which the key is released.
+ ///
+ public KeyboardDevice Device => KeyboardDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/KeyboardAddedEvent.cs b/src/KappaDuck.Quack/Events/KeyboardAddedEvent.cs
index 6a27e12..8d44662 100644
--- a/src/KappaDuck.Quack/Events/KeyboardAddedEvent.cs
+++ b/src/KappaDuck.Quack/Events/KeyboardAddedEvent.cs
@@ -2,17 +2,24 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a new keyboard device was connected.
+/// Raised when a new keyboard device was connected.
///
-/// The keyboard id which was added.
-public readonly struct KeyboardAddedEvent(uint keyboardId)
+public readonly struct KeyboardAddedEvent
{
+ internal KeyboardAddedEvent(SDL_KeyboardDeviceEvent e) => Which = e.Which;
+
+ ///
+ /// Gets the keyboard device id which was added.
+ ///
+ public uint Which { get; }
+
///
/// Gets the keyboard device which was added.
///
- public KeyboardDevice Device => KeyboardDevices.FromId(keyboardId);
+ public KeyboardDevice Device => KeyboardDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/KeyboardRemovedEvent.cs b/src/KappaDuck.Quack/Events/KeyboardRemovedEvent.cs
index 063fbe1..85af621 100644
--- a/src/KappaDuck.Quack/Events/KeyboardRemovedEvent.cs
+++ b/src/KappaDuck.Quack/Events/KeyboardRemovedEvent.cs
@@ -2,17 +2,24 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a keyboard device was disconnected.
+/// Raised when a keyboard device was disconnected.
///
-/// The keyboard id which was removed.
-public readonly struct KeyboardRemovedEvent(uint keyboardId)
+public readonly struct KeyboardRemovedEvent
{
+ internal KeyboardRemovedEvent(SDL_KeyboardDeviceEvent e) => Which = e.Which;
+
+ ///
+ /// Gets the keyboard device id which was removed.
+ ///
+ public uint Which { get; }
+
///
/// Gets the keyboard device which was removed.
///
- public KeyboardDevice Device => KeyboardDevices.FromId(keyboardId);
+ public KeyboardDevice Device => KeyboardDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/MouseAddedEvent.cs b/src/KappaDuck.Quack/Events/MouseAddedEvent.cs
index ecd70ba..08c1a5f 100644
--- a/src/KappaDuck.Quack/Events/MouseAddedEvent.cs
+++ b/src/KappaDuck.Quack/Events/MouseAddedEvent.cs
@@ -2,17 +2,24 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a new mouse device was connected.
+/// Raised when a new mouse device was connected.
///
-/// The mouse id which was added.
-public readonly struct MouseAddedEvent(uint mouseId)
+public readonly struct MouseAddedEvent
{
+ internal MouseAddedEvent(SDL_MouseDeviceEvent e) => Which = e.Which;
+
+ ///
+ /// Gets the mouse device id which was added.
+ ///
+ public uint Which { get; }
+
///
/// Gets the mouse device which was added.
///
- public MouseDevice Device => MouseDevices.FromId(mouseId);
+ public MouseDevice Device => MouseDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/MouseButtonPressedEvent.cs b/src/KappaDuck.Quack/Events/MouseButtonPressedEvent.cs
new file mode 100644
index 0000000..192d8bd
--- /dev/null
+++ b/src/KappaDuck.Quack/Events/MouseButtonPressedEvent.cs
@@ -0,0 +1,54 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input;
+using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
+using System.Drawing;
+
+namespace KappaDuck.Quack.Events;
+
+///
+/// Raised when a mouse button is pressed.
+///
+public readonly struct MouseButtonPressedEvent : IEvent
+{
+ internal MouseButtonPressedEvent(SDL_MouseButtonEvent e)
+ {
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Button = e.Button;
+ Position = new PointF(e.X, e.Y);
+ Clicks = e.Clicks;
+ }
+
+ ///
+ /// Gets the window id on which the mouse button pressed.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the mouse id which the mouse button pressed.
+ ///
+ public uint Which { get; }
+
+ ///
+ /// Gets the button that was pressed.
+ ///
+ public MouseButton Button { get; }
+
+ ///
+ /// Gets the cursor position, relative to the top-left of the window, when the button was pressed.
+ ///
+ public PointF Position { get; }
+
+ ///
+ /// Gets the consecutive click count: 1 for a single click, 2 for a double click, and so on.
+ ///
+ public int Clicks { get; }
+
+ ///
+ /// Gets the mouse device which the button was pressed.
+ ///
+ public MouseDevice Device => MouseDevices.FromId(Which);
+}
diff --git a/src/KappaDuck.Quack/Events/MouseButtonReleasedEvent.cs b/src/KappaDuck.Quack/Events/MouseButtonReleasedEvent.cs
new file mode 100644
index 0000000..633a0e8
--- /dev/null
+++ b/src/KappaDuck.Quack/Events/MouseButtonReleasedEvent.cs
@@ -0,0 +1,48 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input;
+using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
+using System.Drawing;
+
+namespace KappaDuck.Quack.Events;
+
+///
+/// Raised when a mouse button is released.
+///
+public readonly struct MouseButtonReleasedEvent : IEvent
+{
+ internal MouseButtonReleasedEvent(SDL_MouseButtonEvent e)
+ {
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Button = e.Button;
+ Position = new PointF(e.X, e.Y);
+ }
+
+ ///
+ /// Gets the window id on which the mouse button released.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the mouse id which the mouse button released.
+ ///
+ public uint Which { get; }
+
+ ///
+ /// Gets the button that was released.
+ ///
+ public MouseButton Button { get; }
+
+ ///
+ /// Gets the cursor position, relative to the top-left of the window, when the button was released.
+ ///
+ public PointF Position { get; }
+
+ ///
+ /// Gets the mouse device which the button was released.
+ ///
+ public MouseDevice Device => MouseDevices.FromId(Which);
+}
diff --git a/src/KappaDuck.Quack/Events/MouseMovedEvent.cs b/src/KappaDuck.Quack/Events/MouseMovedEvent.cs
new file mode 100644
index 0000000..c758089
--- /dev/null
+++ b/src/KappaDuck.Quack/Events/MouseMovedEvent.cs
@@ -0,0 +1,55 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input;
+using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
+using System.Drawing;
+using System.Numerics;
+
+namespace KappaDuck.Quack.Events;
+
+///
+/// Raised when the mouse moves.
+///
+public readonly struct MouseMovedEvent : IEvent
+{
+ internal MouseMovedEvent(SDL_MouseMotionEvent e)
+ {
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Position = new PointF(e.X, e.Y);
+ Delta = new Vector2(e.Xrel, e.Yrel);
+ Buttons = e.State;
+ }
+
+ ///
+ /// Gets the window id on which the mouse moved.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the mouse id which moved.
+ ///
+ public uint Which { get; }
+
+ ///
+ /// Gets the cursor position, relative to the top-left of the window.
+ ///
+ public PointF Position { get; }
+
+ ///
+ /// Gets the motion since the previous event.
+ ///
+ public Vector2 Delta { get; }
+
+ ///
+ /// Gets the buttons held while the mouse moved
+ ///
+ public MouseButtonState Buttons { get; }
+
+ ///
+ /// Gets the mouse device which moved.
+ ///
+ public MouseDevice Device => MouseDevices.FromId(Which);
+}
diff --git a/src/KappaDuck.Quack/Events/MouseRemovedEvent.cs b/src/KappaDuck.Quack/Events/MouseRemovedEvent.cs
index f18eb3c..e5ee5e8 100644
--- a/src/KappaDuck.Quack/Events/MouseRemovedEvent.cs
+++ b/src/KappaDuck.Quack/Events/MouseRemovedEvent.cs
@@ -2,17 +2,24 @@
// Licensed under the MIT license.
using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
namespace KappaDuck.Quack.Events;
///
-/// Represents an event which a mouse device was disconnected.
+/// Raised when a mouse device was disconnected.
///
-/// The mouse id which was removed.
-public readonly struct MouseRemovedEvent(uint mouseId)
+public readonly struct MouseRemovedEvent
{
+ internal MouseRemovedEvent(SDL_MouseDeviceEvent e) => Which = e.Which;
+
+ ///
+ /// Gets the mouse device id which was removed.
+ ///
+ public uint Which { get; }
+
///
/// Gets the mouse device which was removed.
///
- public MouseDevice Device => MouseDevices.FromId(mouseId);
+ public MouseDevice Device => MouseDevices.FromId(Which);
}
diff --git a/src/KappaDuck.Quack/Events/MouseWheelEvent.cs b/src/KappaDuck.Quack/Events/MouseWheelEvent.cs
new file mode 100644
index 0000000..e08c242
--- /dev/null
+++ b/src/KappaDuck.Quack/Events/MouseWheelEvent.cs
@@ -0,0 +1,52 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input.Devices;
+using KappaDuck.Quack.Interop.SDL.Primitives;
+using KappaDuck.Quack.Interop.SDL.Primitives.Events;
+using System.Drawing;
+using System.Numerics;
+
+namespace KappaDuck.Quack.Events;
+
+///
+/// Raised when the mouse wheel is scrolled.
+///
+public readonly struct MouseWheelEvent : IEvent
+{
+ internal MouseWheelEvent(SDL_MouseWheelEvent e)
+ {
+ float sign = e.Direction is SDL_MouseWheelDirection.Flipped ? -1f : 1f;
+
+ WindowId = e.WindowId;
+ Which = e.Which;
+ Delta = new Vector2(e.X * sign, e.Y * sign);
+ Position = new PointF(e.MouseX, e.MouseY);
+ }
+
+ ///
+ /// Gets the window id on which the mouse scrolled.
+ ///
+ public uint WindowId { get; }
+
+ ///
+ /// Gets the mouse id which scrolled.
+ ///
+ public uint Which { get; }
+
+ ///
+ /// Gets the scroll amount. Positive Y scrolls away from the user and positive X to the
+ /// right, regardless of the platform's natural-scrolling setting.
+ ///
+ public Vector2 Delta { get; }
+
+ ///
+ /// Gets the cursor position, relative to the top-left of the window, when the wheel was scrolled.
+ ///
+ public PointF Position { get; }
+
+ ///
+ /// Gets the mouse device which scrolled.
+ ///
+ public MouseDevice Device => MouseDevices.FromId(Which);
+}
diff --git a/src/KappaDuck.Quack/Events/QuitRequestedEvent.cs b/src/KappaDuck.Quack/Events/QuitRequestedEvent.cs
index a495f75..bf8e908 100644
--- a/src/KappaDuck.Quack/Events/QuitRequestedEvent.cs
+++ b/src/KappaDuck.Quack/Events/QuitRequestedEvent.cs
@@ -4,7 +4,7 @@
namespace KappaDuck.Quack.Events;
///
-/// Represents a request to quit the application, raised when the user closes the
-/// last window or the operating system asks the application to terminate.
+/// raised when the user closes the last window or
+/// the operating system asks the application to terminate.
///
public readonly struct QuitRequestedEvent : IEvent;
diff --git a/src/KappaDuck.Quack/Events/ThemeChangedEvent.cs b/src/KappaDuck.Quack/Events/ThemeChangedEvent.cs
index 26b8cf2..9555aae 100644
--- a/src/KappaDuck.Quack/Events/ThemeChangedEvent.cs
+++ b/src/KappaDuck.Quack/Events/ThemeChangedEvent.cs
@@ -4,6 +4,6 @@
namespace KappaDuck.Quack.Events;
///
-/// Represents an event where the user's theme has been changed.
+/// Raised when the user's theme has been changed.
///
public readonly struct ThemeChangedEvent : IEvent;
diff --git a/src/KappaDuck.Quack/Input/Mouse.cs b/src/KappaDuck.Quack/Input/Mouse.cs
new file mode 100644
index 0000000..1348df5
--- /dev/null
+++ b/src/KappaDuck.Quack/Input/Mouse.cs
@@ -0,0 +1,170 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Events;
+using KappaDuck.Quack.Exceptions;
+using KappaDuck.Quack.Geometry;
+
+namespace KappaDuck.Quack.Input;
+
+///
+/// Represents a mouse input.
+///
+public static class Mouse
+{
+ ///
+ /// Gets the asynchronous mouse button state and the desktop-relative platform-cursor position of the mouse.
+ ///
+ ///
+ ///
+ /// It immediately queries the platform for the current mouse state, more costly than using .
+ ///
+ ///
+ /// In relative mode, the platform-cursor's position usually contradicts the engine-cursor's position as
+ /// manually calculated from and window's position.
+ ///
+ ///
+ public static MouseState GlobalState
+ {
+ get
+ {
+ MouseButtonState buttons = SDL3.GetGlobalMouseState(out float x, out float y);
+ return new MouseState(buttons, new PointF(x, y));
+ }
+ }
+
+ ///
+ /// Gets the engine cache for synchronous mouse button state and the window-relative position of the mouse.
+ ///
+ ///
+ ///
+ /// The cache is based on the last pump of the event queue. To query the
+ /// platform for immediate mouse state, use .
+ ///
+ ///
+ /// In relative mode, the platform-cursor's position usually contradicts the engine-cursor's position as
+ /// manually calculated from and window's position.
+ ///
+ ///
+ public static MouseState State
+ {
+ get
+ {
+ MouseButtonState buttons = SDL3.GetMouseState(out float x, out float y);
+ return new MouseState(buttons, new PointF(x, y));
+ }
+ }
+
+ ///
+ /// Gets the window-relative position of the cursor, from .
+ ///
+ public static PointF Position => State.Position;
+
+ ///
+ /// Gets or sets a value indicating whether the cursor is visible.
+ ///
+ /// Thrown when failed to change the cursor visibility.
+ public static bool Visible
+ {
+ get => SDL3.CursorVisible();
+ set => SDLThrowHelper.ThrowIfFailed(value ? SDL3.ShowCursor() : SDL3.HideCursor());
+ }
+
+ ///
+ /// Reads the mouse movement accumulated since the previous call.
+ ///
+ ///
+ ///
+ /// The movement comes from the engine cache, based on the last pump of the event queue.
+ /// To query the platform for immediate mouse state, use .
+ ///
+ ///
+ /// Each call consumes the accumulator, so an immediate second call returns near-zero. Read it once per frame.
+ ///
+ ///
+ /// It is useful for reducing overhead by processing relative mouse inputs in one go per-frame
+ /// instead of individually per-event, at the expense of losing the order between events within the frame
+ /// (e.g. quickly pressing and releasing a button within the same frame).
+ ///
+ ///
+ /// The movement accumulated since the last call.
+ public static Vector2 GetRelativeMotion()
+ {
+ _ = SDL3.GetRelativeMouseState(out float x, out float y);
+ return new Vector2(x, y);
+ }
+
+ ///
+ /// Capture the mouse and to track input outside the window.
+ ///
+ ///
+ ///
+ /// Capturing enables your app to obtain mouse events globally, instead of just within your window.
+ /// Not all video targets support this feature. When capturing is enabled, the current window will get all mouse
+ /// events, but unlike relative mode, no change is made to the cursor and it is not restrained to your window.
+ ///
+ ///
+ /// This method may also deny mouse input to other windows, both those in your application and others on
+ /// the system, so you should use this method sparingly and in small bursts. For example, you might want to track
+ /// the mouse while the user is dragging something, until the user releases a mouse button.
+ /// It is not recommended that you capture the mouse for long periods of time, such as the entire time
+ /// your app is running. For that, consider using or , depending on your needs.
+ ///
+ ///
+ /// While captured, mouse events still report coordinates relative to the current (foreground) window,
+ /// but those coordinates may be outside the bounds of the window (including negative values).
+ /// Capturing is only allowed for the foreground window. If the window loses focus while capturing,
+ /// the capture will be disabled automatically.
+ ///
+ ///
+ /// While capturing is enabled, the current window will have the set to .
+ ///
+ ///
+ /// Please note that the engine will attempt to "auto capture" the mouse while the user is pressing a button;
+ /// this is to try and make mouse behavior more consistent between platforms, and deal with the common case of
+ /// a user dragging the mouse outside of the window. This means that if you are calling this method only to
+ /// deal with this situation, you do not have to (although it is safe to do so).
+ ///
+ ///
+ /// value indicating whether to enable or disable mouse capture.
+ /// Thrown when failed to set mouse capture.
+ public static void Capture(bool enabled) => SDLThrowHelper.ThrowIfFailed(SDL3.CaptureMouse(enabled));
+
+ ///
+ /// Determines whether the specified button is currently pressed in .
+ ///
+ /// The button to check.
+ /// if the button is pressed; otherwise, .
+ public static bool IsDown(MouseButton button) => State.IsDown(button);
+
+ ///
+ /// Determines whether the specified button is currently released in .
+ ///
+ /// The button to check.
+ /// if the button is released; otherwise, .
+ public static bool IsUp(MouseButton button) => State.IsUp(button);
+
+ ///
+ /// Moves the mouse cursor to the given position in global screen space.
+ ///
+ ///
+ /// It generates a event.
+ /// It will not move the mouse when used over Microsoft Remote Desktop.
+ ///
+ /// The x-coordinate in global screen space.
+ /// The y-coordinate in global screen space.
+ /// Thrown when failed to warp the mouse.
+ public static void Warp(float x, float y)
+ => SDLThrowHelper.ThrowIfFailed(SDL3.WarpMouseGlobal(x, y));
+
+ ///
+ /// Moves the mouse cursor to the given position in global screen space.
+ ///
+ ///
+ /// It generates a event.
+ /// It will not move the mouse when used over Microsoft Remote Desktop.
+ ///
+ /// The position in global screen space.
+ /// Thrown when failed to warp the mouse.
+ public static void Warp(PointF position) => Warp(position.X, position.Y);
+}
diff --git a/src/KappaDuck.Quack/Input/MouseButton.cs b/src/KappaDuck.Quack/Input/MouseButton.cs
new file mode 100644
index 0000000..b7fc710
--- /dev/null
+++ b/src/KappaDuck.Quack/Input/MouseButton.cs
@@ -0,0 +1,35 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+namespace KappaDuck.Quack.Input;
+
+///
+/// Represents a mouse button.
+///
+public enum MouseButton : byte
+{
+ ///
+ /// The left mouse button.
+ ///
+ Left = 1,
+
+ ///
+ /// The middle mouse button, usually the wheel.
+ ///
+ Middle = 2,
+
+ ///
+ /// The right mouse button.
+ ///
+ Right = 3,
+
+ ///
+ /// The first extra mouse button, usually the side button.
+ ///
+ X1 = 4,
+
+ ///
+ /// The second extra mouse button, usually the side button.
+ ///
+ X2 = 5
+}
diff --git a/src/KappaDuck.Quack/Input/MouseButtonState.cs b/src/KappaDuck.Quack/Input/MouseButtonState.cs
new file mode 100644
index 0000000..b195ea6
--- /dev/null
+++ b/src/KappaDuck.Quack/Input/MouseButtonState.cs
@@ -0,0 +1,41 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+namespace KappaDuck.Quack.Input;
+
+///
+/// A bitmask of the mouse buttons currently held.
+///
+[Flags]
+public enum MouseButtonState : uint
+{
+ ///
+ /// No button is held.
+ ///
+ None = 0,
+
+ ///
+ /// The left mouse button.
+ ///
+ Left = 1 << 0,
+
+ ///
+ /// The middle mouse button, usually the wheel.
+ ///
+ Middle = 1 << 1,
+
+ ///
+ /// The right mouse button.
+ ///
+ Right = 1 << 2,
+
+ ///
+ /// The first extra mouse button, usually the side button.
+ ///
+ X1 = 1 << 3,
+
+ ///
+ /// The second extra mouse button, usually the side button.
+ ///
+ X2 = 1 << 4
+}
diff --git a/src/KappaDuck.Quack/Input/MouseState.cs b/src/KappaDuck.Quack/Input/MouseState.cs
new file mode 100644
index 0000000..4eeae63
--- /dev/null
+++ b/src/KappaDuck.Quack/Input/MouseState.cs
@@ -0,0 +1,44 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Geometry;
+
+namespace KappaDuck.Quack.Input;
+
+///
+/// A snapshot of the mouse buttons and cursor position.
+///
+public readonly struct MouseState
+{
+ internal MouseState(MouseButtonState buttons, PointF position)
+ {
+ Buttons = buttons;
+ Position = position;
+ }
+
+ ///
+ /// Gets the buttons held in this snapshot.
+ ///
+ public MouseButtonState Buttons { get; }
+
+ ///
+ /// Gets the cursor position captured with this snapshot.
+ ///
+ public PointF Position { get; }
+
+ ///
+ /// Determines whether the given button is held in this snapshot.
+ ///
+ /// The button to check.
+ /// if the button is held; otherwise, .
+ public bool IsDown(MouseButton button) => (Buttons & ToState(button)) != 0;
+
+ ///
+ /// Determines whether the given button is released in this snapshot.
+ ///
+ /// The button to check.
+ /// if the button is released; otherwise, .
+ public bool IsUp(MouseButton button) => !IsDown(button);
+
+ private static MouseButtonState ToState(MouseButton button) => (MouseButtonState)(1 << ((byte)button - 1));
+}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_Event.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_Event.cs
index 862f229..2d9c3af 100644
--- a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_Event.cs
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_Event.cs
@@ -4,20 +4,26 @@
namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
[StructLayout(LayoutKind.Explicit, Size = 128)]
-internal struct SDL_Event
+internal readonly struct SDL_Event
{
[field: FieldOffset(0)]
- internal SDL_EventType Type { get; set; }
+ internal SDL_EventType Type { get; }
[field: FieldOffset(0)]
- internal SDL_QuitEvent Quit { get; set; }
+ internal SDL_KeyboardDeviceEvent KeyboardDevice { get; }
[field: FieldOffset(0)]
- internal SDL_KeyboardDeviceEvent KeyboardDevice { get; set; }
+ internal SDL_MouseDeviceEvent MouseDevice { get; }
[field: FieldOffset(0)]
- internal SDL_MouseDeviceEvent MouseDevice { get; set; }
+ internal SDL_KeyboardEvent Keyboard { get; }
[field: FieldOffset(0)]
- internal SDL_KeyboardEvent Keyboard { get; set; }
+ internal SDL_MouseMotionEvent Motion { get; }
+
+ [field: FieldOffset(0)]
+ internal SDL_MouseButtonEvent Button { get; }
+
+ [field: FieldOffset(0)]
+ internal SDL_MouseWheelEvent Wheel { get; }
}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_KeyboardEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_KeyboardEvent.cs
index 8c5a148..61513b6 100644
--- a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_KeyboardEvent.cs
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_KeyboardEvent.cs
@@ -8,11 +8,11 @@ namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
[StructLayout(LayoutKind.Sequential)]
internal readonly struct SDL_KeyboardEvent
{
- internal SDL_EventType Type { get; init; }
-
+ private readonly SDL_EventType _type;
private readonly uint _reserved;
private readonly ulong _timestamp;
- private readonly uint _windowId;
+
+ internal uint WindowId { get; }
internal uint Which { get; init; }
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseButtonEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseButtonEvent.cs
new file mode 100644
index 0000000..6859cf9
--- /dev/null
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseButtonEvent.cs
@@ -0,0 +1,30 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input;
+
+namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
+
+[StructLayout(LayoutKind.Sequential)]
+internal readonly struct SDL_MouseButtonEvent
+{
+ private readonly SDL_EventType _type;
+ private readonly uint _reserved;
+ private readonly ulong _timestamp;
+
+ internal uint WindowId { get; }
+
+ internal uint Which { get; }
+
+ internal MouseButton Button { get; }
+
+ private readonly byte _down;
+
+ internal byte Clicks { get; }
+
+ private readonly byte _padding;
+
+ internal float X { get; }
+
+ internal float Y { get; }
+}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseDeviceEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseDeviceEvent.cs
index 5926758..f41c855 100644
--- a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseDeviceEvent.cs
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseDeviceEvent.cs
@@ -4,11 +4,11 @@
namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
[StructLayout(LayoutKind.Sequential)]
-internal readonly struct SDL_MouseDeviceEvent(uint which, SDL_EventType type)
+internal readonly struct SDL_MouseDeviceEvent
{
- private readonly SDL_EventType _type = type;
+ private readonly SDL_EventType _type;
private readonly uint _reserved;
private readonly ulong _timestamp;
- internal readonly uint Which { get; } = which;
+ internal readonly uint Which { get; }
}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseMotionEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseMotionEvent.cs
new file mode 100644
index 0000000..947800b
--- /dev/null
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseMotionEvent.cs
@@ -0,0 +1,28 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+using KappaDuck.Quack.Input;
+
+namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
+
+[StructLayout(LayoutKind.Sequential)]
+internal readonly struct SDL_MouseMotionEvent
+{
+ private readonly SDL_EventType _type;
+ private readonly uint _reserved;
+ private readonly ulong _timestamp;
+
+ internal uint WindowId { get; }
+
+ internal uint Which { get; }
+
+ internal MouseButtonState State { get; }
+
+ internal float X { get; }
+
+ internal float Y { get; }
+
+ internal float Xrel { get; }
+
+ internal float Yrel { get; }
+}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseWheelEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseWheelEvent.cs
new file mode 100644
index 0000000..1c81e48
--- /dev/null
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_MouseWheelEvent.cs
@@ -0,0 +1,29 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
+
+[StructLayout(LayoutKind.Sequential)]
+internal readonly struct SDL_MouseWheelEvent
+{
+ private readonly SDL_EventType _type;
+ private readonly uint _reserved;
+ private readonly ulong _timestamp;
+
+ internal uint WindowId { get; }
+
+ internal uint Which { get; }
+
+ internal float X { get; }
+
+ internal float Y { get; }
+
+ internal SDL_MouseWheelDirection Direction { get; }
+
+ internal float MouseX { get; }
+
+ internal float MouseY { get; }
+
+ private readonly int _integerX;
+ private readonly int _integerY;
+}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_QuitEvent.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_QuitEvent.cs
deleted file mode 100644
index a07af80..0000000
--- a/src/KappaDuck.Quack/Interop/SDL/Primitives/Events/SDL_QuitEvent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) KappaDuck.
-// Licensed under the MIT license.
-
-namespace KappaDuck.Quack.Interop.SDL.Primitives.Events;
-
-[StructLayout(LayoutKind.Sequential)]
-internal readonly struct SDL_QuitEvent()
-{
- private readonly SDL_EventType _type = SDL_EventType.Quit;
- private readonly uint _reserved;
- private readonly ulong _timestamp;
-}
diff --git a/src/KappaDuck.Quack/Interop/SDL/Primitives/SDL_MouseWheelDirection.cs b/src/KappaDuck.Quack/Interop/SDL/Primitives/SDL_MouseWheelDirection.cs
new file mode 100644
index 0000000..f151c04
--- /dev/null
+++ b/src/KappaDuck.Quack/Interop/SDL/Primitives/SDL_MouseWheelDirection.cs
@@ -0,0 +1,10 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+namespace KappaDuck.Quack.Interop.SDL.Primitives;
+
+internal enum SDL_MouseWheelDirection
+{
+ Normal = 0,
+ Flipped = 1
+}
diff --git a/src/KappaDuck.Quack/Interop/SDL/SDL3.Input.cs b/src/KappaDuck.Quack/Interop/SDL/SDL3.Input.cs
index c3b7392..b376f52 100644
--- a/src/KappaDuck.Quack/Interop/SDL/SDL3.Input.cs
+++ b/src/KappaDuck.Quack/Interop/SDL/SDL3.Input.cs
@@ -8,6 +8,20 @@ namespace KappaDuck.Quack.Interop.SDL;
internal static partial class SDL3
{
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_CaptureMouse")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal static partial bool CaptureMouse([MarshalAs(UnmanagedType.I1)] bool enabled);
+
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_CursorVisible")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal static partial bool CursorVisible();
+
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetGlobalMouseState")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ internal static partial MouseButtonState GetGlobalMouseState(out float x, out float y);
+
[LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetKeyFromScancode")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static partial Key GetKeyFromScancode(Scancode code, Keymod mod, [MarshalAs(UnmanagedType.I1)] bool keyEvents);
@@ -50,6 +64,14 @@ internal static partial class SDL3
[return: MarshalUsing(typeof(SDLStringMarshaller))]
internal static partial string GetMouseNameById(uint id);
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetMouseState")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ internal static partial MouseButtonState GetMouseState(out float x, out float y);
+
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetRelativeMouseState")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ internal static partial MouseButtonState GetRelativeMouseState(out float x, out float y);
+
[LibraryImport(nameof(SDL3), EntryPoint = "SDL_GetScancodeFromKey")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
internal static partial Scancode GetScancodeFromKey(Key key, Keymod* mod);
@@ -70,9 +92,14 @@ internal static partial class SDL3
[LibraryImport(nameof(SDL3), EntryPoint = "SDL_HasMouse")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
- [return: MarshalAs(UnmanagedType.U1)]
+ [return: MarshalAs(UnmanagedType.I1)]
internal static partial bool HasMouse();
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_HideCursor")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal static partial bool HideCursor();
+
[LibraryImport(nameof(SDL3), EntryPoint = "SDL_HasScreenKeyboardSupport")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.I1)]
@@ -94,4 +121,14 @@ internal static partial class SDL3
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.I1)]
internal static partial bool SetScancodeName(Scancode code, Span name);
+
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_ShowCursor")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal static partial bool ShowCursor();
+
+ [LibraryImport(nameof(SDL3), EntryPoint = "SDL_WarpMouseGlobal")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal static partial bool WarpMouseGlobal(float x, float y);
}
diff --git a/tests/Integration.Tests/Events/EventQueueTests.cs b/tests/Integration.Tests/Events/EventQueueTests.cs
deleted file mode 100644
index 141ebfd..0000000
--- a/tests/Integration.Tests/Events/EventQueueTests.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) KappaDuck.
-// Licensed under the MIT license.
-
-using KappaDuck.Quack.Events;
-
-namespace Integration.Tests.Events;
-
-[NotInParallel]
-internal sealed class EventQueueTests
-{
- [Before(Test)]
- public void FlushEvents()
- {
- EventQueue.Pump();
- EventQueue.Flush();
- }
-
- [Test]
- public async Task PeekShouldReturnZeroWhenDestinationIsEmpty()
- {
- Span events = [];
-
- int count = EventQueue.Peek(events);
- await count.Should().BeZero();
- }
-
- [Test]
- public async Task PeekShouldFillDestination()
- {
- Span events = new Event[3];
-
- EventQueue.Push(new QuitRequestedEvent());
- EventQueue.Push(new QuitRequestedEvent());
-
- int count = EventQueue.Peek(events);
-
- await count.Should().BeEqualTo(2);
- }
-
- [Test]
- public async Task PeekOfTypeShouldReturnZeroWhenDestinationIsEmpty()
- {
- Span events = [];
-
- int count = EventQueue.Peek(events);
- await count.Should().BeZero();
- }
-
- [Test]
- public async Task PeekOfTypeShouldFillDestination()
- {
- Span events = new QuitRequestedEvent[3];
-
- EventQueue.Push(new QuitRequestedEvent());
- EventQueue.Push(new QuitRequestedEvent());
-
- int count = EventQueue.Peek(events);
-
- await count.Should().BeEqualTo(2);
- }
-
- [Test]
- public async Task PushShouldReturnZeroWhenDestinationIsEmpty()
- {
- Span events = [];
-
- int count = EventQueue.Push(events);
- await count.Should().BeZero();
- }
-
- [Test]
- public async Task PushShouldPopulateTheQueue()
- {
- Span events = [new QuitRequestedEvent()];
-
- int count = EventQueue.Push(events);
- await count.Should().BeEqualTo(1);
- }
-
- [Test]
- public async Task RetrieveShouldReturnZeroWhenDestinationIsEmpty()
- {
- Span events = [];
-
- int count = EventQueue.Retrieve(events);
- await count.Should().BeZero();
- }
-
- [Test]
- public async Task RetrieveShouldPopulateTheQueue()
- {
- Span events = new Event[3];
-
- EventQueue.Push(new QuitRequestedEvent());
- EventQueue.Push(new QuitRequestedEvent());
-
- int count = EventQueue.Retrieve(events);
- await count.Should().BeEqualTo(2);
- }
-
- [Test]
- public async Task RetrieveOfTypeShouldReturnZeroWhenDestinationIsEmpty()
- {
- Span events = [];
-
- int count = EventQueue.Retrieve(events);
- await count.Should().BeZero();
- }
-
- [Test]
- public async Task RetrieveOfTypeShouldPopulateTheQueue()
- {
- Span events = new QuitRequestedEvent[3];
-
- EventQueue.Push(new QuitRequestedEvent());
- EventQueue.Push(new QuitRequestedEvent());
-
- int count = EventQueue.Retrieve(events);
- await count.Should().BeEqualTo(2);
- }
-}
diff --git a/tests/Integration.Tests/SmokeTests.cs b/tests/Integration.Tests/SmokeTests.cs
new file mode 100644
index 0000000..2bcfb70
--- /dev/null
+++ b/tests/Integration.Tests/SmokeTests.cs
@@ -0,0 +1,13 @@
+// Copyright (c) KappaDuck.
+// Licensed under the MIT license.
+
+namespace Integration.Tests;
+
+internal sealed class SmokeTests
+{
+ [Test]
+ public async Task TrueShouldBeTrue()
+ {
+ await true.Should().BeTrue();
+ }
+}