@@ -83,6 +83,25 @@ public MainWindow()
8383 return true ;
8484 return false ;
8585 } ;
86+ _viewModel . Settings . HasAnyMidiSlots = ( ) =>
87+ {
88+ for ( int i = 0 ; i < InputManager . MaxPads ; i ++ )
89+ if ( SettingsManager . SlotCreated [ i ] &&
90+ _viewModel . Pads [ i ] . OutputType == VirtualControllerType . Midi )
91+ return true ;
92+ return false ;
93+ } ;
94+ _viewModel . Settings . HasAnyHidHideDevices = ( ) =>
95+ {
96+ var devices = SettingsManager . UserDevices ;
97+ if ( devices == null ) return false ;
98+ lock ( devices . SyncRoot )
99+ {
100+ foreach ( var ud in devices . Items )
101+ if ( ud . HidHideEnabled ) return true ;
102+ }
103+ return false ;
104+ } ;
86105
87106 // Wire engine start/stop commands.
88107 _viewModel . StartEngineRequested += ( s , e ) => _inputService . Start ( ) ;
@@ -116,11 +135,23 @@ public MainWindow()
116135 _viewModel . Settings . LoadProfileRequested += OnLoadProfile ;
117136 _viewModel . Settings . RevertToDefaultRequested += OnRevertToDefault ;
118137
119- // Apply registry Run key when Start at Login is toggled .
138+ // Persist Settings VM changes (theme, polling, checkboxes) and handle login toggle .
120139 _viewModel . Settings . PropertyChanged += ( s , e ) =>
121140 {
122141 if ( e . PropertyName == nameof ( SettingsViewModel . StartAtLogin ) )
123142 Common . StartupHelper . SetStartupEnabled ( _viewModel . Settings . StartAtLogin ) ;
143+
144+ if ( e . PropertyName is nameof ( SettingsViewModel . SelectedThemeIndex )
145+ or nameof ( SettingsViewModel . AutoStartEngine )
146+ or nameof ( SettingsViewModel . MinimizeToTray )
147+ or nameof ( SettingsViewModel . StartMinimized )
148+ or nameof ( SettingsViewModel . StartAtLogin )
149+ or nameof ( SettingsViewModel . EnablePollingOnFocusLoss )
150+ or nameof ( SettingsViewModel . PollingRateMs )
151+ or nameof ( SettingsViewModel . EnableInputHiding )
152+ or nameof ( SettingsViewModel . Use2DControllerView )
153+ or nameof ( SettingsViewModel . EnableAutoProfileSwitching ) )
154+ _settingsService . MarkDirty ( ) ;
124155 } ;
125156
126157 // Persist DSU / web controller server settings on change (Dashboard VM).
@@ -151,7 +182,7 @@ or nameof(DashboardViewModel.WebControllerPort))
151182 _viewModel . Settings . UninstallVJoyRequested += async ( s , e ) => await RunDriverOperationAsync (
152183 "Uninstalling vJoy…" , DriverInstaller . UninstallVJoy , OnVJoyDriverChanged ) ;
153184
154- // Wire MIDI Services install command .
185+ // Wire MIDI Services install/uninstall commands .
155186 _viewModel . Settings . InstallMidiServicesRequested += async ( s , e ) =>
156187 {
157188 _viewModel . StatusText = "Downloading Windows MIDI Services…" ;
@@ -176,6 +207,15 @@ or nameof(DashboardViewModel.WebControllerPort))
176207 RefreshMidiServicesStatus ( ) ;
177208 }
178209 } ;
210+ _viewModel . Settings . UninstallMidiServicesRequested += async ( s , e ) =>
211+ {
212+ // The uninstall guard prevents this when MIDI slots are active, so the
213+ // SDK runtime won't be loaded in-process. Safe to wait for the uninstaller.
214+ // Abandon the initializer just in case (e.g. IsAvailable was called elsewhere).
215+ Common . Input . MidiVirtualController . Shutdown ( skipDispose : true ) ;
216+ await RunDriverOperationAsync (
217+ "Uninstalling Windows MIDI Services…" , DriverInstaller . UninstallMidiServices , RefreshMidiServicesStatus ) ;
218+ } ;
179219
180220 // Wire device service events (assign to slot, hide, etc.).
181221 _deviceService . WireEvents ( ) ;
@@ -191,6 +231,7 @@ or nameof(DashboardViewModel.WebControllerPort))
191231 _deviceService . DeviceHidingStateChanged += ( s , e ) =>
192232 {
193233 _inputService . ApplyDeviceHiding ( ) ;
234+ _viewModel . Settings . RefreshDriverGuards ( ) ;
194235 } ;
195236
196237 // After assigning a device to a slot, navigate to that controller page.
@@ -1873,7 +1914,7 @@ private bool HasAnyControllerTypeCapacity()
18731914 return xboxCount < SettingsManager . MaxXbox360Slots
18741915 || ds4Count < SettingsManager . MaxDS4Slots
18751916 || vjoyCount < SettingsManager . MaxVJoySlots
1876- || ( Common . Input . MidiVirtualController . IsAvailable ( ) && midiCount < SettingsManager . MaxMidiSlots ) ;
1917+ || ( DriverInstaller . IsMidiServicesInstalled ( ) && midiCount < SettingsManager . MaxMidiSlots ) ;
18771918 }
18781919
18791920 private void ShowControllerTypePopup ( UIElement anchor , PlacementMode placement = PlacementMode . Right )
@@ -2078,7 +2119,7 @@ private void ShowControllerTypePopup(UIElement anchor, PlacementMode placement =
20782119 Stretch = System . Windows . Media . Stretch . Uniform
20792120 } ;
20802121 midiPopupPath . SetResourceReference ( System . Windows . Shapes . Shape . FillProperty , "SystemControlForegroundBaseHighBrush" ) ;
2081- bool midiAvailable = Common . Input . MidiVirtualController . IsAvailable ( ) ;
2122+ bool midiAvailable = DriverInstaller . IsMidiServicesInstalled ( ) ;
20822123 bool midiAtCapacity = midiCount >= SettingsManager . MaxMidiSlots ;
20832124 bool midiDisabled = ! midiAvailable || midiAtCapacity ;
20842125 string midiTooltip = ! midiAvailable ? "MIDI (requires Windows MIDI Services)"
0 commit comments