Skip to content

Commit 7eb9061

Browse files
committed
Added throttle-position tracking, audio feedback, and (bonus) mapping for mouse x-buttons.
1 parent 84297d3 commit 7eb9061

13 files changed

Lines changed: 544 additions & 63 deletions

src/App.config

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,48 @@
66
</startup>
77

88
<appSettings>
9-
<!--mouse sensitivity (DPI) .. for typical 1200 dpi mouse, the total "throw"
10-
distance of the throttle is approx 2.5 inches of mouse travel-->
9+
<!--
10+
Mouse sensitivity (DPI)
11+
For typical 1200 dpi mouse, the total "throw" distance of the throttle is approx 2.5 inches of mouse travel.
12+
-->
1113
<add key="MouseResolution" value="1200"/>
1214

13-
<!--mouse button to engage throttle [0=LeftButton ... 4=XButton-Forward] -->
15+
<!--
16+
(optional) Mouse button to engage throttle [0-4]
17+
Note: really only the left-button (button #0) makes sense, with mouselook mode engaged in BMS-->
1418
<add key="MouseButton" value="0"/>
1519

16-
<!--(optional) joystick button to engage throttle [0-31] -->
20+
<!--
21+
(optional) Joystick button to engage throttle [0-31]
22+
Tip: using your SimHotasPinkyShift button is often a convenient choice
23+
-->
1724
<add key="JoystickButton" value="0"/>
25+
26+
<!--
27+
Throttle "throw" range of motion, in mousewheel-notch units
28+
In BMS 4.35, the throttle throw-distance appears to be 125 "wheelnotch" units, with mousewheel set to minimum sensitivity.
29+
-->
30+
<add key="ThrottleThrow" value="125"/>
31+
32+
<!--
33+
Throttle setting for 100% MIL power (one notch below AB range), in "wheelnotch" units
34+
-->
35+
<add key="ThrottleMaxMIL" value="83"/>
36+
37+
<!--
38+
(optional) Audio feedback for passing through AB detent, and hitting min/max extent range of throttle.
39+
Note: currently these absolute positions are estimated, and may not always be reliable.
40+
-->
41+
<add key="IdleStopSound" value="throttleExtent"/>
42+
<add key="BurnerDetentUpSound" value="throttleDetent"/>
43+
<add key="BurnerDetentDownSound" value="throttleDetent"/>
44+
<add key="MaxBurnerSound" value="throttleExtent"/>
45+
46+
<!--
47+
(bonus-feature) Remap mouse x-buttons to (unshifted) keybd scancodes.. because why not.
48+
-->
49+
<add key="ScancodeForMouseXButton1" value="0xC7"/><!--[Home]: UHF push-to-talk-->
50+
<add key="ScancodeForMouseXButton2" value="0xCF"/><!--[End]: VHF push-to-talk-->
1851
</appSettings>
1952

2053
</configuration>

src/Config.cs

Lines changed: 144 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ namespace DragWheel
66
{
77
internal static class Config
88
{
9+
//--------------------------------------------------------------
10+
// Settings
11+
912
//----------------------------------------
1013
public static int MouseResolution
1114
{
@@ -16,7 +19,7 @@ public static int MouseResolution
1619
if (String.IsNullOrEmpty(configValue))
1720
return defaultMouseResolution;
1821

19-
return Int32.Parse(configValue, System.Globalization.CultureInfo.InvariantCulture);
22+
return (int)_ParseUInt32(configValue);
2023
}
2124
}
2225
static int defaultMouseResolution = 1200;//dpi
@@ -29,15 +32,16 @@ public static int? MouseButton
2932
string configValue = ConfigurationManager.AppSettings["MouseButton"];
3033

3134
if (String.IsNullOrEmpty(configValue))
32-
return null;
35+
return defaultMouseButton;
3336

34-
int value = Int32.Parse(configValue, System.Globalization.CultureInfo.InvariantCulture);
35-
if (value < 0 || value > 4)
37+
uint value = _ParseUInt32(configValue);
38+
if (value > 4)
3639
throw new ConfigurationErrorsException("MouseButton must be in range [0-4]");
3740

38-
return value;
41+
return (int)value;
3942
}
4043
}
44+
static int? defaultMouseButton = null;//not mouse-activated
4145

