Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f5b5f1c
Start work on documenting the API
Perksey Feb 15, 2025
4888320
Finish documenting API & split out files
Perksey Feb 22, 2025
2585ede
Add InputMarshal and InputContextDeviceList, lay out SDL implementation
Perksey Mar 15, 2025
2080e1b
Start on SdlInputBackend
Perksey Apr 5, 2025
f1880f0
Push latest work on SDL backend
Perksey May 25, 2025
1d16317
Remove types I didn't use in the end
Perksey May 25, 2025
e497dde
begin mucking about input event processing
domportera Jun 22, 2025
f129ae9
refine internal device creation abstractions
domportera Jun 24, 2025
fcf49f6
continued - move name back where it belongs
domportera Jun 24, 2025
33cc20d
cont'd
domportera Jun 29, 2025
e72bf55
comments/clarity + enum helper class
domportera Jul 4, 2025
3e4e168
simplify enum assistant and provide guaranteed sorted enum values
domportera Jul 4, 2025
bce7136
simpler indexing
domportera Jul 4, 2025
8f00a4b
add support for enums with multiple names for the same value
domportera Jul 4, 2025
bf382bc
optimize enum performance for larger enums
domportera Jul 5, 2025
555275b
clarifying comment
domportera Jul 5, 2025
fb889c6
documentation/cleanup enum class
domportera Jul 5, 2025
021a8c9
begin constructing gamepads via the joystick API
domportera Jul 6, 2025
8e7eda4
some experimentation/progress re: axis/button indexing
domportera Aug 9, 2025
e637ba7
joystick/gamepad abstraction progress, expand rumble system
domportera Aug 28, 2025
d0f05aa
cont'd
domportera Aug 29, 2025
7942c92
start attempting to guarantee unique ids
domportera Sep 1, 2025
7e84c15
move backendextensions to its own file, begin keyboard
domportera Sep 6, 2025
8c5f3da
start key conversions
domportera Sep 6, 2025
e20d640
tweak device id abstraction
domportera Sep 6, 2025
4387adb
begin keyboard text handling
domportera Sep 7, 2025
35677b0
improve text retrieval functions
domportera Sep 7, 2025
e0abc15
continue text recorder
domportera Sep 7, 2025
c7159ad
begin integration with sdl native text entry
domportera Sep 12, 2025
436aa72
add span utility
domportera Sep 14, 2025
a35120a
text entry continued
domportera Sep 14, 2025
67df00f
additional modifier utility methods
domportera Sep 14, 2025
dd81078
create world's weakest fallback key -> character converter
domportera Sep 14, 2025
0ba7c09
add joystick interface to gamepad, invoke id refresh
domportera Sep 14, 2025
ec0e48b
increase number of characters supported
domportera Sep 14, 2025
7983462
organization
domportera Sep 14, 2025
e263b9b
constrained usage of special enum-as-indexes functionality via attrib…
domportera Sep 14, 2025
4d77d20
mark method static
domportera Sep 14, 2025
116ec2d
begin pointer logic - mouse
domportera Sep 21, 2025
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
7 changes: 4 additions & 3 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.29.2",
"version": "1.0.1",
"commands": [
"dotnet-csharpier"
]
"csharpier"
],
"rollForward": false
}
}
}
20 changes: 20 additions & 0 deletions Silk.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windowing", "Windowing", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Windowing", "sources\Windowing\Windowing\Silk.NET.Windowing.csproj", "{EF07CBB5-D253-4CA9-A5DA-8B3DF2B0DF8E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Input", "Input", "{33ED9765-8C36-4A9D-95E8-AF037FE104B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Input", "sources\Input\Input\Silk.NET.Input.csproj", "{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Input", "Input", "{4E0EF53A-76BC-4729-8E3B-4768E86E357E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Input.UnitTests", "tests\Input\Input\Silk.NET.Input.UnitTests.csproj", "{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -168,6 +176,14 @@ Global
{EF07CBB5-D253-4CA9-A5DA-8B3DF2B0DF8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF07CBB5-D253-4CA9-A5DA-8B3DF2B0DF8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF07CBB5-D253-4CA9-A5DA-8B3DF2B0DF8E}.Release|Any CPU.Build.0 = Release|Any CPU
{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA}.Release|Any CPU.Build.0 = Release|Any CPU
{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -200,6 +216,10 @@ Global
{F16C0AB9-DE7E-4C09-9EE9-DAA8B8E935A6} = {EC4D7B06-D277-4411-BD7B-71A6D37683F0}
{FE4414F8-5370-445D-9F24-C3AD3223F299} = {DD29EA8F-B1A6-45AA-8D2E-B38DA56D9EF6}
{EF07CBB5-D253-4CA9-A5DA-8B3DF2B0DF8E} = {FE4414F8-5370-445D-9F24-C3AD3223F299}
{33ED9765-8C36-4A9D-95E8-AF037FE104B3} = {DD29EA8F-B1A6-45AA-8D2E-B38DA56D9EF6}
{49A42CE3-94C5-4239-B0FC-F1FF8D7AAADA} = {33ED9765-8C36-4A9D-95E8-AF037FE104B3}
{4E0EF53A-76BC-4729-8E3B-4768E86E357E} = {A5578D12-9E77-4647-8C22-0DBD17760BFF}
{00B9B6E6-776E-480C-B3ED-D6420C5B4E8E} = {4E0EF53A-76BC-4729-8E3B-4768E86E357E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {78D2CF6A-60A1-43E3-837B-00B73C9DA384}
Expand Down
21 changes: 21 additions & 0 deletions docs/silk.net/diagnostics/ST0001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ST0001 - ProcessClass failure

## Overview

This internal error was raised by SilkTouch when failing to generate an implementation for a binding at source
generation time. It provided details regarding the exception that led to the entire native API class failing to have its
implementation generated.

| Attribute | Value |
|--------------------|----------------------|
| Diagnostic ID | ST0001 |
| Title | ProcessClass failure |
| Category | SilkTouch.Internal |
| Default Severity | Error |
| Enabled by Default | Yes |

Example message: `ProcessClass failed. Exception: '...'`

## Explanation & Solutions

This functionality is no longer supported in 3.0, where this diagnostic is never raised.
21 changes: 21 additions & 0 deletions docs/silk.net/diagnostics/ST0002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ST0002 - MethodClass failure

## Overview

This internal error was raised by SilkTouch when failing to generate an implementation for a binding at source
generation time. It provided details regarding the exception that led to a specific native API method failing to have
its implementation generated.

| Attribute | Value |
|--------------------|---------------------|
| Diagnostic ID | ST0002 |
| Title | MethodClass failure |
| Category | SilkTouch.Internal |
| Default Severity | Error |
| Enabled by Default | Yes |

Example message: `MethodClass failed. Exception: '...'`

## Explanation & Solutions

This functionality is no longer supported in 3.0, where this diagnostic is never raised.
20 changes: 20 additions & 0 deletions docs/silk.net/diagnostics/ST0003.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ST0003 - Silk.NET.Core is Missing

## Overview

This internal diagnostic was raised by SilkTouch when failing to generate an implementation for bindings at source
generation time due to the binding project missing a reference to Silk.NET.Core.

| Attribute | Value |
|--------------------|---------------------|
| Diagnostic ID | ST0003 |
| Title | MethodClass failure |
| Category | SilkTouch.Internal |
| Default Severity | Info |
| Enabled by Default | Yes |

Example message: `Silk.NET.Core is missing from references. You should use SilkTouch with Silk.NET.Core`

## Explanation & Solutions

This functionality is no longer supported in 3.0, where this diagnostic is never raised.
20 changes: 20 additions & 0 deletions docs/silk.net/diagnostics/ST0004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ST0004 - Build Info

## Overview

This internal diagnostic was raised by SilkTouch when configured to do so. It provided diagnostic information relating
to the performance and characteristics of SilkTouch's internals.

| Attribute | Value |
|--------------------|--------------------|
| Diagnostic ID | ST0004 |
| Title | Build Info |
| Category | SilkTouch.Internal |
| Default Severity | Warning |
| Enabled by Default | Yes |

Example message: `GCSlotCount: '127'. Time: '6437ms'`

## Explanation & Solutions

This functionality is no longer supported in 3.0, where this diagnostic is never raised.
15 changes: 15 additions & 0 deletions docs/silk.net/diagnostics/ST0005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ST0005 - Intentionally Unstable API

## Overview

This diagnostic is raised when trying to use a Silk.NET API that has been marked with the `Experimental` attribute due
to its API and/or ABI being unstable. When this diagnostic ID is used, it indicates that it is intentional that this is
the case and that this API is extremely unlikely to ever graduate to a stable, versioned API.

## Explanation & Solutions

Typically, APIs meeting this description are internal APIs and are not intended for use outside of the assembly they're
defined in. As a result, where this diagnostic is raised, you should cease use of this API or at least only continue if
you can guarantee that you will never update Silk.NET ever again and that your downstream consumers, if applicable, will
lock their version to the same version referenced by your project. However, please reconsider use of the API if this is
the case.
6 changes: 6 additions & 0 deletions eng/build/Silk.NET.NUKE.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@
<PackageReference Include="Octokit" Version="13.0.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<Content Include="..\..\tests\Input\Input\Silk.NET.Input.UnitTests.csproj">
<Link>Directory.Build\tests\Input\Input\Silk.NET.Input.UnitTests.csproj</Link>
</Content>
</ItemGroup>
</Project>
38 changes: 38 additions & 0 deletions sources/Core/Core/Pointers/PointerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -232,6 +233,43 @@ public static string ReadToString(this Ptr<sbyte> @this)
}
}

