pub mod event;
pub mod types;
#[cfg(test)]
mod tests;
use std::sync::Arc;
pub use event::{DpiCalibrationCompleted, DpiParametersChanged, ExtendedDpiEvent};
pub use types::{
CalibrationType, DpiCalibrationCorrection, DpiCalibrationInfo, DpiDirection, DpiParameters,
DpiRange, LedHoldType, Lod, SensorCapabilities, SensorCapabilitiesInfo, SetDpiParameters,
ShowDpiStatus, StartDpiCalibration,
};
use self::types::{parse_dpi_list, parse_dpi_ranges, parse_lod_list, terminated_word_len};
use crate::{
channel::{HidppChannel, MessageListenerGuard},
event::EventEmitter,
feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
protocol::v20::Hidpp20Error,
};
const MAX_RANGE_PAGES: u8 = 16;
pub struct ExtendedDpiFeature {
endpoint: FeatureEndpoint,
emitter: Arc<EventEmitter<ExtendedDpiEvent>>,
_msg_listener: MessageListenerGuard,
}
impl CreatableFeature for ExtendedDpiFeature {
const ID: u16 = 0x2202;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
let emitter = Arc::new(EventEmitter::new());
let listener = chan.add_msg_listener_guarded({
let emitter = Arc::clone(&emitter);
move |raw, matched| {
let Some((func, payload)) =
event_payload(raw, matched, device_index, feature_index)
else {
return;
};
if let Some(event) = event::decode_event(func.to_lo(), &payload) {
emitter.emit(event);
}
}
});
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
emitter,
_msg_listener: listener,
}
}
}
impl Feature for ExtendedDpiFeature {}
impl EmittingFeature<ExtendedDpiEvent> for ExtendedDpiFeature {
fn listen(&self) -> async_channel::Receiver<ExtendedDpiEvent> {
self.emitter.create_receiver()
}
}
impl ExtendedDpiFeature {
pub async fn get_sensor_count(&self) -> Result<u8, Hidpp20Error> {
Ok(self.endpoint.call(0, [0; 3]).await?.extend_payload()[0])
}
pub async fn get_sensor_capabilities(
&self,
sensor_index: u8,
) -> Result<SensorCapabilitiesInfo, Hidpp20Error> {
let payload = self
.endpoint
.call(1, [sensor_index, 0, 0])
.await?
.extend_payload();
Ok(SensorCapabilitiesInfo {
sensor_index: payload[0],
dpi_level_count: payload[1],
capabilities: SensorCapabilities::from_bits_retain(payload[2]),
})
}
pub async fn get_sensor_dpi_ranges(
&self,
sensor_index: u8,
direction: DpiDirection,
) -> Result<Vec<DpiRange>, Hidpp20Error> {
let mut stream = Vec::new();
for page in 0..MAX_RANGE_PAGES {
let payload = self
.endpoint
.call(2, [sensor_index, direction.into(), page])
.await?
.extend_payload();
if payload[0] != sensor_index || payload[1] != u8::from(direction) || payload[2] != page
{
return Err(Hidpp20Error::UnsupportedResponse);
}
stream.extend_from_slice(&payload[3..16]);
if terminated_word_len(&stream).is_some() {
return parse_dpi_ranges(&stream);
}
}
Err(Hidpp20Error::UnsupportedResponse)
}
pub async fn get_sensor_dpi_list(
&self,
sensor_index: u8,
direction: DpiDirection,
) -> Result<Vec<u16>, Hidpp20Error> {
let payload = self
.endpoint
.call(3, [sensor_index, direction.into(), 0])
.await?
.extend_payload();
parse_dpi_list(&payload[2..])
}
pub async fn get_sensor_lod_list(
&self,
sensor_index: u8,
dpi_level_count: u8,
) -> Result<Vec<Lod>, Hidpp20Error> {
let payload = self
.endpoint
.call(4, [sensor_index, 0, 0])
.await?
.extend_payload();
parse_lod_list(&payload[1..], usize::from(dpi_level_count))
}
pub async fn get_sensor_dpi_parameters(
&self,
sensor_index: u8,
) -> Result<DpiParameters, Hidpp20Error> {
let payload = self
.endpoint
.call(5, [sensor_index, 0, 0])
.await?
.extend_payload();
Ok(DpiParameters {
sensor_index: payload[0],
dpi_x: u16::from_be_bytes([payload[1], payload[2]]),
default_dpi_x: u16::from_be_bytes([payload[3], payload[4]]),
dpi_y: u16::from_be_bytes([payload[5], payload[6]]),
default_dpi_y: u16::from_be_bytes([payload[7], payload[8]]),
lod: Lod::try_from(payload[9]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
})
}
pub async fn set_sensor_dpi_parameters(
&self,
sensor_index: u8,
params: SetDpiParameters,
) -> Result<(), Hidpp20Error> {
let [dpi_x_hi, dpi_x_lo] = params.dpi_x.to_be_bytes();
let [dpi_y_hi, dpi_y_lo] = params.dpi_y.to_be_bytes();
let mut args = [0; 16];
args[..6].copy_from_slice(&[
sensor_index,
dpi_x_hi,
dpi_x_lo,
dpi_y_hi,
dpi_y_lo,
params.lod.into(),
]);
self.endpoint.call_long(6, args).await?;
Ok(())
}
pub async fn show_sensor_dpi_status(
&self,
sensor_index: u8,
params: ShowDpiStatus,
) -> Result<(), Hidpp20Error> {
let mut args = [0; 16];
args[..4].copy_from_slice(&[
sensor_index,
params.dpi_level,
params.led_hold_type.into(),
params.button_num,
]);
self.endpoint.call_long(7, args).await?;
Ok(())
}
pub async fn get_dpi_calibration_info(
&self,
sensor_index: u8,
) -> Result<DpiCalibrationInfo, Hidpp20Error> {
let payload = self
.endpoint
.call(8, [sensor_index, 0, 0])
.await?
.extend_payload();
Ok(DpiCalibrationInfo {
sensor_index: payload[0],
mouse_width: payload[1],
mouse_length: u16::from_be_bytes([payload[2], payload[3]]),
calib_dpi_x: u16::from_be_bytes([payload[4], payload[5]]),
calib_dpi_y: u16::from_be_bytes([payload[6], payload[7]]),
})
}
pub async fn start_dpi_calibration(
&self,
sensor_index: u8,
params: StartDpiCalibration,
) -> Result<(), Hidpp20Error> {
let [count_hi, count_lo] = params.expected_count.to_be_bytes();
let mut args = [0; 16];
args[..8].copy_from_slice(&[
sensor_index,
params.direction.into(),
count_hi,
count_lo,
params.calib_type.into(),
params.start_timeout,
params.hw_process_timeout,
params.sw_process_timeout,
]);
self.endpoint.call_long(9, args).await?;
Ok(())
}
pub async fn set_dpi_calibration(
&self,
sensor_index: u8,
direction: DpiDirection,
correction: DpiCalibrationCorrection,
) -> Result<(), Hidpp20Error> {
let [cor_hi, cor_lo] = correction.to_wire()?.to_be_bytes();
let mut args = [0; 16];
args[..4].copy_from_slice(&[sensor_index, direction.into(), cor_hi, cor_lo]);
self.endpoint.call_long(10, args).await?;
Ok(())
}
}