Files
sharpemu/tests/SharpEmu.Libs.Tests/Pad/HostWindowInputTests.cs
T
Berk 2b6bd5a532 Sdl backend (#670)
* [audio] added sdl audio backend and in-tree atrac9 decoder

* [input] replaced per-platform pad readers with sdl gamepad input

* [video] added sdl window and host display plumbing

* [gui] added host display options and per-game render settings

* [bink] synced host movie playback to the guest audio clock

* [cpu] hooked windows write faults into guest image tracking

* [perf] added guest and render profiling, reserved host cpu lanes

* [kernel] fixed stale pthread mutex handle alias

* [host] wired the sdl session, save-data paths and project references

* [audio] hoisted ajm trace stackalloc out of its loop

* [video] Add guest image sync setting

* [build] Strip native symbols

* reuse
2026-07-28 03:33:26 +03:00

171 lines
5.3 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.Libs.Pad;
using SharpEmu.HLE.Host;
using Xunit;
namespace SharpEmu.Libs.Tests.Pad;
public sealed class HostWindowInputTests
{
[Theory]
[InlineData(short.MinValue, 0)]
[InlineData(0, 128)]
[InlineData(short.MaxValue, 255)]
public void ToStickByteMapsFullSdlRange(short value, int expected)
{
Assert.Equal((byte)expected, HostWindowInput.ToStickByte(value));
}
[Fact]
public void ConnectPublishesWindowKeyboardState()
{
HostWindowInput.Connect();
try
{
HostWindowInput.SetKey(0x57, true);
var source = Assert.IsAssignableFrom<IHostWindowInputSource>(HostWindowInputSource.Current);
Assert.True(source.HasKeyboardFocus);
Assert.True(source.IsKeyDown(0x57));
}
finally
{
HostWindowInput.Disconnect();
}
Assert.Null(HostWindowInputSource.Current);
}
[Fact]
public void ConnectedGamepadStateIsExposedByWindowSource()
{
var expected = new HostGamepadState(
true,
HostGamepadButtons.Cross | HostGamepadButtons.Options,
64,
128,
192,
128,
0,
255);
HostWindowInput.Connect();
try
{
HostWindowInput.SetGamepad("SDL test pad", expected);
Span<HostGamepadState> states = stackalloc HostGamepadState[1];
var source = Assert.IsAssignableFrom<IHostWindowInputSource>(HostWindowInputSource.Current);
Assert.Equal(1, source.GetGamepadStates(states));
Assert.Equal(expected, states[0]);
Assert.Equal("SDL test pad", source.DescribeConnectedGamepad());
}
finally
{
HostWindowInput.Disconnect();
}
}
[Fact]
public void ControllerOutputIsForwardedToSdlOwner()
{
var output = new RecordingGamepadOutput();
var effect = new HostAdaptiveTriggerEffect(
0x21, 0x04, 0, 0x07, 0, 0, 0, 0, 0, 0, 0, 255);
HostWindowInput.Connect(output);
try
{
var source = Assert.IsAssignableFrom<IHostWindowInputSource>(HostWindowInputSource.Current);
source.SetRumble(10, 20);
source.SetTriggerRumble(30, 40);
source.SetAdaptiveTriggerEffect(effect, null);
source.SetLightbar(50, 60, 70);
source.ResetLightbar();
Assert.Equal((10, 20), output.Rumble);
Assert.Equal(((byte?)30, (byte?)40), output.TriggerRumble);
Assert.Equal(effect, output.LeftTriggerEffect);
Assert.Null(output.RightTriggerEffect);
Assert.Equal((50, 60, 70), output.Lightbar);
Assert.True(output.LightbarReset);
}
finally
{
HostWindowInput.Disconnect();
}
}
[Fact]
public void CommonHostInputUsesPublishedWindowSource()
{
var output = new RecordingGamepadOutput();
var expected = new HostGamepadState(
true,
HostGamepadButtons.Cross,
32,
64,
96,
128,
160,
192);
var input = new WindowHostInput();
HostWindowInput.Connect(output);
try
{
HostWindowInput.SetKey(0x57, true);
HostWindowInput.SetGamepad("SDL test pad", expected);
Span<HostGamepadState> states = stackalloc HostGamepadState[1];
Assert.Equal(1, input.GetGamepadStates(states));
Assert.Equal(expected, states[0]);
Assert.Equal("SDL test pad", input.DescribeConnectedGamepad());
Assert.True(input.IsHostWindowFocused());
Assert.True(input.IsKeyDown(0x57));
input.SetRumble(10, 20);
input.SetLightbar(30, 40, 50);
Assert.Equal((10, 20), output.Rumble);
Assert.Equal((30, 40, 50), output.Lightbar);
}
finally
{
HostWindowInput.Disconnect();
}
}
private sealed class RecordingGamepadOutput : IHostGamepadOutput
{
public (byte Large, byte Small) Rumble { get; private set; }
public (byte? Left, byte? Right) TriggerRumble { get; private set; }
public HostAdaptiveTriggerEffect? LeftTriggerEffect { get; private set; }
public HostAdaptiveTriggerEffect? RightTriggerEffect { get; private set; }
public (byte Red, byte Green, byte Blue) Lightbar { get; private set; }
public bool LightbarReset { get; private set; }
public void SetRumble(byte largeMotor, byte smallMotor) =>
Rumble = (largeMotor, smallMotor);
public void SetTriggerRumble(byte? leftTrigger, byte? rightTrigger) =>
TriggerRumble = (leftTrigger, rightTrigger);
public void SetAdaptiveTriggerEffect(
HostAdaptiveTriggerEffect? leftTrigger,
HostAdaptiveTriggerEffect? rightTrigger)
{
LeftTriggerEffect = leftTrigger;
RightTriggerEffect = rightTrigger;
}
public void SetLightbar(byte red, byte green, byte blue) =>
Lightbar = (red, green, blue);
public void ResetLightbar() => LightbarReset = true;
}
}