use std::sync::Arc;
use hidpp::{
channel::HidppChannel,
device::Device,
feature::{
fn_inversion::{
FnInversionMultiHostFeature, FnInversionState, FnInversionWithDefaultStateFeature,
},
hosts_info::HostIndex,
},
};
use tracing::debug;
use crate::route::DeviceRoute;
use super::{HidppOperation, WriteError, classify_hidpp_error, open_feature, with_route};
fn is_missing_multi_host(err: &WriteError) -> bool {
matches!(
err,
WriteError::FeatureUnsupported { feature_hex } if *feature_hex == 0x40a3
)
}
enum FnInversion {
MultiHost(Arc<FnInversionMultiHostFeature>),
SingleHost(Arc<FnInversionWithDefaultStateFeature>),
}
impl FnInversion {
async fn open(device: &mut Device) -> Result<Self, WriteError> {
match open_feature::<FnInversionMultiHostFeature>(device).await {
Ok(feature) => Ok(Self::MultiHost(feature)),
Err(err) if is_missing_multi_host(&err) => {
let feature = open_feature::<FnInversionWithDefaultStateFeature>(device).await?;
Ok(Self::SingleHost(feature))
}
Err(err) => Err(err),
}
}
async fn set(&self, state: FnInversionState) -> Result<(), WriteError> {
match self {
Self::MultiHost(feature) => {
feature
.set_global_fn_inversion(HostIndex::Current, state)
.await
.map_err(|e| classify_hidpp_error(e, HidppOperation::WriteFnLock, 0x40a3))?;
}
Self::SingleHost(feature) => {
feature
.set_global_fn_inversion(state)
.await
.map_err(|e| classify_hidpp_error(e, HidppOperation::WriteFnLock, 0x40a2))?;
}
}
Ok(())
}
}
pub async fn set_fn_lock(route: &DeviceRoute, on: bool) -> Result<(), WriteError> {
let index = route.device_index();
with_route(route, move |channel| async move {
set_fn_lock_on_channel(&channel, index, on).await
})
.await
}
pub(super) async fn set_fn_lock_on_channel(
channel: &Arc<HidppChannel>,
index: u8,
on: bool,
) -> Result<(), WriteError> {
let mut device = Device::new(Arc::clone(channel), index)
.await
.map_err(|_| WriteError::DeviceUnreachable { index })?;
let fn_inversion = FnInversion::open(&mut device).await?;
fn_inversion.set(FnInversionState::from(on)).await?;
debug!(index, on, "fn-lock written");
Ok(())
}