1use std::sync::atomic::{AtomicUsize, Ordering};
18use std::sync::{Arc, PoisonError, RwLock};
19
20use hidpp::channel::{HidppChannel, HidppMessage, MessageListenerGuard};
21use hidpp::feature::adc_measurement::AdcMeasurementFeature;
22use hidpp::feature::backlight::BacklightFeature;
23use hidpp::feature::battery_status::BatteryStatusFeature;
24use hidpp::feature::battery_voltage::BatteryVoltageFeature;
25use hidpp::feature::extended_dpi::ExtendedDpiFeature;
26use hidpp::feature::extended_report_rate::ExtendedReportRateFeature;
27use hidpp::feature::fn_inversion::{
28 FnInversionMultiHostFeature, FnInversionWithDefaultStateFeature,
29};
30use hidpp::feature::multi_platform::PlatformChange;
31use hidpp::feature::unified_battery::BatteryEvent;
32use hidpp::feature::wireless_device_status::WirelessDeviceStatusEvent;
33use hidpp::protocol::{v10, v20};
34use hidpp::receiver::{bolt, unifying};
35use serde::{Deserialize, Serialize};
36#[cfg(test)]
37use tokio::sync::Notify;
38use tokio::sync::mpsc;
39
40use crate::ReceiverProtocol;
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum HidppEventSource {
46 ReceiverConnection,
48 WirelessDeviceStatus,
50 UnifiedBattery,
52 BatteryStatus,
55 BatteryVoltage,
58 AdcMeasurement,
61 FnInversion,
64 ExtendedDpi,
67 MultiPlatform,
71 ExtendedReportRate,
74 Backlight,
78}
79
80impl HidppEventSource {
81 const fn changes_settings(self) -> bool {
84 matches!(
85 self,
86 Self::FnInversion
87 | Self::ExtendedDpi
88 | Self::MultiPlatform
89 | Self::ExtendedReportRate
90 | Self::Backlight
91 )
92 }
93}
94
95#[derive(Clone)]
101pub struct EventNotifier {
102 sender: mpsc::Sender<HidppEventSource>,
103 #[cfg(test)]
104 observation: Option<Arc<EventObservation>>,
105}
106
107impl EventNotifier {
108 fn notify(&self, source: HidppEventSource) {
109 let _ = self.sender.try_send(source);
110 #[cfg(test)]
111 if let Some(observation) = &self.observation {
112 observation.count.fetch_add(1, Ordering::Release);
113 observation.changed.notify_waiters();
114 }
115 }
116}
117
118pub type EventReceiver = mpsc::Receiver<HidppEventSource>;
120
121#[must_use]
123pub fn event_channel() -> (EventNotifier, EventReceiver) {
124 let (sender, receiver) = mpsc::channel(1);
125 (
126 EventNotifier {
127 sender,
128 #[cfg(test)]
129 observation: None,
130 },
131 receiver,
132 )
133}
134
135#[cfg(test)]
136struct EventObservation {
137 count: AtomicUsize,
138 changed: Notify,
139}
140
141#[cfg(test)]
143pub(crate) struct EventObserver {
144 observation: Arc<EventObservation>,
145}
146
147#[cfg(test)]
148impl EventObserver {
149 pub(crate) async fn wait_for(&self, expected: usize) {
150 loop {
151 let changed = self.observation.changed.notified();
152 if self.observation.count.load(Ordering::Acquire) >= expected {
153 return;
154 }
155 changed.await;
156 }
157 }
158}
159
160#[cfg(test)]
162pub(crate) fn observed_event_channel() -> (EventNotifier, EventReceiver, EventObserver) {
163 let (sender, receiver) = mpsc::channel(1);
164 let observation = Arc::new(EventObservation {
165 count: AtomicUsize::new(0),
166 changed: Notify::new(),
167 });
168 (
169 EventNotifier {
170 sender,
171 observation: Some(Arc::clone(&observation)),
172 },
173 receiver,
174 EventObserver { observation },
175 )
176}
177
178#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
188pub(super) struct EventFeatureIndices {
189 pub(super) wireless_status: Option<u8>,
190 pub(super) unified_battery: Option<u8>,
191 pub(super) battery_status: Option<u8>,
192 pub(super) battery_voltage: Option<u8>,
193 pub(super) adc_measurement: Option<u8>,
194 pub(super) fn_inversion: Option<u8>,
195 pub(super) fn_inversion_multi_host: Option<u8>,
196 pub(super) extended_dpi: Option<u8>,
197 pub(super) multi_platform: Option<u8>,
198 pub(super) extended_report_rate: Option<u8>,
199 pub(super) backlight: Option<u8>,
200}
201
202impl EventFeatureIndices {
203 pub(super) fn from_feature_ids(ids: &[u16]) -> Self {
204 let mut indices = Self::default();
205 for (position, id) in ids.iter().copied().enumerate() {
206 let Ok(index) = u8::try_from(position + 1) else {
207 break;
208 };
209 match id {
210 0x1d4b => indices.wireless_status = Some(index),
211 0x1004 => indices.unified_battery = Some(index),
212 0x1000 => indices.battery_status = Some(index),
213 0x1001 => indices.battery_voltage = Some(index),
214 0x1f20 => indices.adc_measurement = Some(index),
215 0x40a2 => indices.fn_inversion = Some(index),
216 0x40a3 => indices.fn_inversion_multi_host = Some(index),
217 0x2202 => indices.extended_dpi = Some(index),
218 0x4531 => indices.multi_platform = Some(index),
219 0x8061 => indices.extended_report_rate = Some(index),
220 0x1982 => indices.backlight = Some(index),
221 _ => {}
222 }
223 }
224 indices
225 }
226
227 fn recognizes(self, message: &v20::Message) -> Option<HidppEventSource> {
228 let header = message.header();
229 if header.software_id.to_lo() != 0 {
230 return None;
231 }
232 let function_id = header.function_id.to_lo();
233 let payload = message.extend_payload();
234 if self.wireless_status == Some(header.feature_index)
235 && WirelessDeviceStatusEvent::decode(function_id, &payload).is_some()
236 {
237 return Some(HidppEventSource::WirelessDeviceStatus);
238 }
239 if self.unified_battery == Some(header.feature_index)
240 && BatteryEvent::decode(function_id, &payload).is_some()
241 {
242 return Some(HidppEventSource::UnifiedBattery);
243 }
244 if self.battery_status == Some(header.feature_index)
248 && BatteryStatusFeature::is_status_broadcast(function_id)
249 {
250 return Some(HidppEventSource::BatteryStatus);
251 }
252 if self.battery_voltage == Some(header.feature_index)
253 && BatteryVoltageFeature::is_status_broadcast(function_id)
254 {
255 return Some(HidppEventSource::BatteryVoltage);
256 }
257 if self.adc_measurement == Some(header.feature_index)
258 && AdcMeasurementFeature::is_status_broadcast(function_id)
259 {
260 return Some(HidppEventSource::AdcMeasurement);
261 }
262 if self.fn_inversion == Some(header.feature_index)
264 && FnInversionWithDefaultStateFeature::is_state_broadcast(function_id)
265 {
266 return Some(HidppEventSource::FnInversion);
267 }
268 if self.fn_inversion_multi_host == Some(header.feature_index)
269 && FnInversionMultiHostFeature::is_state_broadcast(message)
270 {
271 return Some(HidppEventSource::FnInversion);
272 }
273 if self.extended_dpi == Some(header.feature_index)
274 && ExtendedDpiFeature::is_parameters_broadcast(function_id)
275 {
276 return Some(HidppEventSource::ExtendedDpi);
277 }
278 if self.multi_platform == Some(header.feature_index)
281 && PlatformChange::decode(&payload).is_some()
282 {
283 return Some(HidppEventSource::MultiPlatform);
284 }
285 if self.extended_report_rate == Some(header.feature_index)
286 && ExtendedReportRateFeature::is_rate_broadcast(function_id)
287 {
288 return Some(HidppEventSource::ExtendedReportRate);
289 }
290 if self.backlight == Some(header.feature_index)
291 && BacklightFeature::is_info_broadcast(function_id)
292 {
293 return Some(HidppEventSource::Backlight);
294 }
295 None
296 }
297}
298
299#[derive(Clone, Copy)]
300struct DeviceEvents {
301 device_index: u8,
302 features: EventFeatureIndices,
303 settings_revision: u64,
307}
308
309struct SubscriptionState {
310 protocol: Option<ReceiverProtocol>,
311 devices: RwLock<Vec<DeviceEvents>>,
312 receiver_snapshot_depth: AtomicUsize,
313 notifier: EventNotifier,
314}
315
316impl SubscriptionState {
317 fn decode(&self, raw: HidppMessage, matched: bool) -> Option<HidppEventSource> {
318 if matched {
319 return None;
320 }
321
322 let receiver_connection = match self.protocol {
323 Some(ReceiverProtocol::Bolt) => matches!(
324 bolt::decode_notification(&v10::Message::from(raw)),
325 Some(bolt::Event::DeviceConnection(_))
326 ),
327 Some(ReceiverProtocol::Unifying) => matches!(
328 unifying::decode_notification(&v10::Message::from(raw)),
329 Some(unifying::Event::DeviceConnection(_))
330 ),
331 None => false,
332 };
333 if receiver_connection {
334 return (self.receiver_snapshot_depth.load(Ordering::Acquire) == 0)
335 .then_some(HidppEventSource::ReceiverConnection);
336 }
337
338 let message = v20::Message::from(raw);
339 let device_index = message.header().device_index;
340 let source = self
341 .devices
342 .read()
343 .unwrap_or_else(PoisonError::into_inner)
344 .iter()
345 .find(|device| device.device_index == device_index)
346 .and_then(|device| device.features.recognizes(&message))?;
347 if source.changes_settings() {
348 if let Some(device) = self
351 .devices
352 .write()
353 .unwrap_or_else(PoisonError::into_inner)
354 .iter_mut()
355 .find(|device| device.device_index == device_index)
356 {
357 device.settings_revision = device.settings_revision.wrapping_add(1);
358 }
359 }
360 Some(source)
361 }
362}
363
364#[derive(Clone)]
366pub(super) struct EventSubscriptionHandle {
367 state: Arc<SubscriptionState>,
368}
369
370impl EventSubscriptionHandle {
371 pub(super) fn register_device(&self, device_index: u8, features: EventFeatureIndices) {
376 let mut devices = self
377 .state
378 .devices
379 .write()
380 .unwrap_or_else(PoisonError::into_inner);
381 if let Some(device) = devices
382 .iter_mut()
383 .find(|device| device.device_index == device_index)
384 {
385 device.features = features;
386 } else {
387 devices.push(DeviceEvents {
388 device_index,
389 features,
390 settings_revision: 0,
391 });
392 }
393 }
394
395 pub(super) fn settings_revision(&self, device_index: u8) -> u64 {
398 self.state
399 .devices
400 .read()
401 .unwrap_or_else(PoisonError::into_inner)
402 .iter()
403 .find(|device| device.device_index == device_index)
404 .map_or(0, |device| device.settings_revision)
405 }
406
407 pub(super) fn begin_receiver_snapshot(&self) -> ReceiverSnapshotGuard {
411 self.state
412 .receiver_snapshot_depth
413 .fetch_add(1, Ordering::AcqRel);
414 ReceiverSnapshotGuard {
415 state: Arc::clone(&self.state),
416 }
417 }
418}
419
420pub(super) struct ChannelEventSubscriptions {
422 handle: EventSubscriptionHandle,
423 _listener: MessageListenerGuard,
424}
425
426impl ChannelEventSubscriptions {
427 pub(super) fn attach(
428 channel: &Arc<HidppChannel>,
429 protocol: Option<ReceiverProtocol>,
430 notifier: EventNotifier,
431 ) -> Self {
432 let state = Arc::new(SubscriptionState {
433 protocol,
434 devices: RwLock::new(Vec::new()),
435 receiver_snapshot_depth: AtomicUsize::new(0),
436 notifier,
437 });
438 let listener = channel.add_msg_listener_guarded({
439 let state = Arc::clone(&state);
440 move |raw, matched| {
441 if let Some(source) = state.decode(raw, matched) {
442 state.notifier.notify(source);
443 }
444 }
445 });
446 Self {
447 handle: EventSubscriptionHandle { state },
448 _listener: listener,
449 }
450 }
451
452 pub(super) fn handle(&self) -> EventSubscriptionHandle {
453 self.handle.clone()
454 }
455}
456
457pub(super) struct ReceiverSnapshotGuard {
459 state: Arc<SubscriptionState>,
460}
461
462impl Drop for ReceiverSnapshotGuard {
463 fn drop(&mut self) {
464 self.state
465 .receiver_snapshot_depth
466 .fetch_sub(1, Ordering::AcqRel);
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use hidpp::nibble::U4;
473 use hidpp::protocol::{v10, v20};
474
475 use super::*;
476
477 fn state(protocol: Option<ReceiverProtocol>) -> Arc<SubscriptionState> {
478 let (notifier, _receiver) = event_channel();
479 Arc::new(SubscriptionState {
480 protocol,
481 devices: RwLock::new(Vec::new()),
482 receiver_snapshot_depth: AtomicUsize::new(0),
483 notifier,
484 })
485 }
486
487 #[test]
488 fn feature_indices_are_runtime_table_positions() {
489 assert_eq!(
490 EventFeatureIndices::from_feature_ids(&[0x0001, 0x1d4b, 0x1004]),
491 EventFeatureIndices {
492 wireless_status: Some(2),
493 unified_battery: Some(3),
494 battery_status: None,
495 battery_voltage: None,
496 adc_measurement: None,
497 fn_inversion: None,
498 fn_inversion_multi_host: None,
499 extended_dpi: None,
500 multi_platform: None,
501 extended_report_rate: None,
502 backlight: None,
503 }
504 );
505 }
506
507 #[test]
508 fn receiver_snapshot_suppresses_only_triggered_connection_wakeups() {
509 let state = state(Some(ReceiverProtocol::Unifying));
510 let raw = v10::Message::Short(
511 v10::MessageHeader {
512 device_index: 2,
513 sub_id: 0x41,
514 },
515 [0, 0x02, 0x34, 0x12],
516 )
517 .into();
518
519 assert_eq!(
520 state.decode(raw, false),
521 Some(HidppEventSource::ReceiverConnection)
522 );
523 state.receiver_snapshot_depth.store(1, Ordering::Release);
524 assert_eq!(state.decode(raw, false), None);
525 }
526
527 #[test]
528 fn receiver_snapshot_does_not_suppress_device_feature_events() {
529 let state = state(Some(ReceiverProtocol::Bolt));
530 state.devices.write().unwrap().push(DeviceEvents {
531 device_index: 2,
532 settings_revision: 0,
533 features: EventFeatureIndices {
534 wireless_status: Some(5),
535 unified_battery: None,
536 battery_status: None,
537 battery_voltage: None,
538 adc_measurement: None,
539 fn_inversion: None,
540 fn_inversion_multi_host: None,
541 extended_dpi: None,
542 multi_platform: None,
543 extended_report_rate: None,
544 backlight: None,
545 },
546 });
547 state.receiver_snapshot_depth.store(1, Ordering::Release);
548 let wireless = v20::Message::Long(
549 v20::MessageHeader {
550 device_index: 2,
551 feature_index: 5,
552 function_id: U4::from_lo(0),
553 software_id: U4::from_lo(0),
554 },
555 [0; 16],
556 )
557 .into();
558
559 assert_eq!(
560 state.decode(wireless, false),
561 Some(HidppEventSource::WirelessDeviceStatus)
562 );
563 }
564
565 #[test]
566 fn registered_device_events_decode_to_typed_sources() {
567 let state = state(None);
568 state.devices.write().unwrap().push(DeviceEvents {
569 device_index: 3,
570 settings_revision: 0,
571 features: EventFeatureIndices {
572 wireless_status: Some(5),
573 unified_battery: Some(7),
574 battery_status: None,
575 battery_voltage: None,
576 adc_measurement: None,
577 fn_inversion: None,
578 fn_inversion_multi_host: None,
579 extended_dpi: None,
580 multi_platform: None,
581 extended_report_rate: None,
582 backlight: None,
583 },
584 });
585
586 let wireless = v20::Message::Long(
587 v20::MessageHeader {
588 device_index: 3,
589 feature_index: 5,
590 function_id: U4::from_lo(0),
591 software_id: U4::from_lo(0),
592 },
593 [0; 16],
594 )
595 .into();
596 assert_eq!(
597 state.decode(wireless, false),
598 Some(HidppEventSource::WirelessDeviceStatus)
599 );
600
601 let mut payload = [0; 16];
602 payload[0] = 80;
603 payload[1] = 4;
604 let battery = v20::Message::Long(
605 v20::MessageHeader {
606 device_index: 3,
607 feature_index: 7,
608 function_id: U4::from_lo(0),
609 software_id: U4::from_lo(0),
610 },
611 payload,
612 )
613 .into();
614 assert_eq!(
615 state.decode(battery, false),
616 Some(HidppEventSource::UnifiedBattery)
617 );
618 }
619
620 #[test]
621 fn adc_measurement_index_is_its_table_position() {
622 let table = [
624 0x0001, 0x0003, 0x0005, 0x8070, 0x8010, 0x8310, 0x8300, 0x1f20,
625 ];
626 assert_eq!(
627 EventFeatureIndices::from_feature_ids(&table).adc_measurement,
628 Some(8)
629 );
630 }
631
632 #[test]
633 fn adc_measurement_broadcasts_request_reconciliation() {
634 let state = state(None);
635 state.devices.write().unwrap().push(DeviceEvents {
636 device_index: 0xff,
637 settings_revision: 0,
638 features: EventFeatureIndices {
639 wireless_status: None,
640 unified_battery: None,
641 battery_status: None,
642 battery_voltage: None,
643 adc_measurement: Some(8),
644 fn_inversion: None,
645 fn_inversion_multi_host: None,
646 extended_dpi: None,
647 multi_platform: None,
648 extended_report_rate: None,
649 backlight: None,
650 },
651 });
652 let event = |function_id: u8, software_id: u8, bytes: [u8; 3]| {
653 let mut payload = [0; 16];
654 payload[..3].copy_from_slice(&bytes);
655 v20::Message::Long(
656 v20::MessageHeader {
657 device_index: 0xff,
658 feature_index: 8,
659 function_id: U4::from_lo(function_id),
660 software_id: U4::from_lo(software_id),
661 },
662 payload,
663 )
664 .into()
665 };
666
667 for bytes in [[0x00, 0x00, 0x00], [0x10, 0x4a, 0x03]] {
671 assert_eq!(
672 state.decode(event(0, 0, bytes), false),
673 Some(HidppEventSource::AdcMeasurement),
674 "{bytes:02x?}"
675 );
676 }
677 assert_eq!(
680 state.decode(event(0, 0, [0x10, 0x4a, 0x0f]), false),
681 Some(HidppEventSource::AdcMeasurement)
682 );
683 assert_eq!(state.decode(event(0, 0xd, [0x10, 0x4a, 0x01]), false), None);
685 assert_eq!(state.decode(event(1, 0, [0x10, 0x4a, 0x01]), false), None);
687 }
688
689 #[test]
690 fn legacy_and_voltage_battery_indexes_are_table_positions() {
691 let legacy =
693 EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x0005, 0x1000, 0x1d4b]);
694 assert_eq!(legacy.battery_status, Some(4));
695 assert_eq!(legacy.battery_voltage, None);
696 assert_eq!(legacy.unified_battery, None);
697 let voltage = EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x1001, 0x8070]);
699 assert_eq!(voltage.battery_voltage, Some(3));
700 assert_eq!(voltage.battery_status, None);
701 }
702
703 #[test]
704 fn legacy_and_voltage_battery_broadcasts_request_reconciliation() {
705 let state = state(None);
706 state.devices.write().unwrap().push(DeviceEvents {
707 device_index: 1,
708 settings_revision: 0,
709 features: EventFeatureIndices {
710 battery_status: Some(4),
711 battery_voltage: Some(6),
712 ..EventFeatureIndices::default()
713 },
714 });
715 let event = |feature_index: u8, function_id: u8, software_id: u8, bytes: [u8; 3]| {
716 let mut payload = [0; 16];
717 payload[..3].copy_from_slice(&bytes);
718 v20::Message::Long(
719 v20::MessageHeader {
720 device_index: 1,
721 feature_index,
722 function_id: U4::from_lo(function_id),
723 software_id: U4::from_lo(software_id),
724 },
725 payload,
726 )
727 .into()
728 };
729
730 for bytes in [[50, 20, 0x00], [0, 0, 0x09]] {
733 assert_eq!(
734 state.decode(event(4, 0, 0, bytes), false),
735 Some(HidppEventSource::BatteryStatus),
736 "{bytes:02x?}"
737 );
738 }
739 assert_eq!(
741 state.decode(event(6, 0, 0, [0x0f, 0x3c, 0x80]), false),
742 Some(HidppEventSource::BatteryVoltage)
743 );
744 for (feature_index, source) in [(4, "0x1000"), (6, "0x1001")] {
745 assert_eq!(
748 state.decode(event(feature_index, 0, 0, [50, 20, 0]), true),
749 None,
750 "{source}"
751 );
752 assert_eq!(
753 state.decode(event(feature_index, 0, 0xd, [50, 20, 0]), false),
754 None,
755 "{source}"
756 );
757 assert_eq!(
759 state.decode(event(feature_index, 1, 0, [50, 20, 0]), false),
760 None,
761 "{source}"
762 );
763 }
764 assert_eq!(state.decode(event(5, 0, 0, [50, 20, 0]), false), None);
766 }
767
768 #[test]
769 fn setting_feature_indexes_are_table_positions() {
770 let keyboard = EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x1d4b, 0x40a3]);
773 assert_eq!(keyboard.fn_inversion_multi_host, Some(4));
774 assert_eq!(keyboard.fn_inversion, None);
775 let single_host = EventFeatureIndices::from_feature_ids(&[0x0001, 0x40a2]);
776 assert_eq!(single_host.fn_inversion, Some(2));
777 let mouse = EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x2202, 0x1001]);
778 assert_eq!(mouse.extended_dpi, Some(3));
779 assert_eq!(mouse.battery_voltage, Some(4));
780 }
781
782 #[test]
783 fn setting_broadcasts_request_reconciliation_and_bump_the_revision() {
784 let state = state(Some(ReceiverProtocol::Bolt));
785 let handle = EventSubscriptionHandle {
786 state: Arc::clone(&state),
787 };
788 let features = EventFeatureIndices {
789 fn_inversion: Some(4),
790 fn_inversion_multi_host: Some(5),
791 extended_dpi: Some(6),
792 battery_status: Some(7),
793 ..EventFeatureIndices::default()
794 };
795 handle.register_device(1, features);
796 handle.register_device(2, features);
797 let long = |device_index: u8, feature_index: u8, function_id: u8, software_id: u8| {
798 v20::Message::Long(
799 v20::MessageHeader {
800 device_index,
801 feature_index,
802 function_id: U4::from_lo(function_id),
803 software_id: U4::from_lo(software_id),
804 },
805 [0; 16],
806 )
807 .into()
808 };
809 assert_eq!(handle.settings_revision(1), 0);
810
811 assert_eq!(
813 state.decode(long(1, 4, 1, 0), false),
814 Some(HidppEventSource::FnInversion)
815 );
816 assert_eq!(handle.settings_revision(1), 1);
817 assert_eq!(state.decode(long(1, 4, 0, 0), false), None);
818 assert_eq!(
820 state.decode(long(1, 5, 0, 0), false),
821 Some(HidppEventSource::FnInversion)
822 );
823 let short = v20::Message::Short(
824 v20::MessageHeader {
825 device_index: 1,
826 feature_index: 5,
827 function_id: U4::from_lo(0),
828 software_id: U4::from_lo(0),
829 },
830 [1, 1, 0],
831 )
832 .into();
833 assert_eq!(state.decode(short, false), None);
834 assert_eq!(handle.settings_revision(1), 2);
835 assert_eq!(
837 state.decode(long(1, 6, 0, 0), false),
838 Some(HidppEventSource::ExtendedDpi)
839 );
840 assert_eq!(state.decode(long(1, 6, 1, 0), false), None);
841 assert_eq!(handle.settings_revision(1), 3);
842
843 assert_eq!(state.decode(long(1, 5, 1, 0), true), None);
846 assert_eq!(state.decode(long(1, 5, 1, 0xd), false), None);
847 assert_eq!(
849 state.decode(long(1, 7, 0, 0), false),
850 Some(HidppEventSource::BatteryStatus)
851 );
852 assert_eq!(handle.settings_revision(1), 3);
853
854 handle.register_device(1, features);
857 assert_eq!(handle.settings_revision(1), 3);
858 assert_eq!(handle.settings_revision(2), 0);
860 assert_eq!(state.decode(long(3, 5, 0, 0), false), None);
861 assert_eq!(handle.settings_revision(3), 0);
862 }
863
864 #[test]
865 fn platform_change_reports_bump_the_revision() {
866 let keyboard = EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x40a3, 0x4531]);
867 assert_eq!(keyboard.multi_platform, Some(4));
868
869 let state = state(Some(ReceiverProtocol::Bolt));
870 let handle = EventSubscriptionHandle {
871 state: Arc::clone(&state),
872 };
873 handle.register_device(1, keyboard);
874 let report = |function_id: u8, software_id: u8| {
875 v20::Message::Short(
876 v20::MessageHeader {
877 device_index: 1,
878 feature_index: 4,
879 function_id: U4::from_lo(function_id),
880 software_id: U4::from_lo(software_id),
881 },
882 [1, 2, 2],
883 )
884 .into()
885 };
886
887 for (function_id, revision) in [(0, 1), (1, 2)] {
889 assert_eq!(
890 state.decode(report(function_id, 0), false),
891 Some(HidppEventSource::MultiPlatform)
892 );
893 assert_eq!(handle.settings_revision(1), revision);
894 }
895 assert_eq!(state.decode(report(2, 0), true), None);
897 assert_eq!(state.decode(report(2, 0xd), false), None);
898 assert_eq!(handle.settings_revision(1), 2);
899 }
900
901 #[test]
902 fn backlight_info_broadcasts_bump_the_revision() {
903 let keyboard =
905 EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x40a3, 0x1982, 0x4531]);
906 assert_eq!(keyboard.backlight, Some(4));
907
908 let state = state(Some(ReceiverProtocol::Bolt));
909 let handle = EventSubscriptionHandle {
910 state: Arc::clone(&state),
911 };
912 handle.register_device(1, keyboard);
913 let report = |function_id: u8, software_id: u8| {
914 v20::Message::Long(
915 v20::MessageHeader {
916 device_index: 1,
917 feature_index: 4,
918 function_id: U4::from_lo(function_id),
919 software_id: U4::from_lo(software_id),
920 },
921 [8, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
922 )
923 .into()
924 };
925
926 assert_eq!(
928 state.decode(report(0, 0), false),
929 Some(HidppEventSource::Backlight)
930 );
931 assert_eq!(handle.settings_revision(1), 1);
932 assert_eq!(state.decode(report(1, 0), false), None);
933 assert_eq!(state.decode(report(0, 0), true), None);
935 assert_eq!(state.decode(report(0, 0xd), false), None);
936 assert_eq!(handle.settings_revision(1), 1);
937 }
938
939 #[test]
940 fn report_rate_broadcasts_bump_the_revision() {
941 let mouse =
943 EventFeatureIndices::from_feature_ids(&[0x0001, 0x0003, 0x2202, 0x8060, 0x8061]);
944 assert_eq!(mouse.extended_report_rate, Some(5));
945
946 let state = state(Some(ReceiverProtocol::Bolt));
947 let handle = EventSubscriptionHandle {
948 state: Arc::clone(&state),
949 };
950 handle.register_device(1, mouse);
951 let report = |feature_index: u8, function_id: u8, software_id: u8| {
952 v20::Message::Short(
953 v20::MessageHeader {
954 device_index: 1,
955 feature_index,
956 function_id: U4::from_lo(function_id),
957 software_id: U4::from_lo(software_id),
958 },
959 [3, 0, 0],
960 )
961 .into()
962 };
963
964 assert_eq!(
966 state.decode(report(5, 0, 0), false),
967 Some(HidppEventSource::ExtendedReportRate)
968 );
969 assert_eq!(handle.settings_revision(1), 1);
970 assert_eq!(state.decode(report(5, 2, 0), false), None);
971 assert_eq!(state.decode(report(5, 0, 0), true), None);
973 assert_eq!(state.decode(report(5, 0, 0xd), false), None);
974 assert_eq!(state.decode(report(4, 0, 0), false), None);
976 assert_eq!(handle.settings_revision(1), 1);
977 }
978
979 #[test]
980 fn event_requests_are_bounded_and_coalesced() {
981 let (notifier, mut receiver) = event_channel();
982 notifier.notify(HidppEventSource::ReceiverConnection);
983 notifier.notify(HidppEventSource::UnifiedBattery);
984
985 assert_eq!(
986 receiver.try_recv(),
987 Ok(HidppEventSource::ReceiverConnection)
988 );
989 assert_eq!(receiver.try_recv(), Err(mpsc::error::TryRecvError::Empty));
990 }
991}