Files
CydandClaude Opus 4.8 cc64d241c9 Phase 3: input mapping + output routing (RioJoy.Core.Mapping)
Port the iRIO decode and Press_V2/Release_V2 routing into testable C#:

- Mapping/: RioMapEntry decodes the 16-bit map word (lamp/mouse/hat/joy/extended/
  alt/ctrl/shift flags + value) and resolves the routing Kind by the legacy
  precedence (joy+hat+mouse => RIO command; none => keyboard; else joy>hat>mouse).
  RioAddress translates button/keypad events to table addresses (+0x50/+0x60
  keypad offsets); RioInputMap is the 112-entry per-profile table replacing the
  hard-coded iRIO[].
- InputRouter ports Press_V2/Release_V2: modifier press/release ordering,
  scancode keys, joystick buttons, POV hat, mouse move/click (deltas corrected
  per PROTOCOL.md), RIO-command dispatch, and lamp feedback (bright on press, dim
  on release; RIO commands carry none). InitializeLamps() dims all lamp entries.
- Output is split behind sink interfaces (IInputSink/IJoystickSink/ILampSink/
  IRioCommandSink) so routing is pure + unit-tested; Output/SendInputSink is the
  real SendInput keyboard/mouse adapter (scancode injection).
- tests: 30 new xUnit tests (84 total) for entry decode, address translation,
  and router routing/precedence/lamp/modifier ordering via a recording sink.

The joystick sink's real adapter (HID feeder -> RioGamepad via DeviceIoControl)
is blocked on the Phase 1 driver; routing already targets IJoystickSink.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:10:06 -05:00

60 lines
1.8 KiB
C#

using RioJoy.Core.Mapping;
using Xunit;
namespace RioJoy.Core.Tests.Mapping;
public class RioMapEntryTests
{
[Fact]
public void Keyboard_NoRoutingFlags_DecodesModifiersAndValue()
{
// 0x8749 = hasLamp + alt + ctrl + shift, VK 0x49 ('I')
var e = new RioMapEntry(0x8749);
Assert.Equal(RioRouteKind.Keyboard, e.Kind);
Assert.True(e.HasLamp);
Assert.True(e.Alt);
Assert.True(e.Ctrl);
Assert.True(e.Shift);
Assert.False(e.Extended);
Assert.Equal(0x49, e.Value);
}
[Fact]
public void Extended_FlagDecodes()
{
// 0x88BE = hasLamp + extended, VK 0xBE
var e = new RioMapEntry(0x88BE);
Assert.Equal(RioRouteKind.Keyboard, e.Kind);
Assert.True(e.HasLamp);
Assert.True(e.Extended);
Assert.Equal(0xBE, e.Value);
}
[Theory]
[InlineData(0x1009, RioRouteKind.Joystick)] // joy flag
[InlineData(0x2003, RioRouteKind.Hat)] // hat flag
[InlineData(0x4002, RioRouteKind.Mouse)] // mouse flag
[InlineData(0x7005, RioRouteKind.RioCommand)] // joy+hat+mouse → command
[InlineData(0x0041, RioRouteKind.Keyboard)] // none → keyboard
public void Kind_FollowsPrecedence(ushort raw, RioRouteKind expected)
{
Assert.Equal(expected, new RioMapEntry(raw).Kind);
}
[Fact]
public void Joystick_BeatsHatAndMouse_WhenMultipleSet()
{
// joy + hat set (but not mouse) → joystick wins
Assert.Equal(RioRouteKind.Joystick, new RioMapEntry(0x3001).Kind);
// hat + mouse set (but not joy) → hat wins
Assert.Equal(RioRouteKind.Hat, new RioMapEntry(0x6001).Kind);
}
[Fact]
public void Unmapped_IsZero()
{
Assert.True(new RioMapEntry(0).IsUnmapped);
Assert.False(new RioMapEntry(0x0041).IsUnmapped);
}
}