11
2+ using MarvinsAIRARefactored . Controls ;
23using MarvinsAIRARefactored . GameBridges ;
34
45using static MarvinsAIRARefactored . Windows . MainWindow ;
@@ -124,6 +125,8 @@ public void Pump( double totalSeconds )
124125
125126 public void Tick ( App app )
126127 {
128+ UpdateSteeringPassthrough ( app ) ;
129+
127130 ActiveAdapter ? . Tick ( app ) ;
128131
129132 _updateCounter -- ;
@@ -135,6 +138,15 @@ public void Tick( App app )
135138
136139 _updateCounter = UpdateInterval ;
137140
141+ // keep the vJoy driver warning and the sweep button's blink fresh while the game bridge page is
142+ // showing - the fault is only discovered after the passthrough first tries to initialize the
143+ // device, and the sweep can be cancelled by the toggles rather than by its own button
144+ if ( MairaAppMenuPopup . CurrentAppPage == AppPage . GameBridge )
145+ {
146+ _gameBridgePage . UpdateVJoyStatus ( app ) ;
147+ _gameBridgePage . UpdateSweepButton ( app ) ;
148+ }
149+
138150 if ( _transitioning )
139151 {
140152 return ;
@@ -161,6 +173,86 @@ public void Tick( App app )
161173 }
162174 }
163175
176+ // When enabled, the wheelbase's steering axis is passed through to the vJoy device's X axis every tick.
177+ // This is for games that provide no way to disable their built-in force feedback (Assetto Corsa, for
178+ // example): the user binds the game's steering input to the vJoy X axis instead of the real wheel, so the
179+ // game sends its force feedback to the vJoy device (which discards it) and MAIRA keeps sole control of
180+ // the real wheelbase. This tick runs BEFORE VirtualJoystick.Tick in the app worker loop, so the position
181+ // written here goes out to vJoy in the same frame.
182+ private bool _steeringPassthroughActive = false ;
183+ private int _steeringPassthroughShutdownCountdown = 0 ;
184+
185+ // the steering sweep continuously oscillates the vJoy axis (one full left-right-left cycle every two
186+ // seconds) - games like AC ignore controller input while their window is not in the foreground, so the
187+ // user arms the sweep in MAIRA, clicks over to the game, and the game then sees the axis moving
188+ public bool SteeringSweepActive { get ; set ; } = false ;
189+
190+ private const double SteeringSweepPeriodSeconds = 2.0 ;
191+
192+ private double _steeringSweepPhase = 0.0 ;
193+
194+ private void UpdateSteeringPassthrough ( App app )
195+ {
196+ var settings = DataContext . DataContext . Instance . Settings ;
197+
198+ if ( settings . GameBridgeSendSteeringToVJoy || settings . GameBridgeSteeringTestEnabled )
199+ {
200+ if ( ! app . VirtualJoystick . Initialized && ! app . VirtualJoystick . Faulted )
201+ {
202+ app . VirtualJoystick . Initialize ( ) ;
203+ }
204+
205+ // in steering test mode the vJoy axis is driven by the test buttons on the game bridge page
206+ // instead of the real wheel - this lets the user move ONLY the vJoy axis while binding the
207+ // steering input in the game, so the game cannot mistake the real wheel for the moving axis
208+ if ( ! settings . GameBridgeSteeringTestEnabled )
209+ {
210+ SteeringSweepActive = false ;
211+
212+ app . VirtualJoystick . Steering = app . DirectInput . ForceFeedbackWheelPosition ;
213+ }
214+ else if ( SteeringSweepActive )
215+ {
216+ _steeringSweepPhase += 2.0 * Math . PI / ( SteeringSweepPeriodSeconds * App . TimerTicksPerSecond ) ;
217+
218+ app . VirtualJoystick . Steering = ( float ) Math . Sin ( _steeringSweepPhase ) ;
219+ }
220+ else
221+ {
222+ _steeringSweepPhase = 0.0 ;
223+ }
224+
225+ if ( ! _steeringPassthroughActive && app . VirtualJoystick . Initialized )
226+ {
227+ app . Logger . WriteLine ( "[GameBridge] vJoy steering passthrough started" ) ;
228+ }
229+
230+ _steeringPassthroughActive = true ;
231+ _steeringPassthroughShutdownCountdown = 2 ;
232+ }
233+ else if ( _steeringPassthroughActive )
234+ {
235+ // on the way out, center the axis first and give VirtualJoystick.Tick one frame to actually push
236+ // the zero to the device before it is released
237+ app . VirtualJoystick . Steering = 0f ;
238+
239+ _steeringPassthroughShutdownCountdown -- ;
240+
241+ if ( _steeringPassthroughShutdownCountdown <= 0 )
242+ {
243+ _steeringPassthroughActive = false ;
244+ SteeringSweepActive = false ;
245+
246+ app . Logger . WriteLine ( "[GameBridge] vJoy steering passthrough stopped" ) ;
247+
248+ if ( app . VirtualJoystick . Initialized )
249+ {
250+ app . VirtualJoystick . Shutdown ( ) ;
251+ }
252+ }
253+ }
254+ }
255+
164256 private void Activate ( App app , GameBridgeAdapter adapter )
165257 {
166258 app . Logger . WriteLine ( $ "[GameBridge] Activating the { adapter . GameName } bridge" ) ;
0 commit comments