/// <summary>
/// Populates the given span with the characters of this <see cref="Ptr{T}"/> as a c-style string.
/// </summary>
/// <param name="this"></param>
/// <param name="span">The span to populate characters into</param>
/// <returns>True if the given span is of sufficient length and can be filled - false otherwise, in which case
/// no data has been modified in the given span</returns>
public static bool TryReadToSpan(this Ptr<sbyte> @this, ref Span<char> span)
{
fixed (void* raw = @this)
{
var bytes = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)raw);
var count = Encoding.UTF8.GetCharCount(bytes);
if (span.Length < count)
{
return false;
}

#if DEBUG
// This if-def is here to prevent this constant string from taking up space in extremely constrained
// release environments.
const string assertionLog = $"{nameof(Encoding)}.{nameof(Encoding.UTF8)}." +
$"{nameof(Encoding.UTF8.GetChars)}) returned an unexpected number of " +
$"characters";

var charCount = Encoding.UTF8.GetChars(bytes, span);
Debug.Assert(charCount == count, assertionLog);;
#else
Encoding.UTF8.GetChars(bytes, span);
#endif

span = span[..count];
return true;
}
}


/// <summary>
/// Creates a string from this <see cref="Ptr{T}"/> with the given length
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ static Silk.NET.Core.PointerExtensions.ReadToStringArray(this Silk.NET.Core.Ref3
static Silk.NET.Core.PointerExtensions.ReadToStringArray(this Silk.NET.Core.Ref3D<short> this, int length, int[]! lengths) -> string?[]?[]?
static Silk.NET.Core.PointerExtensions.ReadToStringArray(this Silk.NET.Core.Ref3D<uint> this, int length, int[]! lengths) -> string?[]?[]?
static Silk.NET.Core.PointerExtensions.ReadToStringArray(this Silk.NET.Core.Ref3D<ushort> this, int length, int[]! lengths) -> string?[]?[]?
static Silk.NET.Core.PointerExtensions.TryReadToSpan(this Silk.NET.Core.Ptr<sbyte> this, ref System.Span<char> span) -> bool
static Silk.NET.Core.Ptr.explicit operator nint(Silk.NET.Core.Ptr ptr) -> nint
static Silk.NET.Core.Ptr.explicit operator Silk.NET.Core.Ptr(nint ptr) -> Silk.NET.Core.Ptr
static Silk.NET.Core.Ptr.explicit operator Silk.NET.Core.Ptr(Silk.NET.Core.Ref ptr) -> Silk.NET.Core.Ptr
Expand Down
24 changes: 24 additions & 0 deletions sources/Input/Input/Button.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Silk.NET.Input;

/// <summary>
/// Represents a button the user can push.
/// </summary>
/// <param name="Name">The name of the button.</param>
/// <param name="IsDown">Whether the user is pushing the button.</param>
/// <param name="Pressure">
/// The pressure with which the user is pushing the button, where <c>0.0</c> is the smallest measurable pressure and
/// <c>1.0</c> is the largest measurable pressure.
/// </param>
/// <typeparam name="T">
/// The button type (e.g. <see cref="JoystickButton"/>, <see cref="PointerButton"/>, etc).
/// </typeparam>
public readonly record struct Button<T>(T Name, bool IsDown, float Pressure)
where T : unmanaged, Enum
{
/// <summary>
/// Collapses this <see cref="Button{T}"/> struct into just its <see cref="IsDown"/> value.
/// </summary>
/// <param name="state">The button state.</param>
/// <returns>The <see cref="IsDown"/> value.</returns>
public static implicit operator bool(Button<T> state) => state.IsDown;
}
21 changes: 21 additions & 0 deletions sources/Input/Input/ButtonChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Diagnostics;

namespace Silk.NET.Input;

/// <summary>
/// Contains information pertaining to a button state change (e.g. press, depress, etc).
/// </summary>
/// <param name="Device">The device on which the button being pressed or depressed resides.</param>
/// <param name="Timestamp">
/// The timestamp (as retrieved from <see cref="Stopwatch.GetTimestamp"/>) at which the event occurred.
/// </param>
/// <param name="Button">The new state of the button being pressed or depressed.</param>
/// <param name="Previous">The previous state of the button.</param>
/// <typeparam name="T">The button type e.g. <see cref="JoystickButton"/>, <see cref="PointerButton"/>, etc.</typeparam>
public readonly record struct ButtonChangedEvent<T>(
IButtonDevice<T> Device,
long Timestamp,
Button<T> Button,
Button<T> Previous
)
where T : unmanaged, Enum;
47 changes: 47 additions & 0 deletions sources/Input/Input/ButtonReadOnlyList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections;

namespace Silk.NET.Input;

/// <summary>
/// An implementation of <see cref="IReadOnlyList{T}"/> providing utility APIs for getting a <see cref="Button{T}"/>
/// given a button name <typeparamref name="T"/>, that is optimised for storing <see cref="Button{T}"/>s with the
/// given button name type <typeparamref name="T"/> using the most memory-efficient mechanism available.
/// </summary>
/// <typeparam name="T">
/// The button type (e.g. <see cref="JoystickButton"/>, <see cref="PointerButton"/>, etc).
/// </typeparam>
public readonly record struct ButtonReadOnlyList<T> : IReadOnlyList<Button<T>>
where T : unmanaged, Enum
{
private readonly Func<int, int> _indexMap;
private readonly IReadOnlyList<Button<T>> _list;

/// <summary>
/// A constructor for an input list that takes in:
/// </summary>
/// <param name="buttonList">A list of buttons that will be indexed</param>
/// <param name="indexMap">A pre-built mapping function, if required,
/// used for iterating through the button list in order, regardless of the backend's internal button order.</param>
public ButtonReadOnlyList(IReadOnlyList<Button<T>> buttonList, Func<int, int>? indexMap = null)
{
_list = buttonList;
_indexMap = indexMap ?? (i => i);
}

/// <summary>
/// Gets the state for the button with the given name.
/// </summary>
/// <param name="name">The button name.</param>
public Button<T> this[T name] => _list[EnumInfo<T>.ValueIndexOf(name)];

/// <inheritdoc />
public IEnumerator<Button<T>> GetEnumerator() => _list.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

/// <inheritdoc />
public int Count => _list.Count;

/// <inheritdoc />
public Button<T> this[int index] => _list[_indexMap(index)];
}
13 changes: 13 additions & 0 deletions sources/Input/Input/ConnectionEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Diagnostics;

namespace Silk.NET.Input;

/// <summary>
/// Contains information pertaining to a device connection or disconnection event.
/// </summary>
/// <param name="Device">The device that has disconnected or connected.</param>
/// <param name="Timestamp">
/// The timestamp (as retrieved from <see cref="Stopwatch.GetTimestamp"/>) at which the event occurred.
/// </param>
/// <param name="IsConnected">Whether the device has connected (<c>true</c>) or disconnected (<c>false</c>).</param>
public readonly record struct ConnectionEvent(IInputDevice Device, long Timestamp, bool IsConnected);
57 changes: 57 additions & 0 deletions sources/Input/Input/CursorModes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace Silk.NET.Input;

/// <summary>
/// Enumerates the modes in which a mouse cursor can operate.
/// </summary>
/// <remarks>
/// <see cref="IPointerDevice"/> implementations for <see cref="IMouse"/> implementations typically have two
/// <see cref="IPointerDevice.Targets"/>:
/// <list type="bullet">
/// <item>
/// <term>Bounded <see cref="IPointerTarget"/></term>
/// <description>
/// An <see cref="IPointerTarget"/> that is bounded to the desktop environment i.e. the
/// <see cref="IPointerTarget.Bounds"/> are not infinite and reflect the total screen space that is available to the
/// running application in window coordinates. This is typically the sum of all monitor resolutions, with the positions
/// being defined using an implementation-defined mechanism. The window bounds operate in this same coordinate space.
/// It is highly unlikely that you will be unable to determine the individual points for multiple mice on this target,
/// as desktop environments typically aggregate all movement from all mice into a single <see cref="TargetPoint"/>.
/// This target is used for every cursor mode except <see cref="Unbounded"/>.
/// </description>
/// </item>
/// <item>
/// <term>Unbounded <see cref="IPointerTarget"/></term>
/// <description>
/// An <see cref="IPointerTarget"/> that is unbounded and operates in an arbitrary coordinate space. This target is used
/// for <b>raw mouse mode</b> and points on this target represent the net mouse movement from a mouse. Implementations
/// are more likely to be able to give multiple <see cref="TargetPoint"/>s for each mouse when this target is used. This
/// target is used when the <see cref="Unbounded"/> cursor mode is enabled. <see cref="IPointerTarget.Bounds"/> will
/// represent an infinitely large unbounded target.
/// </description>
/// </item>
/// </list>
/// </remarks>
[Flags]
public enum CursorModes
{
/// <summary>
/// The cursor is visible to the user and operating within the bounds of the <b>desktop environment</b>. The
/// coordinates received are in desktop coordinates, operating in the same coordinate space as the window
/// position/size.
/// </summary>
Normal = 1 << 0,

/// <summary>
/// The cursor is visible to the user but is constrained to the <b>window's client area</b>. The coordinates
/// received are in desktop coordinates, operating in the same coordinate space as the window position/size.
/// The <see cref="IPointerTarget"/> bounded to the desktop environment is used.
/// </summary>
Confined = 1 << 1,

/// <summary>
/// The cursor is invisible to the user and is <b>unconstrained/unbounded</b>. The coordinates received are
/// arbitrary values that have no bounds representing the net mouse movement since entering into this cursor mode.
/// The unbounded <see cref="IPointerTarget"/> is used. This is the equivalent of <b>raw mouse mode</b>.
/// </summary>
Unbounded = 1 << 2,
}
Loading
Loading