Skip to content

Commit c566e4b

Browse files
PerkseyHurricanKai
andauthored
Multi-Backend Input for Silk.NET 3.0 (#554)
* Create Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md Co-authored-by: Kai Jellinghaus <[email protected]> * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md Co-authored-by: Kai Jellinghaus <[email protected]>
1 parent d24009b commit c566e4b

File tree

1 file changed

+213
-0
lines changed

1 file changed

+213
-0
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Summary
2+
Proposal API for backend-agnostic, refactored Input via keyboards, mice, and controllers.
3+
4+
# Contributors
5+
- Dylan Perks (@Perksey)
6+
7+
# Current Status
8+
- [x] Proposed
9+
- [ ] Discussed with Working Group
10+
- [ ] Approved
11+
- [ ] Implemented
12+
13+
# Design Decisions
14+
- This proposal refactors the internals of the Input API to be less tied to a single backend, and instead allow multiple backends to be added and removed from an input context at will.
15+
- This proposal aims to keep the look and feel of the Input APIs intended for general consumption similar to that of 2.0.
16+
- This proposal assumes knowledge of the following proposals:
17+
- Windowing 3.0
18+
- Input
19+
- Enhanced Input Events
20+
- This proposal also assumes knowledge of 2.0's Input APIs as it is only a refactor and not a complete redesign of the API as in other proposals. The most noticable differences between the design of Input 2.0 and Input 3.0 are:
21+
- Input no longer has a hard bond to Windowing. The integration will remain the same to the end user but will use Source Generators, more on that later.
22+
- Input contexts are no longer interfaces, they are instead classes which contain input backends.
23+
- Unlike 1.0 and 2.0, all device lists (including those on `InputContext`) will return both connected and disconnected devices when indexed or iterated.
24+
25+
## Reference Implementation
26+
27+
Similar to Windowing 3.0, a reference implementation will be included in the main `Silk.NET.Input` package which uses the same API or family of APIs as Windowing. This will be exposed via the `InputBackend` class. The `InputBackend` class contains a static `Create` method which accepts the native handle for the input context on the particular underlying API i.e. on GLFW this handle represents a `GLFWwindow*`. This may be subject to change.
28+
29+
## Source Generator
30+
31+
Alongside the input package there will be a source generator (shipped in a `Silk.NET.Input.Roslyn` NuGet package, which is referenced by the main input package). This source generator will be very small, but it allows us to create a link between windowing and input at compile time without a hard reference.
32+
33+
If the source generator detects that both `Silk.NET.Input` and `Silk.NET.Windowing` are referenced, code with the following API surface will be injected into the assembly:
34+
```cs
35+
namespace Silk.NET.Input
36+
{
37+
internal static class InputSurfaceExtensions // internal to avoid conflicts with other assemblies
38+
{
39+
public static InputContext CreateInput(this ISurface surface);
40+
}
41+
}
42+
```
43+
44+
The `CreateInput` method will use the surface, obtain its platform data (i.e. by using something like `IGlfwPlatformData`), and then feed that into `InputBackend.Create`. The returned `InputContext` will be configured to have the returned `IInputBackend` as a backend. This method will also appropriately bind the `ISurface.Update` callback:
45+
- The `IInputBackend.Update` method is configured to run in the `ISurface.Update` callback.
46+
- The `IInputBackend.Disposing` event will be bound such that the `IInputBackend.Update` method is unbound from `ISurface.Update` event callback.
47+
48+
The API surface for this is defined later in the Proposed API section.
49+
50+
One instance of an `IInputBackend` can only belong to one `InputContext` throughout the entirety of the `IInputBackend`'s lifetime. Once a `IInputBackend` is `Add`ed to an `InputContext`, the `InputContext` takes full ownership of the `IInputBackend` and, as a result, will `Dispose` the `IInputBackend` when it is `Remove`d or the context itself is `Dispose`d. If multiple `InputContext`s are in use, multiple `IInputBackend` instances must also be used. `IInputBackend` will throw an exception if its APIs are used after the backend has been disposed.
51+
52+
**KEY POINT FOR WORKING GROUP**: The Windowing-Input integration mandates the use of source generators. Is this ok?
53+
54+
# Proposed API
55+
56+
```diff
57+
namespace Silk.NET.Input
58+
{
59+
public interface IInputDevice
60+
{
61+
- int Index { get; }
62+
+ /// <summary>
63+
+ /// The backend-specific, device-type-specific identifier for this device.
64+
+ /// </summary>
65+
+ /// <remarks>
66+
+ /// For example, no gamepad may share a DeviceId with another gamepad (same applies for all other device types) on that individual backend.
67+
+ /// This property should not be used as a globally unique identifier.
68+
+ /// </remarks>
69+
+ int DeviceId { get; }
70+
}
71+
}
72+
```
73+
74+
Index has been removed in favour of DeviceId, as Index implies its globally unique and also tied to the index of the device in the list in which it's contained. The reason why this is not globally unique is primarily to allow users to do their own interop with the native backend using our high-level representations of the devices (i.e. DeviceId will be the GLFW joystick ID)
75+
76+
```diff
77+
namespace Silk.NET.Input
78+
{
79+
- public interface IInputPlatform
80+
- {
81+
- bool IsApplicable(IView view);
82+
- IInputContext CreateInput(IView view);
83+
- }
84+
}
85+
```
86+
87+
```diff
88+
namespace Silk.NET.Input
89+
{
90+
+ /// <summary>
91+
+ /// Encapsulates input devices sourced from input backends.
92+
+ /// </summary>
93+
- public interface IInputContext : IDisposable
94+
+ public sealed class InputContext : IDisposable
95+
{
96+
- nint Handle { get; }
97+
IReadOnlyList<IGamepad> Gamepads { get; }
98+
IReadOnlyList<IJoystick> Joysticks { get; }
99+
IReadOnlyList<IKeyboard> Keyboards { get; }
100+
IReadOnlyList<IMouse> Mice { get; }
101+
IReadOnlyList<IInputDevice> OtherDevices { get; }
102+
+
103+
+ /// <summary>
104+
+ /// Gets a list of backends from which all input devices on this input context are sourced.
105+
+ /// </summary>
106+
+ IReadOnlyList<IInputBackend> Backends { get; }
107+
event Action<IInputDevice, bool>? ConnectionChanged;
108+
+
109+
+ /// <summary>
110+
+ /// Adds the given backend to the list of <see cref="Backends" />.
111+
+ /// </summary>
112+
+ /// <remarks>
113+
+ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend when
114+
+ /// <see cref="Remove" /> is called or this input context is <see cref="Dispose" />d.
115+
+ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead.
116+
+ /// </remarks>
117+
+ void Add(IInputBackend backend);
118+
+
119+
+ /// <summary>
120+
+ /// Removes the given backend from the list of <see cref="Backends" />.
121+
+ /// </summary>
122+
+ /// <remarks>
123+
+ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend.
124+
+ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead.
125+
+ /// </remarks>
126+
+ void Remove(IInputBackend backend);
127+
+
128+
+ /// <summary>
129+
+ /// Updates all input data on all backends.
130+
+ /// </summary>
131+
+ void Update();
132+
}
133+
}
134+
```
135+
136+
```diff
137+
namespace Silk.NET.Input
138+
{
139+
+ /// <summary>
140+
+ /// Represents an input backend from which input devices can be retrieved.
141+
+ /// </summary>
142+
+ /// <remarks>
143+
+ /// This interface is not intended for general consumption. Consider <see cref="InputContext" /> instead.
144+
+ /// </remarks>
145+
+ public interface IInputBackend : IDisposable
146+
+ {
147+
+ /// <summary>
148+
+ /// Gets all devices of the given type recognised by this backend.
149+
+ /// </summary>
150+
+ /// <remarks>
151+
+ /// The <typeparamref name="T" /> type parameter must be the last interface in the inheritance heirarchy (the "device type")
152+
+ /// i.e. it must be a device type like <see cref="IJoystick" /> and not something like <see cref="IInputDevice" />.
153+
+ /// The behaviour of using a T that isn't a device type recognised by the backend is undefined.<br />
154+
+ /// <br />
155+
+ /// This method is not intended for general consumption. Consider <see cref="InputContext" /> instead.
156+
+ /// </remarks>
157+
+ IReadOnlyList<IInputDevice> GetDevices<T>() where T : IInputDevice;
158+
+
159+
+ /// <summary>
160+
+ /// Raised when an input device is connected or disconnected.
161+
+ /// </summary>
162+
+ event Action<IInputDevice, bool>? ConnectionChanged;
163+
+
164+
+ /// <summary>
165+
+ /// Occurs when the backend is disposing. This can be used as a good indicator that this backend is being removed from an input context.
166+
+ /// </summary>
167+
+ event Action Disposing;
168+
+
169+
+ /// <summary>
170+
+ /// Updates this backend's input data.
171+
+ /// </summary>
172+
+ /// <remarks>
173+
+ /// Input events and changes are permitted to occur outside of this method.
174+
+ /// </remarks>
175+
+ void Update();
176+
+ }
177+
}
178+
```
179+
180+
```diff
181+
namespace Silk.NET.Input
182+
{
183+
- public static class InputWindowExtensions
184+
- {
185+
- // ...
186+
- }
187+
}
188+
```
189+
190+
```diff
191+
namespace Silk.NET.Input
192+
{
193+
+ /// <summary>
194+
+ /// Represents the out-of-the-box input backend, which uses a native API to retrieve input backends using a native handle.
195+
+ /// </summary>
196+
+ /// <remarks>
197+
+ /// On desktop, this uses GLFW.
198+
+ /// </remarks>
199+
+ public static class InputBackend
200+
+ {
201+
+ /// <summary>
202+
+ /// Creates an instance of the out-of-the-box input backend, which uses a native API to retrieve input backends using
203+
+ /// the given native handle.
204+
+ /// </summary>
205+
+ /// <param name="handle">The native handle created/sourced from the underlying native API used by this input backend.</param>
206+
+ /// <remarks>
207+
+ /// This method is implicitly called by the Windowing-Input integration. <br />
208+
+ /// On desktop, this uses GLFW. The handle refers to a <c>GLFWwindow*</c> if GLFW is in use.
209+
+ /// </remarks>
210+
+ public static unsafe IInputBackend Create(void* handle);
211+
+ }
212+
}
213+
```

0 commit comments

Comments
 (0)