1use crate::types::*;
7use crate::wire::{
8 BayUid, DeviceUid, EdidProfile, LinkFeature, RcAction, RcKey, RcType, V2ipFpgaFeature,
9};
10
11macro_rules! events {
24 (
25 device {
26 $( $(#[$dmeta:meta])* $dvariant:ident => $dmethod:ident ( $($darg:ident : $dty:ty),* ); )*
27 }
28 bay {
29 $( $(#[$bmeta:meta])* $bvariant:ident => $bmethod:ident ( $($barg:ident : $bty:ty),* ); )*
30 }
31 bay_and_device {
32 $( $(#[$lmeta:meta])* $lvariant:ident => $lmethod:ident ( $($larg:ident : $lty:ty),* ); )*
33 }
34 ) => {
35 #[derive(Clone, Debug, PartialEq)]
40 #[non_exhaustive]
41 pub enum Event {
42 $(
43 $(#[$dmeta])*
44 $dvariant {
45 device: DeviceUid,
47 $( #[allow(missing_docs)] $darg: $dty, )*
48 },
49 )*
50 $(
51 $(#[$bmeta])*
52 $bvariant {
53 bay: BayUid,
55 $( #[allow(missing_docs)] $barg: $bty, )*
56 },
57 )*
58 $(
59 $(#[$lmeta])*
60 $lvariant {
61 bay: BayUid,
63 $( #[allow(missing_docs)] $larg: $lty, )*
64 },
65 )*
66 }
67
68 #[allow(unused_variables)]
76 pub trait EventHandler: Send + Sync {
77 $(
78 $(#[$dmeta])*
79 fn $dmethod(&self, device: DeviceUid $(, $darg: $dty)*) {}
80 )*
81 $(
82 $(#[$bmeta])*
83 fn $bmethod(&self, bay: BayUid $(, $barg: $bty)*) {}
84 )*
85 $(
86 $(#[$lmeta])*
87 fn $lmethod(&self, bay: BayUid $(, $larg: $lty)*) {}
88 )*
89
90 fn on_device_update(&self, device: DeviceUid) {}
92
93 fn on_bay_update(&self, bay: BayUid) {}
95 }
96
97 impl Event {
98 fn bay(&self) -> Option<BayUid> {
100 match self {
101 $( Self::$bvariant { bay, .. } => Some(*bay), )*
102 _ => None,
103 }
104 }
105
106 pub(crate) fn for_bay(&self, to: BayUid) -> Self {
108 let mut event = self.clone();
109 match &mut event {
110 $( Self::$bvariant { bay, .. } => *bay = to, )*
111 _ => {}
112 }
113 event
114 }
115
116 pub(crate) fn dispatch(self, handler: &dyn EventHandler) {
119 match self {
120 $(
121 Self::$dvariant { device $(, $darg)* } => {
122 handler.$dmethod(device $(, $darg)*);
123 handler.on_device_update(device);
124 }
125 )*
126 $(
127 Self::$bvariant { bay $(, $barg)* } => {
128 handler.$bmethod(bay $(, $barg)*);
129 handler.on_bay_update(bay);
130 }
131 )*
132 $(
133 Self::$lvariant { bay $(, $larg)* } => {
134 handler.$lmethod(bay $(, $larg)*);
135 handler.on_bay_update(bay);
136 handler.on_device_update(bay.device);
137 }
138 )*
139 }
140 }
141 }
142 };
143}
144
145impl Event {
146 pub(crate) fn mirrored_bay(&self) -> Option<BayUid> {
150 match self {
151 Self::BayRegistered { .. }
152 | Self::KeyPressed { .. }
153 | Self::ActionReceived { .. }
154 | Self::VolumeStep { .. }
155 | Self::AudioClipped { .. }
156 | Self::IrCaptured { .. } => None,
157 _ => self.bay(),
158 }
159 }
160}
161
162impl EventHandler for () {}
169
170events! {
171 device {
172 DeviceConfigChanged => on_device_config_changed();
174 DeviceConfigComplete => on_device_config_complete();
176 DeviceOnlineChanged => on_device_online_changed(online: bool);
178 DeviceTemperatureChanged => on_device_temperature_changed(temperatures: Vec<i8>);
180 FirmwareVersionChanged => on_firmware_version_changed(version: FirmwareVersion);
182 SystemStatusChanged => on_system_status_changed(status: u16, message: String);
184 NetworkStatusChanged => on_network_status_changed(status: NetworkPortStatus);
186 V2ipStatsChanged => on_v2ip_stats_changed(stats: V2ipDeviceStats);
188 V2ipSourcesChanged => on_v2ip_sources_changed(sources: Vec<V2ipStreamSources>);
190 V2ipDetailsChanged => on_v2ip_details_changed(details: DeviceV2ipDetails);
192 V2ipSinkChanged => on_v2ip_sink_changed(sink: DeviceV2ipSink);
195 MultiviewerStatusChanged => on_multiviewer_status_changed(status: MultiviewerStatus);
197 AudioEndpointsChanged => on_audio_endpoints_changed(endpoints: AudioEndpoints);
200 MeshMasterChanged => on_mesh_master_changed(master: DeviceUid);
202 TopologyChanged => on_topology_changed(topology: Vec<TopologyEntry>);
204 AmpDolbySettingsChanged => on_amp_dolby_settings_changed(settings: AmpDolbySettings);
206 SetupStatusChanged => on_setup_status_changed(completed: bool);
208 InstallerIdChanged => on_installer_id_changed(installer_id: u16);
210 TilingChanged => on_tiling_changed(tiling: V2ipTilingConfig);
212 V2ipFeaturesChanged => on_v2ip_features_changed(features: V2ipFpgaFeature);
214 V2ipDeviceSettingsChanged => on_v2ip_device_settings_changed(settings: V2ipDeviceSettings);
216 V2ipVlanChanged => on_v2ip_vlan_changed(vlan: V2ipVlan);
218 V2ipSinkSdpChanged => on_v2ip_sink_sdp_changed(sdp: V2ipSinkSdp);
221 V2ipPrivacyChanged => on_v2ip_privacy_changed(privacy: V2ipPrivacy);
223 OsdSyncChanged => on_osd_sync_changed(sync: OsdSync);
225 OsdSyncReportChanged => on_osd_sync_report_changed(report: OsdSyncReport);
228 OsdSyncRequested => on_osd_sync_requested(request: OsdSyncRequest);
230 OsdSyncSettingReceived => on_osd_sync_setting_received(setting: OsdSyncSetting);
232 UnitStatusChanged => on_unit_status_changed(status: UnitStatus);
234 V2ipPtpChanged => on_v2ip_ptp_changed(ptp: Option<V2ipPtpState>);
237 V2ipTestcardChanged => on_v2ip_testcard_changed(testcard: V2ipTestcard);
239 TimeZoneChanged => on_time_zone_changed(time_zone: TimeZone);
241 RcSettingsChanged => on_rc_settings_changed(settings: RcSettings);
243 V2ipLinkChanged => on_v2ip_link_changed(target: DeviceUid);
245 MultiviewerCommand => on_multiviewer_command(command: MultiviewerCommand);
247 AudioSelectInput => on_audio_select_input(change: AudioChangeSource);
250 AudioEndpointMute => on_audio_endpoint_mute(endpoint: u16, muted: bool);
253 AudioEndpointTrigger => on_audio_endpoint_trigger(endpoint: u16, active: bool);
255 AudioEndpointLock => on_audio_endpoint_lock(endpoint: u16, locked: bool);
258 AudioEndpointVolume => on_audio_endpoint_volume(endpoint: u16, volume: u32);
261 DiscoverRequest => on_discover_request();
263 SetRouteRequested => on_set_route_requested(request: SetRouteRequest);
265 EdidRequested => on_edid_requested(request: EdidRequest);
267 EdidReceived => on_edid_received(edid: EdidRecord);
269 BayNameChangeRequested => on_bay_name_change_requested(change: BayNameChange);
271 EdidProfileChangeRequested => on_edid_profile_change_requested(change: EdidProfileChange);
273 RebootRequested => on_reboot_requested(request: RebootRequest);
275 UnitCommandRequested => on_unit_command_requested(command: UnitCommand);
277 FactoryResetRequested => on_factory_reset_requested(request: FactoryResetRequest);
279 MonitoringPulse => on_monitoring_pulse();
281 UpgradeFpgaRequested => on_upgrade_fpga_requested();
283 DetectBaysRequested => on_detect_bays_requested();
285 PowerSaveRequested => on_power_save_requested(request: V2ipPowerSaveRequest);
287 KeyTransmitRequested => on_key_transmit_requested(request: KeyTransmitRequest);
289 ActionTransmitRequested => on_action_transmit_requested(request: ActionTransmitRequest);
291 IrTransmitRequested => on_ir_transmit_requested(request: IrTransmitRequest);
293 BlacklistChanged => on_blacklist_changed(change: V2ipBlacklistChange);
296 VideoWallCommand => on_video_wall_command(command: VideoWallCommand);
298 }
299 bay {
300 BayRegistered => on_bay_registered();
302 VideoSourceChanged => on_video_source_changed(source: Option<BayUid>);
304 AudioSourceChanged => on_audio_source_changed(source: Option<BayUid>);
306 VolumeChanged => on_volume_changed(volume: VolumeMuteStatus);
308 PowerChanged => on_power_changed(power: PowerStatus);
310 NameChanged => on_name_changed(name: String);
312 SignalDetectedChanged => on_signal_detected_changed(detected: bool);
314 FaultyChanged => on_faulty_changed(faulty: bool);
316 HiddenChanged => on_hidden_changed(hidden: bool);
318 ZonePoweredChanged => on_zone_powered_changed(powered: bool);
320 HdbtConnectedChanged => on_hdbt_connected_changed(connected: bool);
322 SignalTypeChanged => on_signal_type_changed(signal_type: String);
324 HpdDetectedChanged => on_hpd_detected_changed(detected: bool);
326 CecDetectedChanged => on_cec_detected_changed(detected: bool);
328 ArcChanged => on_arc_changed(arc: ArcStatus);
330 EdidProfileChanged => on_edid_profile_changed(profile: EdidProfile);
332 RcTypeChanged => on_rc_type_changed(rc_type: RcType);
334 KeyPressed => on_key_pressed(key: RcKey);
336 ActionReceived => on_action_received(action: RcAction);
338 MirrorStatusChanged => on_mirror_status_changed(mirror: BayMirrorStatus);
340 AmpZoneSettingsChanged => on_amp_zone_settings_changed(settings: AmpZoneSettings);
342 VolumeStep => on_volume_step(up: bool);
344 AudioClipped => on_audio_clip(clip: AudioClip);
346 IrCaptured => on_ir_captured(capture: IrCapture);
348 FilteredDevicesChanged => on_filtered_devices_changed(filtered: Vec<DeviceUid>);
350 AudioEndpointChanged => on_audio_endpoint_changed(endpoint: u8);
352 EncoderDisabledChanged => on_encoder_disabled_changed(disabled: bool);
354 DecoderDisabledChanged => on_decoder_disabled_changed(disabled: bool);
356 ContentChanged => on_content_changed(content: Vec<BayContentField>);
359 }
360 bay_and_device {
361 BayLinked => on_bay_linked(linked_serial: String, bay_name: String, features: LinkFeature);
368 BayUnlinked => on_bay_unlinked(linked_serial: String, bay_name: String);
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379 use std::sync::Mutex;
380
381 #[derive(Default)]
382 struct Recorder {
383 calls: Mutex<Vec<String>>,
384 }
385
386 impl Recorder {
387 fn record(&self, what: &str) {
388 if let Ok(mut calls) = self.calls.lock() {
389 calls.push(what.to_owned());
390 }
391 }
392
393 fn calls(&self) -> Vec<String> {
394 self.calls.lock().map(|c| c.clone()).unwrap_or_default()
395 }
396 }
397
398 impl EventHandler for Recorder {
399 fn on_power_changed(&self, _bay: BayUid, power: PowerStatus) {
400 self.record(&format!("power={power}"));
401 }
402
403 fn on_setup_status_changed(&self, _device: DeviceUid, completed: bool) {
404 self.record(&format!("setup={completed}"));
405 }
406
407 fn on_bay_update(&self, _bay: BayUid) {
408 self.record("bay_update");
409 }
410
411 fn on_device_update(&self, _device: DeviceUid) {
412 self.record("device_update");
413 }
414 }
415
416 const DEVICE: DeviceUid = DeviceUid::from_array([9; 16]);
417
418 #[test]
419 fn a_bay_event_fires_its_own_method_then_the_generic_bay_update() {
420 let recorder = Recorder::default();
421 Event::PowerChanged {
422 bay: BayUid::new(DEVICE, 3),
423 power: PowerStatus::On,
424 }
425 .dispatch(&recorder);
426 assert_eq!(recorder.calls(), ["power=on", "bay_update"]);
427 }
428
429 #[test]
430 fn a_device_event_fires_its_own_method_then_the_generic_device_update() {
431 let recorder = Recorder::default();
432 Event::SetupStatusChanged {
433 device: DEVICE,
434 completed: true,
435 }
436 .dispatch(&recorder);
437 assert_eq!(recorder.calls(), ["setup=true", "device_update"]);
438 }
439
440 #[test]
441 fn a_link_event_fires_both_generic_updates() {
442 let recorder = Recorder::default();
443 Event::BayUnlinked {
444 bay: BayUid::new(DEVICE, 3),
445 linked_serial: "AB1234".to_owned(),
446 bay_name: "Output 1".to_owned(),
447 }
448 .dispatch(&recorder);
449 assert_eq!(recorder.calls(), ["bay_update", "device_update"]);
450 }
451
452 #[test]
453 fn an_event_a_handler_does_not_name_still_fires_the_generic_update() {
454 let recorder = Recorder::default();
455 Event::MonitoringPulse { device: DEVICE }.dispatch(&recorder);
456 assert_eq!(
457 recorder.calls(),
458 ["device_update"],
459 "the default no-op must not swallow the fan-in"
460 );
461 }
462}