use crate::Device;
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum StickPosition {
Center = 0,
Up = 1,
UpRight = 2,
Right = 3,
DownRight = 4,
Down = 5,
DownLeft = 6,
Left = 7,
UpLeft = 8,
}
impl Default for StickPosition {
fn default() -> Self {
StickPosition::Center
}
}
impl StickPosition {
pub fn new() -> Self {
StickPosition::default()
}
pub fn from_u8(position: u8) -> Option<Self> {
match position {
0 => Some(StickPosition::Center),
1 => Some(StickPosition::Up),
2 => Some(StickPosition::UpRight),
3 => Some(StickPosition::Right),
4 => Some(StickPosition::DownRight),
5 => Some(StickPosition::Down),
6 => Some(StickPosition::DownLeft),
7 => Some(StickPosition::Left),
8 => Some(StickPosition::UpLeft),
_ => None,
}
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct State {
pub stick_position: StickPosition,
pub button_1: bool,
pub button_2: bool,
pub button_back: bool,
pub button_menu: bool,
pub button_fuji: bool,
pub roll: u16,
}
impl State {
pub fn from_report(data: &[u8]) -> Self {
assert!(data.len() >= 6);
assert_eq!(data[0], 1);
msg_to_state(&data[1..])
}
}
fn msg_to_state(msg: &[u8]) -> State {
assert_eq!(msg.len(), 5);
State {
stick_position: StickPosition::from_u8(msg[2] >> 4).unwrap_or_default(),
button_1: (msg[1] & 1) == 1,
button_2: ((msg[1] >> 1) & 1) == 1,
button_back: (msg[2] & 1) == 1,
button_menu: ((msg[2] >> 1) & 1) == 1,
button_fuji: ((msg[2] >> 2) & 1) == 1,
roll: u16::from(msg[3]) + (u16::from(msg[4]) << 8),
}
}
pub fn process_input<D>(mut device: D) -> Result<Option<State>, D::Error>
where
D: Device,
{
let mut buf = [0; 6];
buf.fill(0);
let mut has_msg = false;
let mut last_amount = 0;
device.set_blocking(false)?;
let msg = loop {
let amount = device.read(&mut buf)?;
if amount == 0 && !has_msg {
break &buf[0..0];
} else if amount != 0 {
has_msg = true;
last_amount = amount;
continue;
}
let msg = &buf[..last_amount];
break msg;
};
if !msg.is_empty() {
if msg.len() != 5 {
eprintln!(
"Special report #{:02X}: {:?}",
buf[0],
msg,
);
Ok(None)
} else {
Ok(Some(msg_to_state(msg)))
}
} else {
Ok(None)
}
}