@@ -8,8 +8,10 @@ mod commands;
88mod device_target;
99mod midi;
1010mod model;
11+ mod monitors;
1112mod plugin_api;
1213mod profile_store;
14+ mod runtime_helpers;
1315mod store_api;
1416mod windows_autostart;
1517mod windows_display;
@@ -23,45 +25,9 @@ use commands::*;
2325use device_target:: { parse_device_target, DeviceTargetKind } ;
2426use midi:: MidiManager ;
2527use model:: { LearnedControl , MidiEvent , OsdSettings , Profile } ;
28+ use monitors:: resolve_monitor_for_osd;
29+ use runtime_helpers:: { classify_learned_control, send_media_key, LearnCandidate } ;
2630use windows_autostart:: set_windows_autostart;
27- use windows_display:: { display_device_id, monitor_display_name} ;
28-
29- fn send_media_key ( vk : u16 ) {
30- use windows:: Win32 :: UI :: Input :: KeyboardAndMouse :: {
31- SendInput , INPUT , INPUT_0 , INPUT_KEYBOARD , KEYBDINPUT , KEYBD_EVENT_FLAGS , KEYEVENTF_KEYUP ,
32- VIRTUAL_KEY ,
33- } ;
34-
35- let key_down = INPUT {
36- r#type : INPUT_KEYBOARD ,
37- Anonymous : INPUT_0 {
38- ki : KEYBDINPUT {
39- wVk : VIRTUAL_KEY ( vk) ,
40- wScan : 0 ,
41- dwFlags : KEYBD_EVENT_FLAGS ( 0 ) ,
42- time : 0 ,
43- dwExtraInfo : 0 ,
44- } ,
45- } ,
46- } ;
47-
48- let key_up = INPUT {
49- r#type : INPUT_KEYBOARD ,
50- Anonymous : INPUT_0 {
51- ki : KEYBDINPUT {
52- wVk : VIRTUAL_KEY ( vk) ,
53- wScan : 0 ,
54- dwFlags : KEYEVENTF_KEYUP ,
55- time : 0 ,
56- dwExtraInfo : 0 ,
57- } ,
58- } ,
59- } ;
60-
61- unsafe {
62- SendInput ( & [ key_down, key_up] , std:: mem:: size_of :: < INPUT > ( ) as i32 ) ;
63- }
64- }
6531
6632use profile_store:: ProfileStore ;
6733use std:: collections:: HashMap ;
@@ -76,13 +42,12 @@ use tauri::{
7642} ;
7743use tokio:: time:: sleep;
7844
45+ pub ( crate ) use monitors:: collect_monitor_descriptors;
7946use plugin_api:: {
8047 ensure_builtin_plugin, get_plugins_dir, hue_api_get, hue_api_put, hue_discover_bridges,
8148 hue_pair_bridge, install_plugin_package, list_plugins, read_plugin_base64, read_plugin_text,
8249 set_plugin_enabled, uninstall_plugin,
8350} ;
84- use std:: thread:: sleep as thread_sleep;
85- use std:: time:: Duration as StdDuration ;
8651use store_api:: { fetch_store_catalog, install_store_plugin} ;
8752use ws_bridge:: { get_wavelink_ws_port, ws_close, ws_open, ws_send, WsHub } ;
8853
@@ -92,34 +57,6 @@ use audio::windows::WindowsAudioBackend;
9257#[ cfg( not( target_os = "windows" ) ) ]
9358use audio:: unsupported:: UnsupportedAudioBackend ;
9459
95- #[ derive( Debug , Clone ) ]
96- struct LearnCandidate {
97- control : LearnedControl ,
98- last_seen_at : Instant ,
99- saw_zero : bool ,
100- saw_max : bool ,
101- }
102-
103- fn classify_cc_candidate ( saw_zero : bool , saw_max : bool ) -> model:: BindingControlKind {
104- if saw_zero && saw_max {
105- model:: BindingControlKind :: Button
106- } else {
107- model:: BindingControlKind :: Continuous
108- }
109- }
110-
111- fn classify_learned_control ( candidate : & LearnCandidate ) -> LearnedControl {
112- let mut learned = candidate. control . clone ( ) ;
113- learned. control_kind = match learned. msg_type {
114- model:: MidiMessageType :: Note => model:: BindingControlKind :: Button ,
115- model:: MidiMessageType :: ControlChange => {
116- classify_cc_candidate ( candidate. saw_zero , candidate. saw_max )
117- }
118- model:: MidiMessageType :: PitchBend => model:: BindingControlKind :: Continuous ,
119- } ;
120- learned
121- }
122-
12360struct AppState {
12461 audio : Box < dyn AudioBackend > ,
12562 midi : Arc < Mutex < MidiManager > > ,
@@ -138,101 +75,6 @@ struct AppState {
13875 app_settings : Mutex < AppSettings > ,
13976}
14077
141- #[ derive( Clone ) ]
142- pub ( crate ) struct MonitorDescriptor {
143- pub index : usize ,
144- pub friendly_name : String ,
145- pub stable_id : String ,
146- pub is_primary : bool ,
147- pub monitor : tauri:: Monitor ,
148- }
149-
150- pub ( crate ) fn collect_monitor_descriptors (
151- app : & AppHandle ,
152- ) -> Result < Vec < MonitorDescriptor > , String > {
153- let monitors = app
154- . available_monitors ( )
155- . map_err ( |_| "Failed to load monitors" . to_string ( ) ) ?;
156- let primary = app. primary_monitor ( ) . ok ( ) . flatten ( ) ;
157-
158- Ok ( monitors
159- . iter ( )
160- . enumerate ( )
161- . map ( |( index, monitor) | {
162- let raw_name = monitor
163- . name ( )
164- . cloned ( )
165- . unwrap_or_else ( || format ! ( "Monitor {}" , index + 1 ) ) ;
166- let stable_id = display_device_id ( & raw_name) . unwrap_or_else ( || raw_name. clone ( ) ) ;
167- let friendly_name = monitor_display_name ( & raw_name) . unwrap_or_else ( || raw_name. clone ( ) ) ;
168- let is_primary = primary
169- . as_ref ( )
170- . map ( |p| {
171- p. name ( ) == monitor. name ( )
172- && p. size ( ) == monitor. size ( )
173- && p. position ( ) == monitor. position ( )
174- } )
175- . unwrap_or ( false ) ;
176-
177- MonitorDescriptor {
178- index,
179- friendly_name,
180- stable_id,
181- is_primary,
182- monitor : monitor. clone ( ) ,
183- }
184- } )
185- . collect ( ) )
186- }
187-
188- #[ derive( Clone ) ]
189- struct SelectedMonitor {
190- monitor : tauri:: Monitor ,
191- }
192-
193- fn resolve_monitor_for_osd ( app : & AppHandle , settings : & OsdSettings ) -> Option < SelectedMonitor > {
194- let requested_id = settings. monitor_id . as_ref ( ) . and_then ( |id| {
195- let trimmed = id. trim ( ) ;
196- if trimmed. is_empty ( ) {
197- None
198- } else {
199- Some ( trimmed. to_string ( ) )
200- }
201- } ) ;
202-
203- // Retry only when an explicit monitor ID was requested but not yet enumerated.
204- let max_attempts = if requested_id. is_some ( ) { 7 } else { 1 } ;
205-
206- for attempt in 0 ..max_attempts {
207- let descriptors = collect_monitor_descriptors ( app) . ok ( ) ?;
208-
209- if let Some ( ref id) = requested_id {
210- if let Some ( found) = descriptors. iter ( ) . find ( |m| m. stable_id == * id) {
211- return Some ( SelectedMonitor {
212- monitor : found. monitor . clone ( ) ,
213- } ) ;
214- }
215-
216- if attempt + 1 < max_attempts {
217- thread_sleep ( StdDuration :: from_millis ( 250 ) ) ;
218- continue ;
219- }
220- }
221-
222- if let Some ( primary) = descriptors
223- . iter ( )
224- . find ( |m| m. is_primary )
225- . or_else ( || descriptors. first ( ) )
226- {
227- return Some ( SelectedMonitor {
228- monitor : primary. monitor . clone ( ) ,
229- } ) ;
230- }
231- }
232-
233- None
234- }
235-
23678impl AppState {
23779 fn apply_osd_settings ( app : & AppHandle , settings : & OsdSettings ) {
23880 let Some ( osd_window) = app. get_webview_window ( "osd" ) else {
@@ -271,7 +113,7 @@ impl AppState {
271113
272114 let selected = resolve_monitor_for_osd ( app, settings) ;
273115 if let Some ( selected) = selected {
274- let monitor = selected. monitor ;
116+ let monitor = selected;
275117 let scale_factor = monitor. scale_factor ( ) ;
276118 let size = monitor. size ( ) ;
277119 let position = monitor. position ( ) ;
0 commit comments