Skip to content

Commit

Permalink
♻️ Continue shaping features for customizable buttons.
Browse files Browse the repository at this point in the history
  • Loading branch information
hexawyz committed Feb 23, 2025
1 parent 734ba31 commit 149479d
Showing 1 changed file with 41 additions and 182 deletions.
223 changes: 41 additions & 182 deletions src/Exo/Core/Exo.Core/Features/UserInputFeatures.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using DeviceTools.HumanInterfaceDevices;

namespace Exo.Features.UserInput;

/// <summary>Exposes the remappable buttons on the device.</summary>
public interface IRemappableButtonsFeature : IUserInputDeviceFeature
{
ImmutableArray<RemappableButtonDefinition> Buttons { get; }
// TODO: Actions should have parameters. Mayve GUID is not the best way to represent them? (Main point of GUID is that they the ID space is large and they can be shared for reuse when needed.)
void SetAction(ButtonId buttonId, Guid actionId);
void ResetButton(ButtonId buttonId);
ValueTask ApplyChangesAsync(CancellationToken cancellationToken);
}

/// <summary>On devices supporting button interception, this provides button events for intercepted buttons.</summary>
Expand All @@ -15,198 +21,21 @@ public interface IInterceptedButtonsFeature : IUserInputDeviceFeature
event ButtonEventHandler ButtonUp;
}

public delegate void ButtonEventHandler(Driver driver, ushort button);
public delegate void ButtonEventHandler(Driver driver, ButtonId button);

public readonly struct RemappableButtonDefinition
{
/// <summary>The button ID.</summary>
/// <remarks>
/// <para>
/// When possible, well-know IDs from <see cref="WellKnownInputButton"/> should be used.
/// This will allow UIs to have more sensible defaults for displaying informations on screen.
/// </para>
/// <para>
/// When it is necessary to define custom buttons, IDs above <c>0x1000</c> should be used.
/// This allows preserving space for up to 4096 common button that will be able to use a shared definition.
/// </para>
/// </remarks>
public ushort ButtonId { get; }
/// <summary>The ID used to reference the button.</summary>
public ButtonId ButtonId { get; }

/// <summary>The capabilities of the button.</summary>
public ButtonCapabilities Capabilities { get; }

/// <summary>If this button has a customizable screen, this is the embedded monitor ID associated with it through <see cref="EmbeddedMonitors.IEmbeddedMonitorControllerFeature"/>.</summary>
public Guid EmbeddedMonitorId { get; }
}

/// <summary>Defines input keys.</summary>
/// <remarks>
/// This enum contains well-known, shared IDs for buttons.
/// Values starting at <c>0x1000</c> are to be used for custom, device-specific buttons.
/// </remarks>
public enum WellKnownInputButton : ushort
{
// Most common mouse buttons. Those buttons all have a relatively well defined behavior on Windows a least.
LeftMouseButton = 1,
RightMouseButton,
MiddleMouseButton,
MouseButton4,
MouseButton5,

// Modifier Keys
LeftShift,
LeftControl,
LeftAlt,
LeftWindows,
RightShift,
RightControl,
RightAlt,
RightWindows,

// Context Menu Key
Application,

// Standard Lock Keys
ScrollLock,
CapsLock,
NuLock,

Return,
Escape,
Delete,
Tab,
Spacebar,

PrintScreen,
Pause,
Insert,
Home,
End,
PageUp,
PageDown,
DeleteForward,
RightArrow,
LeftArrow,
DownArrow,
UpArrow,

// Function keys. HID lists up to 24 Function Keys, so we will stop at there for now.
// Most keyboards will stop at F12. Apple Keyboards usually go up to F19.
// Programs generally don't have any shortcut associated by default after F12.
Function1,
Function2,
Function3,
Function4,
Function5,
Function6,
Function7,
Function8,
Function9,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
Function21,
Function22,
Function23,
Function24,

// Standard alphabet keys.
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,

// Number keys
Number0,
Number1,
Number2,
Number3,
Number4,
Number5,
Number6,
Number7,
Number8,
Number9,

// Equivalent to Windows VK_OEM_ codes. With addition of the HID-defined alternative definition for OEM 7.
Oem1,
Oem2,
Oem3,
Oem4,
Oem5,
Oem6,
Oem7,
Oem7NonUs,
Oem8,
Oem102,

// Numpad Keys
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
NumpadDecimalSeparator,
NumpadAdd,
NumpadSubtract,
NumpadMultiply,
NumpadDivide,
NumpadEqual,
NumpadDelete,
NumpadEnter,

// Extra mouse buttons up to 20.
// As far as I know, it is unlikely for a mouse to have more than 18 buttons. Hopefully, 20 is large enough of a limit.
MouseButton6,
MouseButton7,
MouseButton8,
MouseButton9,
MouseButton10,
MouseButton11,
MouseButton12,
MouseButton13,
MouseButton14,
MouseButton15,
MouseButton16,
MouseButton17,
MouseButton18,
MouseButton19,
MouseButton20,
/// <summary>Gets the (custom) actions that are allowed to be bound to the button.</summary>
public ImmutableArray<Guid> AllowedActions { get; }
}

/// <summary>Defines actions that can be common to multiple devices.</summary>
Expand All @@ -224,3 +53,33 @@ public enum ButtonCapabilities : ushort
IsInterceptable = 0b_00000000_00000010,
HasMonitor = 0b_00000000_00000100,
}

// Instead of defining yet another lis tof IDs, we will just rely on HID usages to define buttons.
// The only caveat is that we need to support devices having multiple buttons mapped to the same HID usage. (Which should be pretty rare but NOT impossible)
// For that, an easy solution is to add an extra instance ID to discriminate among buttons.
// For buttons that are 100% custom, with no default meaning associated with them, drivers can just assign arbitrary IDs in the vendor-defined HID Usage Pages (0xFF00 to 0xFFFF).
// ⚠️ We don't care at if the HID usages referenced here do not map to actual hardware implementation. HID usages are just a means to an end, which is to know what is the intended purpose of a button.
[StructLayout(LayoutKind.Sequential, Pack = 2, Size = 12)]
public readonly struct ButtonId
{
/// <summary>Gets the HID usage page associated with this button.</summary>
public HidUsagePage ButtonUsagePage { get; }
/// <summary>Gets the HID usage associated with this button in the page specified by <see cref="ButtonUsagePage"/>.</summary>
/// <remarks>
/// <para>
/// The usage represented by <see cref="ButtonUsagePage"/> and <see cref="ButtonUsage"/> must reflect the standard behavior of the button on the device, if there is any.
/// Otherwise, an arbitrary custom ID from any of the vendor-defined pages must be used.
/// </para>
/// <para>
/// In some situations, multiple buttons may be associated with the same HID usage.
/// This scenario is handled by assigning increasing instance indices to each button.
/// </para>
/// </remarks>
public ushort ButtonUsage { get; }
/// <summary>Gets an index associated with the button for the specified usage.</summary>
/// <remarks>
/// In most cases, the value returned must be <c>0</c>.
/// If two or more buttons are associated with the same usage, each button must have a different index assigned, starting at 0.
/// </remarks>
public ushort ButtonUsageInstanceIndex { get; }
}

0 comments on commit 149479d

Please sign in to comment.