Skip to content

Wip/keyboard and mouse#7

Draft
RobertGiesecke wants to merge 39 commits into
mainfrom
wip/keyboard-and-mouse
Draft

Wip/keyboard and mouse#7
RobertGiesecke wants to merge 39 commits into
mainfrom
wip/keyboard-and-mouse

Conversation

@RobertGiesecke

Copy link
Copy Markdown
Owner

No description provided.

Introduce HID-based Key and MouseButton value types (with curated NamedKey /
NamedMouseButton enums and a closed OutputMouseButton), their binding records,
and an empty IInputDevice marker that IJoystickDevice now extends. Keyboard and
Mouse get their own sub-namespaces. No backend or routing yet.
New PressKey/ReleaseKey/PressMouseButton/ReleaseMouseButton macro actions drive
an injected IInputSynthesizer directly, bypassing the vJoy output-button refcount
path. The synthesizer is optional on the runtime (exposed via IRuntimeContext,
flushed once per frame); a key/mouse action without one fails fast at build.
Includes an in-memory FakeInputSynthesizer and tests.
WindowsInputSynthesizer dispatches keys by HID usage page: keyboard-page keys go
through a scancode emitter (what games read), consumer-page keys through a
virtual-key emitter; mouse buttons map onto SendInput mouse flags. It is the
platform default via a new IOutputDeviceWithFactory.DefaultInputSynthesizer that
VJoyDevice overrides, threaded through the console build options.
Replace the FrozenDictionary lookups in the scancode and consumer-key emitters
with switch expressions — faster for these small fixed sets and no per-process
build cost. Rename the INPUT union field to Union and use target-typed new.
Emitters become pure builders (TryBuild -> INPUT?) and the synthesizer sends
through an injected delegate, defaulting to real SendInput. Tests capture the
INPUT structs to assert the scancode/VK tables, up/down + extended flags, page
dispatch and mouse flags without driving the OS, via InternalsVisibleTo.
Move Key/NamedKey/MouseButton and their bindings plus IInputSynthesizer out of
InputAbstractions into a new standalone SharpSticks.InputSynthesis project with no
dependencies, so keyboard/mouse can be used without any joystick coupling.
InputAbstractions now references it; the Windows backend references it directly
instead of InputAbstractions.
Brings the merge extensions alongside IMergeableObject. Shared takes the
Collections.Pooled reference (now transitive into InputAbstractions, which drops
its direct one) and a GlobalUsings, so the moved file and the other Shared types
shed their explicit usings.
Mirror the Windows backend with a uinput-backed IInputSynthesizer that dispatches
keys by HID usage page to evdev KEY_* codes and mouse buttons to BTN_*, wired as
the Linux platform default and tested via an injectable event sink. Add
IInputSynthesizer.EnsureInitialized, called at runtime start (with a build
opt-out), so the uinput device is created up front rather than on first keypress;
Windows and the fakes are no-ops.
…xpression for improved performance and zero allocation.
Move the platform synthesizer onto IOutputDeviceFactory.InputSynthesizer (vJoy
=> SendInput, uinput => uinput; combined factory forwards; fakes inject). The
runtime builder takes an explicit synthesizer if given, else the factory used.
Retire the now-redundant static IOutputDeviceWithFactory.DefaultInputSynthesizer.
This was linked to issues Jun 27, 2026
Route a joystick axis to pointer velocity via AxisToMouseRoute / RouteToMouse,
reusing the AxisBinding + modifier pipeline. Each frame integrates value times
sensitivity times elapsed seconds with a sub-pixel remainder and combines X and Y
into one MoveMouseRelative. Adds MoveMouseRelative to IInputSynthesizer (Windows
SendInput, Linux uinput EV_REL); absolute movement throws until implemented.
Route right stick axes to mouse X/Y movement and bind thumbstick press/release to left mouse button actions. Rename related button for clarity.
Route an input button to a synthesized mouse button (held while the source is
held) via ButtonToMouseRoute / RouteToMouse. The runtime ORs sources per button,
edge-detects, and drives MouseButtonDown/Up on the synthesizer, committed on the
per-frame flush — outside the vJoy output-button refcount.
…Fast` for optimized zero-allocation axis, key, and mouse button enum lookups.
…urceKey` record struct for improved clarity and type safety
@RobertGiesecke RobertGiesecke force-pushed the wip/keyboard-and-mouse branch from be9d862 to 2bf2002 Compare June 28, 2026 11:43
- Introduced `CoverageExclude.Undeterministic.props` and `.cs` to mark code paths that cannot be tested deterministically.
- Applied `[ExcludeFromCodeCoverage]` where required across the codebase.
- Added global using directive for `System.Diagnostics.CodeAnalysis`.
- Removed unused or unnecessary `using` directives for improved clarity.
- Introduce comprehensive black-box tests covering formatting behavior for `NumberFormattingDebugInterpolatedStringHandler`.
- Validate handling of numeric types, strings, Guids, DateTimes, enums, literals, alignment, and nested handlers.
- Ensure edge cases like oversized formats and the `Clear` method are tested thoroughly.
@RobertGiesecke RobertGiesecke force-pushed the wip/keyboard-and-mouse branch from 2bf2002 to 957efed Compare June 28, 2026 12:21
IInputSynthesizer.Scroll(vertical, horizontal, unit) with Notch and
HighResolution units. Windows uses WHEEL/HWHEEL, Linux uses REL_WHEEL with
hi-res accumulation to a legacy notch; the fake records scroll events.
A source button routes to any IButtonTarget (vJoy button, mouse button, or scroll
increment); axis and button RouteToScroll plus the runtime steps that emit them.
Macros.Scroll emits a wheel increment through the synthesizer.
Inverting one route off a source axis leaves other routes from the same axis
unaffected, across split-to-axes and split-to-scroll.
ButtonTarget abstract record (vJoy button, mouse button, scroll) builds both its
route and its runtime IButtonStateSink from the sink context; OutputButtonSink
applies output buttons through the generic OutputDevice.SetButtonState. Also lands
axis-zone-to-mouse-button routing via SplitIntoButtons and drops the unused
IBoundRoute.OutputDeviceId.
The button apply loop now calls _Sinks[index].SetButtonState (OutputButtonSink)
instead of OutputDevice.SetButtonState inline, so every output-button state index
has a uniform apply. Behavior unchanged.
Mouse-button targets get an OutputButtonState slot and an IButtonStateSink like vJoy
buttons; their button sources OR-merge and their zones feed the same presser counter,
so StepMouseButtons is gone. The apply loop now fires sinks on change only, since key
and mouse sinks emit edge events rather than idempotent state.
…d keyboard

