Invert-Y input toggle; git-stamped version in the window title
- InputRouter.InvertJoystickY flips the composed joystick Y (keys and pad alike) before wire scaling; panel drags write the device directly and stay untouched. New "Invert joystick Y" checkbox in the Input group, default on. - StampGitVersion target bakes "YYYY.MM.DD (shortsha)" of HEAD into InformationalVersion; the title shows it via Application.ProductVersion so a running build can be matched to its vYYYY.MM.DD release tag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+16
-12
@@ -49,24 +49,25 @@ internal sealed class MainForm : Form
|
||||
|
||||
private readonly CheckBox _kbInput = new() { Text = "Keyboard", Location = new Point(10, 22), AutoSize = true, Checked = true };
|
||||
private readonly CheckBox _padInput = new() { Text = "Xbox gamepad", Location = new Point(120, 22), AutoSize = true, Checked = true };
|
||||
private readonly CheckBox _invertY = new() { Text = "Invert joystick Y", Location = new Point(10, 46), AutoSize = true, Checked = true };
|
||||
private readonly Label _padStatus = new()
|
||||
{
|
||||
Text = "No controller detected.",
|
||||
Location = new Point(10, 46),
|
||||
Location = new Point(10, 70),
|
||||
AutoSize = true,
|
||||
ForeColor = Color.Gray,
|
||||
};
|
||||
private readonly Button _reloadBindings = new() { Text = "Reload bindings", Location = new Point(10, 68), Width = 140, Height = 26 };
|
||||
private readonly Button _editBindings = new() { Text = "Edit bindings…", Location = new Point(156, 68), Width = 140, Height = 26 };
|
||||
private readonly Button _reloadBindings = new() { Text = "Reload bindings", Location = new Point(10, 92), Width = 140, Height = 26 };
|
||||
private readonly Button _editBindings = new() { Text = "Edit bindings…", Location = new Point(156, 92), Width = 140, Height = 26 };
|
||||
private readonly CheckBox _kbLights = new()
|
||||
{
|
||||
Text = "Mirror lamps on RGB keyboard (Dynamic Lighting)",
|
||||
Location = new Point(10, 100),
|
||||
Location = new Point(10, 124),
|
||||
AutoSize = true,
|
||||
};
|
||||
private readonly ComboBox _kbLightsTarget = new()
|
||||
{
|
||||
Location = new Point(10, 122),
|
||||
Location = new Point(10, 146),
|
||||
Width = 286,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
Enabled = false, // populated while the mirror is on
|
||||
@@ -80,14 +81,14 @@ internal sealed class MainForm : Form
|
||||
|
||||
private readonly Label _counters = new()
|
||||
{
|
||||
Location = new Point(12, 390),
|
||||
Location = new Point(12, 414),
|
||||
AutoSize = true,
|
||||
Font = new Font("Consolas", 8f),
|
||||
};
|
||||
|
||||
private readonly Label _help = new()
|
||||
{
|
||||
Location = new Point(12, 446),
|
||||
Location = new Point(12, 470),
|
||||
MaximumSize = new Size(306, 0),
|
||||
AutoSize = true,
|
||||
ForeColor = Color.Gray,
|
||||
@@ -98,7 +99,7 @@ internal sealed class MainForm : Form
|
||||
|
||||
private readonly TextBox _logBox = new()
|
||||
{
|
||||
Location = new Point(12, 530),
|
||||
Location = new Point(12, 554),
|
||||
Multiline = true,
|
||||
ReadOnly = true,
|
||||
ScrollBars = ScrollBars.Both, // long wire lines don't wrap — scroll to read
|
||||
@@ -122,7 +123,8 @@ internal sealed class MainForm : Form
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
Text = "vRIO — Virtual RIO cockpit device";
|
||||
// ProductVersion carries the git stamp (see StampGitVersion in the csproj).
|
||||
Text = $"vRIO v{Application.ProductVersion} — Virtual RIO cockpit device";
|
||||
// Fit the window to its content: the cockpit canvas plus the 330px
|
||||
// control strip, with just enough height for the strip's log area.
|
||||
ClientSize = new Size(_canvas.Width + 332, Math.Max(_canvas.Height, 640));
|
||||
@@ -188,6 +190,8 @@ internal sealed class MainForm : Form
|
||||
if (!_kbInput.Checked)
|
||||
_router.ReleaseAllKeys();
|
||||
};
|
||||
_invertY.CheckedChanged += (_, _) => _router.InvertJoystickY = _invertY.Checked;
|
||||
_router.InvertJoystickY = _invertY.Checked; // the field initializer raises no event
|
||||
_kbLights.CheckedChanged += (_, _) =>
|
||||
{
|
||||
_lampMirror.Enabled = _kbLights.Checked;
|
||||
@@ -250,13 +254,13 @@ internal sealed class MainForm : Form
|
||||
device.Controls.AddRange(new Control[] { _spring, _centerAxes, _lampsOff, _testEnter, _testExit });
|
||||
panel.Controls.Add(device);
|
||||
|
||||
var input = new GroupBox { Text = "Input", Location = new Point(12, 224), Size = new Size(306, 158) };
|
||||
input.Controls.AddRange(new Control[] { _kbInput, _padInput, _padStatus, _reloadBindings, _editBindings, _kbLights, _kbLightsTarget });
|
||||
var input = new GroupBox { Text = "Input", Location = new Point(12, 224), Size = new Size(306, 182) };
|
||||
input.Controls.AddRange(new Control[] { _kbInput, _padInput, _invertY, _padStatus, _reloadBindings, _editBindings, _kbLights, _kbLightsTarget });
|
||||
panel.Controls.Add(input);
|
||||
|
||||
panel.Controls.Add(_counters);
|
||||
panel.Controls.Add(_help);
|
||||
panel.Controls.Add(new Label { Text = "Wire log:", Location = new Point(12, 512), AutoSize = true });
|
||||
panel.Controls.Add(new Label { Text = "Wire log:", Location = new Point(12, 536), AutoSize = true });
|
||||
|
||||
_logBox.Size = new Size(306, ClientSize.Height - _logBox.Top - 44);
|
||||
panel.Controls.Add(_logBox);
|
||||
|
||||
@@ -9,12 +9,37 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AssemblyTitle>vRIO — Virtual RIO device</AssemblyTitle>
|
||||
<!-- StampGitVersion already puts the sha in InformationalVersion; stop the
|
||||
SDK appending "+fullsha" on top of it. -->
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VRio.Core\VRio.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Stamp commit date + short sha into InformationalVersion; the window
|
||||
title shows it (Application.ProductVersion) so a running build can be
|
||||
matched to its vYYYY.MM.DD release tag at a glance. Falls back to the
|
||||
default 1.0.0 when git isn't available. -->
|
||||
<Target Name="StampGitVersion" BeforeTargets="GetAssemblyVersion" Condition="'$(DesignTimeBuild)' != 'true'">
|
||||
<!-- %%cs survives cmd's percent expansion as %cs: committer date, YYYY-MM-DD. -->
|
||||
<Exec Command="git -C "$(MSBuildProjectDirectory)" log -1 --format=%%cs"
|
||||
ConsoleToMSBuild="true" StandardOutputImportance="low" IgnoreExitCode="true" ContinueOnError="true">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitDate" />
|
||||
<Output TaskParameter="ExitCode" PropertyName="GitDateExitCode" />
|
||||
</Exec>
|
||||
<!-- The exclude flag keeps describe off the release tags: always the short
|
||||
sha, with a "dirty" suffix when the working tree has local edits. -->
|
||||
<Exec Command="git -C "$(MSBuildProjectDirectory)" describe --always --dirty --exclude=*"
|
||||
ConsoleToMSBuild="true" StandardOutputImportance="low" IgnoreExitCode="true" ContinueOnError="true">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitShortSha" />
|
||||
</Exec>
|
||||
<PropertyGroup Condition="'$(GitDateExitCode)' == '0' And '$(GitShortSha)' != ''">
|
||||
<InformationalVersion>$(GitCommitDate.Trim().Replace('-', '.')) ($(GitShortSha))</InformationalVersion>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -57,6 +57,13 @@ public sealed class InputRouter
|
||||
/// <summary>A routed address went down (true) or came back up (false) — for panel highlighting.</summary>
|
||||
public event Action<int, bool>? AddressHeldChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Flip the sign of the composed joystick Y before it reaches the device.
|
||||
/// Applies to all routed sources (keys and pad) alike; panel drags write
|
||||
/// the device directly and are unaffected.
|
||||
/// </summary>
|
||||
public bool InvertJoystickY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The active bindings. Assigning releases everything currently held
|
||||
/// (keys, toggles, pad buttons) so a reload never strands a pressed input.
|
||||
@@ -216,6 +223,8 @@ public sealed class InputRouter
|
||||
if (b.Rate <= 0f)
|
||||
total += Shape(_pad.Axis(b.Source), b);
|
||||
}
|
||||
if (InvertJoystickY && axis == RioAxis.JoystickY)
|
||||
total = -total;
|
||||
total = ClampNorm(axis, total);
|
||||
|
||||
short raw = (short)Math.Round(total * RioAxisRange.Full(axis));
|
||||
|
||||
@@ -191,6 +191,26 @@ public class InputRouterTests
|
||||
Assert.Equal(0, _device.GetAxis(RioAxis.JoystickY));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvertJoystickY_flips_the_composed_value_for_all_sources()
|
||||
{
|
||||
UseProfile("key Up axis JoystickY deflect 1\npadaxis LeftStickY axis JoystickY");
|
||||
_router.InvertJoystickY = true;
|
||||
|
||||
_router.KeyDown("Up");
|
||||
_router.Tick(0.016);
|
||||
Assert.Equal(-RioAxisRange.JoystickExtent, _device.GetAxis(RioAxis.JoystickY));
|
||||
_router.KeyUp("Up");
|
||||
|
||||
_router.SetPadState(new PadState(PadButtons.None, 0, 0.5f, 0, 0, 0, 0));
|
||||
_router.Tick(0.016);
|
||||
Assert.Equal(-RioAxisRange.JoystickExtent / 2, _device.GetAxis(RioAxis.JoystickY));
|
||||
|
||||
_router.InvertJoystickY = false; // toggling back re-sends the upright value
|
||||
_router.Tick(0.016);
|
||||
Assert.Equal(RioAxisRange.JoystickExtent / 2, _device.GetAxis(RioAxis.JoystickY));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rate_key_walks_the_throttle_and_position_sticks()
|
||||
{
|
||||
|
||||
@@ -48,6 +48,7 @@ public class VRioDeviceTests
|
||||
var device = new VRioDevice();
|
||||
var wire = new Wire(device);
|
||||
device.SetAxis(RioAxis.Throttle, 1000);
|
||||
device.SetAxis(RioAxis.JoystickY, 3000);
|
||||
device.SetAxis(RioAxis.JoystickX, -5000);
|
||||
|
||||
Send(device, PacketBuilder.Build(RioCommand.AnalogRequest));
|
||||
@@ -60,7 +61,7 @@ public class VRioDeviceTests
|
||||
Assert.Equal(1000, AnalogCodec.Combine(p[0], p[1])); // throttle
|
||||
Assert.Equal(0, AnalogCodec.Combine(p[2], p[3])); // left pedal
|
||||
Assert.Equal(0, AnalogCodec.Combine(p[4], p[5])); // right pedal
|
||||
Assert.Equal(0, AnalogCodec.Combine(p[6], p[7])); // joystick Y
|
||||
Assert.Equal(3000, AnalogCodec.Combine(p[6], p[7])); // joystick Y
|
||||
Assert.Equal(-5000, AnalogCodec.Combine(p[8], p[9])); // joystick X
|
||||
Assert.Equal(1, device.AnalogRequests);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user