4246
//----------------------------------------
4347
public static int? JoystickButton
@@ -47,15 +51,146 @@ public static int? JoystickButton
4751
string configValue = ConfigurationManager.AppSettings["JoystickButton"];
4852

4953
if (String.IsNullOrEmpty(configValue))
50-
return null;
54+
return defaultJoystickButton;
5155

52-
int value = Int32.Parse(configValue, System.Globalization.CultureInfo.InvariantCulture);
53-
if (value < 0 || value > 31)
56+
uint value = _ParseUInt32(configValue);
57+
if (value > 31)
5458
throw new ConfigurationErrorsException("JoystickButton must be in range [0-31]");
5559

56-
return value;
60+
return (int)value;
61+
}
62+
}
63+
static int? defaultJoystickButton = null;//not stick-activated
64+
65+
//----------------------------------------
66+
public static int ThrottleThrow
67+
{
68+
get
69+
{
70+
string configValue = ConfigurationManager.AppSettings["ThrottleThrow"];
71+
72+
if (String.IsNullOrEmpty(configValue))
73+
return defaultThrottleThrow;
74+
75+
uint value = _ParseUInt32(configValue);
76+
if (value > 1000)
77+
throw new ConfigurationErrorsException("ThrottleThrow expected range: [0-1000]");
78+
79+
return (int)value;
80+
}
81+
}
82+
static int defaultThrottleThrow = 125;//tested on Falcon BMS 4.35, with mousewheel-sensitivity set to minimum
83+
// tested on F-16 and F-18 .. TODO: test other planes?
84+
85+
//----------------------------------------
86+
public static int ThrottleMaxMIL
87+
{
88+
get
89+
{
90+
string configValue = ConfigurationManager.AppSettings["ThrottleMaxMIL"];
91+
92+
if (String.IsNullOrEmpty(configValue))
93+
return defaultThrottleMaxMIL;
94+
95+
uint value = _ParseUInt32(configValue);
96+
if (value > 1000)
97+
throw new ConfigurationErrorsException("ThrottleMaxMIL expected range: [0-1000]");
98+
99+
return (int)value;
100+
}
101+
}
102+
static int defaultThrottleMaxMIL = 83;//tested on Falcon BMS 4.35, with mousewheel-sensitivity set to minimum
103+
// tested on F-16 and F-18 .. TODO: test other planes?
104+
105+
//----------------------------------------
106+
public static string IdleStopSound
107+
{
108+
get
109+
{
110+
return ConfigurationManager.AppSettings["IdleStopSound"];
111+
}
112+
}
113+
114+
//----------------------------------------
115+
public static string BurnerDetentUpSound
116+
{
117+
get
118+
{
119+
return ConfigurationManager.AppSettings["BurnerDetentUpSound"];
120+
}
121+
}
122+
123+
//----------------------------------------
124+
public static string BurnerDetentDownSound
125+
{
126+
get
127+
{
128+
return ConfigurationManager.AppSettings["BurnerDetentDownSound"];
129+
}
130+
}
131+
132+
//----------------------------------------
133+
public static string MaxBurnerSound
134+
{
135+
get
136+
{
137+
return ConfigurationManager.AppSettings["MaxBurnerSound"];
138+
}
139+
}
140+
141+
//----------------------------------------
142+
public static byte? ScancodeForMouseXButton1
143+
{
144+
get
145+
{
146+
string configValue = ConfigurationManager.AppSettings["ScancodeForMouseXButton1"];
147+
148+
if (String.IsNullOrEmpty(configValue))
149+
return null;
150+
151+
uint value = _ParseUInt32(configValue);
152+
if (value > 255)
153+
throw new ConfigurationErrorsException("Scancode must be in range [0x00-0xFF]");
154+
155+
return (byte)value;
57156
}
58157
}
158+
159+
//----------------------------------------
160+
public static byte? ScancodeForMouseXButton2
161+
{
162+
get
163+
{
164+
string configValue = ConfigurationManager.AppSettings["ScancodeForMouseXButton2"];
165+
166+
if (String.IsNullOrEmpty(configValue))
167+
return null;
168+
169+
uint value = _ParseUInt32(configValue);
170+
if (value > 255)
171+
throw new ConfigurationErrorsException("Scancode must be in range [0x00-0xFF]");
172+
173+
return (byte)value;
174+
}
175+
}
176+
177+
//--------------------------------------------------------------
178+
// Helpers
179+
180+
//----------------------------------------
181+
static uint _ParseUInt32( string s )
182+
{
183+
System.Globalization.NumberStyles style = System.Globalization.NumberStyles.None;
184+
if (s.StartsWith("0x"))
185+
{
186+
style = System.Globalization.NumberStyles.HexNumber;
187+
s = s.Substring(2);
188+
}
189+
190+
uint value = UInt32.Parse(s, style, System.Globalization.CultureInfo.InvariantCulture);
191+
return value;
192+
}
193+
59194
}
60195

