diff --git a/src/VRio.App/MainForm.cs b/src/VRio.App/MainForm.cs
index a4b34fd..2b75143 100644
--- a/src/VRio.App/MainForm.cs
+++ b/src/VRio.App/MainForm.cs
@@ -41,35 +41,31 @@ internal sealed class MainForm : Form
ForeColor = Color.Gray,
};
- private readonly NumericUpDown _verMajor = new() { Location = new Point(80, 24), Width = 44, Minimum = 0, Maximum = 127, Value = 4 };
- private readonly NumericUpDown _verMinor = new() { Location = new Point(140, 24), Width = 44, Minimum = 0, Maximum = 127, Value = 2 };
- private readonly CheckBox _spring = new() { Text = "Stick springs back to center", Location = new Point(10, 56), AutoSize = true, Checked = true };
- private readonly Button _centerAxes = new() { Text = "Center all axes", Location = new Point(10, 82), Width = 140, Height = 26 };
- private readonly Button _lampsOff = new() { Text = "All lamps off", Location = new Point(156, 82), Width = 140, Height = 26 };
- private readonly Button _testEnter = new() { Text = "Enter test mode", Location = new Point(10, 114), Width = 140, Height = 26 };
- private readonly Button _testExit = new() { Text = "Exit test mode", Location = new Point(156, 114), Width = 140, Height = 26 };
+ private readonly CheckBox _spring = new() { Text = "Stick springs back to center", Location = new Point(10, 24), AutoSize = true, Checked = true };
+ private readonly Button _centerAxes = new() { Text = "Center all axes", Location = new Point(10, 52), Width = 140, Height = 26 };
+ private readonly Button _lampsOff = new() { Text = "All lamps off", Location = new Point(156, 52), Width = 140, Height = 26 };
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 CheckBox _padInput = new() { Text = "Gamepad", Location = new Point(100, 22), AutoSize = true, Checked = true };
+ private readonly CheckBox _invertY = new() { Text = "Invert Y", Location = new Point(190, 22), AutoSize = true };
private readonly Label _padStatus = new()
{
Text = "No controller detected.",
- Location = new Point(10, 70),
+ Location = new Point(10, 46),
AutoSize = true,
ForeColor = Color.Gray,
};
- 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 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 CheckBox _kbLights = new()
{
Text = "Mirror lamps on RGB keyboard (Dynamic Lighting)",
- Location = new Point(10, 124),
+ Location = new Point(10, 100),
AutoSize = true,
};
private readonly ComboBox _kbLightsTarget = new()
{
- Location = new Point(10, 146),
+ Location = new Point(10, 122),
Width = 286,
DropDownStyle = ComboBoxStyle.DropDownList,
Enabled = false, // populated while the mirror is on
@@ -77,12 +73,12 @@ internal sealed class MainForm : Form
private readonly Label _kbInputLabel = new()
{
Text = "Capture keyboard (no window focus needed):",
- Location = new Point(10, 168),
+ Location = new Point(10, 144),
AutoSize = true,
};
private readonly ComboBox _kbInputTarget = new()
{
- Location = new Point(10, 186),
+ Location = new Point(10, 162),
Width = 286,
DropDownStyle = ComboBoxStyle.DropDownList,
};
@@ -95,25 +91,24 @@ internal sealed class MainForm : Form
private readonly Label _counters = new()
{
- Location = new Point(12, 458),
+ Location = new Point(12, 372),
AutoSize = true,
Font = new Font("Consolas", 8f),
};
private readonly Label _help = new()
{
- Location = new Point(12, 514),
+ Location = new Point(12, 428),
MaximumSize = new Size(306, 0),
AutoSize = true,
ForeColor = Color.Gray,
Text = "Left-click a cell: momentary press. Right-click: latch it down. " +
- "Drag the X/Y box and the Z / L / R gauges to move the axes. " +
- "Keyboard and Xbox-pad input follow the bindings file (Edit bindings…).",
+ "Drag the X/Y box and the Z / L / R gauges to move the axes.",
};
private readonly TextBox _logBox = new()
{
- Location = new Point(12, 598),
+ Location = new Point(12, 498),
Multiline = true,
ReadOnly = true,
ScrollBars = ScrollBars.Both, // long wire lines don't wrap — scroll to read
@@ -184,8 +179,6 @@ internal sealed class MainForm : Form
_rescan.Click += (_, _) => RefreshPorts();
_openClose.Click += (_, _) => ToggleOpen();
- _verMajor.ValueChanged += (_, _) => _device.VersionMajor = (byte)_verMajor.Value;
- _verMinor.ValueChanged += (_, _) => _device.VersionMinor = (byte)_verMinor.Value;
_spring.CheckedChanged += (_, _) => _canvas.StickSpringsBack = _spring.Checked;
_centerAxes.Click += (_, _) =>
{
@@ -198,8 +191,6 @@ internal sealed class MainForm : Form
_device.ClearLamps();
_canvas.Invalidate();
};
- _testEnter.Click += (_, _) => _device.SendTestMode(1);
- _testExit.Click += (_, _) => _device.SendTestMode(0);
_clearLog.Click += (_, _) => _logBox.Clear();
_kbInput.CheckedChanged += (_, _) =>
@@ -280,21 +271,17 @@ internal sealed class MainForm : Form
panel.Controls.Add(_openClose);
panel.Controls.Add(_linkStatus);
- var device = new GroupBox { Text = "Device", Location = new Point(12, 68), Size = new Size(306, 150) };
- device.Controls.Add(new Label { Text = "Firmware:", Location = new Point(10, 27), AutoSize = true });
- device.Controls.Add(_verMajor);
- device.Controls.Add(new Label { Text = ".", Location = new Point(127, 27), AutoSize = true });
- device.Controls.Add(_verMinor);
- device.Controls.AddRange(new Control[] { _spring, _centerAxes, _lampsOff, _testEnter, _testExit });
+ var device = new GroupBox { Text = "Device", Location = new Point(12, 68), Size = new Size(306, 88) };
+ device.Controls.AddRange(new Control[] { _spring, _centerAxes, _lampsOff });
panel.Controls.Add(device);
- var input = new GroupBox { Text = "Input", Location = new Point(12, 224), Size = new Size(306, 226) };
+ var input = new GroupBox { Text = "Input", Location = new Point(12, 162), Size = new Size(306, 202) };
input.Controls.AddRange(new Control[] { _kbInput, _padInput, _invertY, _padStatus, _reloadBindings, _editBindings, _kbLights, _kbLightsTarget, _kbInputLabel, _kbInputTarget });
panel.Controls.Add(input);
panel.Controls.Add(_counters);
panel.Controls.Add(_help);
- panel.Controls.Add(new Label { Text = "Wire log:", Location = new Point(12, 580), AutoSize = true });
+ panel.Controls.Add(new Label { Text = "Wire log:", Location = new Point(12, 480), AutoSize = true });
_logBox.Size = new Size(306, ClientSize.Height - _logBox.Top - 44);
panel.Controls.Add(_logBox);
@@ -359,11 +346,11 @@ internal sealed class MainForm : Form
///
/// Keys route to the panel unless the user is in a control that needs
- /// them (port list, firmware spinners, log box scrolling).
+ /// them (port list, log box scrolling).
///
private bool KeyboardRoutingActive =>
_kbInput.Checked &&
- !_portBox.ContainsFocus && !_verMajor.ContainsFocus && !_verMinor.ContainsFocus && !_logBox.ContainsFocus;
+ !_portBox.ContainsFocus && !_logBox.ContainsFocus;
protected override void OnHandleCreated(EventArgs e)
{
diff --git a/src/VRio.Core/Device/VRioDevice.cs b/src/VRio.Core/Device/VRioDevice.cs
index c9994a6..a871d35 100644
--- a/src/VRio.Core/Device/VRioDevice.cs
+++ b/src/VRio.Core/Device/VRioDevice.cs
@@ -58,9 +58,11 @@ public sealed class VRioDevice
public byte VersionMinor { get; set; } = 2;
///
- /// Negate joystick Y in AnalogReply. Only the host sees the flip — local
- /// state (, the panel's dot and readout) keeps the
- /// physical stick direction.
+ /// The wire natively carries stick-up as negative (full up = −80 in
+ /// AnalogReply); set this to send the physical direction (up = positive)
+ /// instead. Only the host sees the difference — local state
+ /// (, the panel's dot) keeps the physical stick
+ /// direction either way.
///
public bool InvertJoystickY { get; set; }
@@ -110,7 +112,8 @@ public sealed class VRioDevice
///
/// Axis value as the next AnalogReply will carry it — same as
- /// except for the flip.
+ /// except joystick Y follows the wire convention
+ /// (see ).
///
public short GetWireAxis(RioAxis axis)
{
@@ -119,8 +122,8 @@ public sealed class VRioDevice
}
private short WireY(short y) => InvertJoystickY
- ? (short)Math.Min(AnalogCodec.Max, -y) // clamp: -Min (8192) is one past the 14-bit Max
- : y;
+ ? y
+ : (short)Math.Min(AnalogCodec.Max, -y); // clamp: -Min (8192) is one past the 14-bit Max
///
/// Move an axis. Values are clamped to the 14-bit signed range the wire
diff --git a/tests/VRio.Core.Tests/VRioDeviceTests.cs b/tests/VRio.Core.Tests/VRioDeviceTests.cs
index c810c12..4f22fdb 100644
--- a/tests/VRio.Core.Tests/VRioDeviceTests.cs
+++ b/tests/VRio.Core.Tests/VRioDeviceTests.cs
@@ -61,15 +61,15 @@ 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(3000, AnalogCodec.Combine(p[6], p[7])); // joystick Y
+ Assert.Equal(-3000, AnalogCodec.Combine(p[6], p[7])); // joystick Y (up is negative on the wire)
Assert.Equal(-5000, AnalogCodec.Combine(p[8], p[9])); // joystick X
Assert.Equal(1, device.AnalogRequests);
}
[Fact]
- public void InvertJoystickY_flips_the_wire_but_not_local_state()
+ public void Joystick_Y_is_negated_on_the_wire_but_not_locally()
{
- var device = new VRioDevice { InvertJoystickY = true };
+ var device = new VRioDevice();
var wire = new Wire(device);
device.SetAxis(RioAxis.JoystickY, 3000);
@@ -88,6 +88,20 @@ public class VRioDeviceTests
Assert.Equal(AnalogCodec.Max, AnalogCodec.Combine(p[6], p[7]));
}
+ [Fact]
+ public void InvertJoystickY_sends_the_physical_direction_instead()
+ {
+ var device = new VRioDevice { InvertJoystickY = true };
+ var wire = new Wire(device);
+ device.SetAxis(RioAxis.JoystickY, 3000);
+
+ Send(device, PacketBuilder.Build(RioCommand.AnalogRequest));
+
+ byte[] p = Assert.Single(wire.Packets).Payload;
+ Assert.Equal(3000, AnalogCodec.Combine(p[6], p[7]));
+ Assert.Equal(3000, device.GetWireAxis(RioAxis.JoystickY));
+ }
+
[Fact]
public void VersionRequest_reports_configured_firmware()
{