openlogi_device/write/
fn_lock.rs1use std::sync::Arc;
11
12use hidpp::{
13 channel::HidppChannel,
14 device::Device,
15 feature::{
16 fn_inversion::{
17 FnInversionMultiHostFeature, FnInversionState, FnInversionWithDefaultStateFeature,
18 },
19 hosts_info::HostIndex,
20 },
21};
22use tracing::debug;
23
24use crate::SharedChannel;
25use crate::backend::HidBackend;
26use crate::channel::route::DeviceRoute;
27
28use super::{HidppOperation, WriteError, classify_hidpp_error, open_feature, with_route};
29
30fn is_missing_multi_host(err: &WriteError) -> bool {
34 matches!(
35 err,
36 WriteError::FeatureUnsupported { feature_hex } if *feature_hex == 0x40a3
37 )
38}
39
40enum FnInversion {
44 MultiHost(Arc<FnInversionMultiHostFeature>),
46 SingleHost(Arc<FnInversionWithDefaultStateFeature>),
48}
49
50impl FnInversion {
51 async fn open(device: &mut Device) -> Result<Self, WriteError> {
55 match open_feature::<FnInversionMultiHostFeature>(device).await {
56 Ok(feature) => Ok(Self::MultiHost(feature)),
57 Err(err) if is_missing_multi_host(&err) => {
58 let feature = open_feature::<FnInversionWithDefaultStateFeature>(device).await?;
59 Ok(Self::SingleHost(feature))
60 }
61 Err(err) => Err(err),
62 }
63 }
64
65 async fn set(&self, state: FnInversionState) -> Result<(), WriteError> {
67 match self {
68 Self::MultiHost(feature) => {
69 feature
70 .set_global_fn_inversion(HostIndex::Current, state)
71 .await
72 .map_err(|e| classify_hidpp_error(e, HidppOperation::WriteFnLock, 0x40a3))?;
73 }
74 Self::SingleHost(feature) => {
75 feature
76 .set_global_fn_inversion(state)
77 .await
78 .map_err(|e| classify_hidpp_error(e, HidppOperation::WriteFnLock, 0x40a2))?;
79 }
80 }
81 Ok(())
82 }
83}
84
85pub async fn set_fn_lock(
87 backend: &dyn HidBackend,
88 route: &DeviceRoute,
89 on: bool,
90) -> Result<(), WriteError> {
91 let index = route.device_index();
92 with_route(backend, route, move |channel| async move {
93 set_fn_lock_on_channel(&channel, index, on).await
94 })
95 .await
96}
97
98pub(super) async fn set_fn_lock_on_channel(
100 channel: &Arc<HidppChannel>,
101 index: u8,
102 on: bool,
103) -> Result<(), WriteError> {
104 let mut device = Device::new(Arc::clone(channel), index)
105 .await
106 .map_err(|_| WriteError::DeviceUnreachable { index })?;
107 let fn_inversion = FnInversion::open(&mut device).await?;
108 fn_inversion.set(FnInversionState::from(on)).await?;
109 debug!(index, on, "fn-lock written");
110 Ok(())
111}
112
113pub async fn set_fn_lock_on(shared: &SharedChannel, on: bool) -> Result<(), WriteError> {
116 set_fn_lock_on_channel(shared.channel(), shared.device_index(), on).await
117}