61196
//------------------------------------------------------------------

src/DragWheel.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
<Reference Include="Microsoft.CSharp" />
4242
</ItemGroup>
4343
<ItemGroup>
44+
<Compile Include="Sounds.cs" />
4445
<Compile Include="Config.cs" />
46+
<Compile Include="ThrottleAbsolutePosTracker.cs" />
4547
<Compile Include="RawInputJoystickHandler.cs" />
4648
<Compile Include="MouseDragTracker.cs" />
4749
<Compile Include="RawInputMouseHandler.cs" />
@@ -55,7 +57,9 @@
5557
<None Include="App.config" />
5658
</ItemGroup>
5759
<ItemGroup>
58-
<WCFMetadata Include="Connected Services\" />
60+
<EmbeddedResource Include="Sounds\throttleDetent.wav" />
61+
<EmbeddedResource Include="Sounds\throttleExtent.wav" />
62+
<EmbeddedResource Include="Sounds.resx" />
5963
</ItemGroup>
6064
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
6165
</Project>

src/MouseDragTracker.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ public static int TrackDragDeltas( int deltaX, int deltaY )
5555
int distanceThreshold = Config.MouseResolution / 50; // @1200dpi => 24 mickies (~0.02in)
5656

5757
if (_yPos >= (_yLo + distanceThreshold))
58-
return CalcWheelDeltaAndUpdateBaseline(_yPos - _yLo);
58+
return CalcWheelnotchDeltaAndUpdateBaseline(_yPos - _yLo);
5959

6060
if (_yPos <= (_yHi - distanceThreshold))
61-
return CalcWheelDeltaAndUpdateBaseline(_yPos - _yHi);
61+
return CalcWheelnotchDeltaAndUpdateBaseline(_yPos - _yHi);
6262

6363
return 0;
6464

65-
int CalcWheelDeltaAndUpdateBaseline( int draggedDistance )
65+
int CalcWheelnotchDeltaAndUpdateBaseline( int draggedDistance )
6666
{
6767
int quantizedDistance = (int)(draggedDistance / distanceThreshold);
6868

@@ -72,7 +72,7 @@ int CalcWheelDeltaAndUpdateBaseline( int draggedDistance )
7272
if (quantizedDistance > 0)
7373
_yLo += (int)(quantizedDistance * distanceThreshold);
7474

75-
return quantizedDistance * 120;//WHEEL_DELTA, one standardized "notch" of mousewheel rotation
75+
return quantizedDistance;
7676
}
7777
}
7878
}
@@ -86,8 +86,8 @@ internal static void MouseTracker_Up( )
8686
MouseDragTracker.ResetDragDeltas();
8787