Every button source becomes a ButtonToTargetRoute and every axis zone an AxisZoneRoute,
each carrying a ButtonTarget that decides the runtime sink. This removes ButtonRoute,
ButtonToMouseRoute, ButtonToScrollRoute, AxisToButtonRoute and AxisToMouseButtonRoute, and
folds scroll buttons into the shared presser counter. KeyTarget then needs no new route
type: button-to-key and zone-to-key route through the same path. The output-device
request now carries OutputButtonBinding directly rather than routes.
Abstract `ButtonTarget<T>` enables button targets like `KeyTarget`, `MouseButtonTarget`, and `OutputButtonBinding` to implement merge behavior via `IMergeableObject<T>`.
…usable route abstractions

Extracted reusable types (e.g., `AxisSplitIntoButtons`, `AxesWithZones`, `ButtonsRoutedToOutput`, `AxesRoutedToOutput`) to encapsulate routing logic. Simplified usages by replacing inline `SplitIntoButtons` logic with its new abstraction, improving modularity and clarity.
…plement it

Introduced `IConfigurableRoute` as a new interface for configurable routing. Updated all route types and relevant tests to adopt this abstraction, ensuring consistency and improved modularity across the codebase.
Macros.MoveMouse(dx, dy) drives the synthesizer relative mouse move, like the
key/mouse-button/scroll actions. Relative only; absolute is not implemented yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mouse output Keyboard output

1 participant