8888
int dy = Config.MouseResolution / 50 + 1;
89-
Debug.Assert(+120 == MouseDragTracker.TrackDragDeltas(0, +dy));
90-
Debug.Assert(+120 == MouseDragTracker.TrackDragDeltas(0, +dy));
89+
Debug.Assert(+1 == MouseDragTracker.TrackDragDeltas(0, +dy));
90+
Debug.Assert(+1 == MouseDragTracker.TrackDragDeltas(0, +dy));
9191
}
9292

9393
//----------------------------------------
@@ -97,8 +97,8 @@ internal static void MouseTracker_Down( )
9797
MouseTracker_Up();
9898

9999
int dy = Config.MouseResolution / 50 + 1;
100-
Debug.Assert(-120 == MouseDragTracker.TrackDragDeltas(0, -dy));
101-
Debug.Assert(-120 == MouseDragTracker.TrackDragDeltas(0, -dy));
100+
Debug.Assert(-1 == MouseDragTracker.TrackDragDeltas(0, -dy));
101+
Debug.Assert(-1 == MouseDragTracker.TrackDragDeltas(0, -dy));
102102
}
103103
}
104104
}

src/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ static int _WndProcImpl( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam )
108108

109109
if (RawInputMouseHandler.DragActivated || RawInputJoystickHandler.DragActivated)
110110
{
111-
int wheelDelta = MouseDragTracker.TrackDragDeltas(dx, dy);
112-
if (wheelDelta != 0)
111+
int wheelnotchDelta = MouseDragTracker.TrackDragDeltas(dx, dy);
112+
if (wheelnotchDelta != 0)
113113
{
114+
int wheelDelta = wheelnotchDelta * 120;//WHEEL_DELTA, one standardized "notch" of mousewheel rotation
114115
Console.WriteLine("Sending deltaMouseWheel={0}", wheelDelta);
115116
Win32.SendInput.MoveMouseWheel(wheelDelta);
116117
}
@@ -141,6 +142,8 @@ static int _WndProcImpl( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam )
141142
//----------------------------------------
142143
static void TestImpl( )
143144
{
145+
//Tests.PlaySound();
146+
144147
Tests.Config_NullOrEmpty();
145148
Tests.MouseTracker_Up();
146149
Tests.MouseTracker_Down();

src/Properties/AssemblyInfo.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using System.Reflection;
1+
using System;
2+
using System.Reflection;
23
using System.Runtime.CompilerServices;
34
using System.Runtime.InteropServices;
5+
using System.Resources;
46

57
// General Information about an assembly is controlled through the following
68
// set of attributes. Change these attribute values to modify the information
79
// associated with an assembly.
810
[assembly: AssemblyTitle("DragWheel")]
9-
[assembly: AssemblyDescription("Left-drag to synthesize mousewheel movement")]
11+
[assembly: AssemblyDescription("Synthesize mousewheel movement by dragging")]
1012
[assembly: AssemblyConfiguration("")]
1113
[assembly: AssemblyCompany("")]
1214
[assembly: AssemblyProduct("DragWheel")]
@@ -18,6 +20,7 @@
1820
// to COM components. If you need to access a type in this assembly from
1921
// COM, set the ComVisible attribute to true on that type.
2022
[assembly: ComVisible(false)]
23+
[assembly: CLSCompliant(false)]
2124

2225
// The following GUID is for the ID of the typelib if this project is exposed to COM
2326
[assembly: Guid("68c3a8ad-3fa0-4cf9-a942-f7056e61edc1")]
@@ -32,5 +35,8 @@
3235
// You can specify all the values or you can default the Build and Revision Numbers
3336
// by using the '*' as shown below:
3437
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.0.2021.325")]
36-
[assembly: AssemblyFileVersion("1.0.2021.325")]
38+
[assembly: AssemblyVersion("1.1.2021.418")]
39+
[assembly: AssemblyFileVersion("1.1.2021.418")]
40+
41+
// Don't probe satellites for en-US resources.
42+
[assembly: NeutralResourcesLanguageAttribute("en-US")]

0 commit comments

Comments
 (0)