Skip to content

Commit

Permalink
Uncheck reply if you need
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiseiYokoyama committed May 15, 2020
1 parent 5583171 commit d185f82
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 26 deletions.
7 changes: 4 additions & 3 deletions examples/player_lights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ fn main() -> JoyConResult<()> {
driver.set_player_lights(&lights_status.light_up, &lights_status.flash)?;

// Get player lights
let lights_status_received = driver.get_player_lights()?.extra.reply;

assert_eq!(lights_status_received, lights_status);
if let SubCommandReply::Checked(reply) = driver.get_player_lights()? {
let lights_status_received = reply.extra.reply;
assert_eq!(lights_status_received, lights_status);
}

Ok(())
})?;
Expand Down
18 changes: 14 additions & 4 deletions src/joycon/driver/input_report_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ pub trait InputReportMode<D: JoyConDriver>: Sized {
}

/// * timeout - milli seconds
fn read_input_report_timeout(&self, timeout: i32) ->JoyConResult<Self::Report> {
fn read_input_report_timeout(&self, timeout: i32) -> JoyConResult<Self::Report> {
let mut buf = [0u8; 362];
self.driver().read_timeout(&mut buf, timeout)?;

Expand Down Expand Up @@ -460,12 +460,20 @@ pub mod sub_command_mode {
const ARGS: Self::ArgsType;

/// The mode remains the same, sending commands and receiving replies.
fn once<D>(driver: &mut D) -> JoyConResult<StandardInputReport<SubCommandReport<Self>>>
fn once<D>(driver: &mut D) -> JoyConResult<SubCommandReply<StandardInputReport<SubCommandReport<Self>>>>
where Self: std::marker::Sized,
D: JoyConDriver
{
let reply = driver.send_sub_command(Self::SUB_COMMAND, Self::ARGS.as_ref())?;
StandardInputReport::try_from(reply)
match driver.send_sub_command(Self::SUB_COMMAND, Self::ARGS.as_ref()) {
Ok(reply) => {
let ok = match reply {
SubCommandReply::Checked(reply) => SubCommandReply::Checked(StandardInputReport::try_from(reply)?),
SubCommandReply::Unchecked => SubCommandReply::Unchecked
};
Ok(ok)
}
Err(e) => Err(e)
}
}
}

Expand Down Expand Up @@ -707,6 +715,8 @@ pub mod standard_full_mode {
driver.enable_feature(JoyConFeature::IMUFeature(IMUConfig::default()))?;
}

driver.set_valid_reply(false);

driver.send_sub_command(Self::SUB_COMMAND, Self::ARGS.as_ref())?;

Ok(StandardFullMode {
Expand Down
6 changes: 3 additions & 3 deletions src/joycon/driver/lights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ pub trait Lights: JoyConDriver {
/// Player lights will be...
/// > [SL Button] 🤔💡🤔🤔 [SR Button]
///
fn set_player_lights(&mut self, light_up: &Vec<LightUp>, flash: &Vec<Flash>) -> JoyConResult<[u8; 362]> {
fn set_player_lights(&mut self, light_up: &Vec<LightUp>, flash: &Vec<Flash>) -> JoyConResult<SubCommandReply<[u8; 362]>> {
let arg = light_up.iter()
.map(|&lu| lu as u8)
.sum::<u8>()
Expand Down Expand Up @@ -413,7 +413,7 @@ pub trait Lights: JoyConDriver {
/// dbg!(player_lights_status);
/// ```
///
fn get_player_lights(&mut self) -> JoyConResult<StandardInputReport<SubCommandReport<LightsStatus>>>
fn get_player_lights(&mut self) -> JoyConResult<SubCommandReply<StandardInputReport<SubCommandReport<LightsStatus>>>>
where Self: std::marker::Sized
{
LightsStatus::once(self)
Expand Down Expand Up @@ -442,7 +442,7 @@ pub trait Lights: JoyConDriver {
/// .add_phase(0,500,0);
/// let player_lights_status = joycon_driver.set_home_light(&pattern);
/// ```
fn set_home_light(&mut self, pattern: &home_button::LightEmittingPattern) -> JoyConResult<[u8; 362]> {
fn set_home_light(&mut self, pattern: &home_button::LightEmittingPattern) -> JoyConResult<SubCommandReply<[u8; 362]>> {
let arg: [u8; 25] = pattern.clone().into();
self.send_sub_command(SubCommand::SetHOMELight, &arg)
}
Expand Down
68 changes: 52 additions & 16 deletions src/joycon/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use global_packet_number::GlobalPacketNumber;
pub use joycon_features::{JoyConFeature, IMUConfig};
pub use simple_joycon_driver::SimpleJoyConDriver;
use std::sync::{Mutex, MutexGuard};
use std::hash::Hash;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Rotation {
Expand Down Expand Up @@ -106,10 +107,40 @@ mod global_packet_number {

mod simple_joycon_driver;

pub enum SubCommandReply<T> {
Checked(T),
Unchecked,
}

impl<T> Clone for SubCommandReply<T> where T: Clone {
fn clone(&self) -> Self {
match &self {
SubCommandReply::Checked(t) => SubCommandReply::Checked(t.clone()),
SubCommandReply::Unchecked => SubCommandReply::Unchecked,
}
}
}

impl<T> Copy for SubCommandReply<T> where T: Copy {}

impl<T> Debug for SubCommandReply<T> where T: Debug {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
SubCommandReply::Checked(t) => write!(f, "SubCommandReply::Checked({:?})", t),
SubCommandReply::Unchecked => write!(f, "SubCommandReply::Unchecked"),
}
}
}

pub trait JoyConDriver {
/// If a sub-command is sent and no ACK packet is returned, tread again for the number of times of this value.
const ACK_TRY: usize = 5;

/// If `true`, driver does not read and check reply of sub-command.
fn valid_reply(&self) -> bool;

fn set_valid_reply(&mut self, valid: bool);

/// Send command to Joy-Con
fn write(&self, data: &[u8]) -> JoyConResult<usize>;

Expand Down Expand Up @@ -178,26 +209,31 @@ pub trait JoyConDriver {
/// # Notice
/// If you are using non-blocking mode,
/// it is more likely to fail to validate the sub command reply.
fn send_sub_command_raw(&mut self, sub_command: u8, data: &[u8]) -> JoyConResult<[u8; 362]> {
fn send_sub_command_raw(&mut self, sub_command: u8, data: &[u8]) -> JoyConResult<SubCommandReply<[u8;362]>> {
use input_report_mode::sub_command_mode::AckByte;

self.send_command_raw(1, sub_command, data)?;

// check reply
std::iter::repeat(())
.take(Self::ACK_TRY)
.flat_map(|()| {
let mut buf = [0u8; 362];
self.read(&mut buf).ok()?;
let ack_byte = AckByte::from(buf[13]);

match ack_byte {
AckByte::Ack { .. } => Some(buf),
AckByte::Nack => None
}
})
.next()
.ok_or(JoyConError::SubCommandError(sub_command, Vec::new()))
if self.valid_reply() {
std::iter::repeat(())
.take(Self::ACK_TRY)
.flat_map(|()| {
let mut buf = [0u8; 362];
self.read(&mut buf).ok()?;
let ack_byte = AckByte::from(buf[13]);

match ack_byte {
AckByte::Ack { .. } => Some(buf),
AckByte::Nack => None
}
})
.next()
.map(SubCommandReply::Checked)
.ok_or(JoyConError::SubCommandError(sub_command, Vec::new()))
} else {
Ok(SubCommandReply::Unchecked)
}
}

/// Send command, sub-command, and data (sub-command's arguments) with `Command` and `SubCommand`
Expand All @@ -215,7 +251,7 @@ pub trait JoyConDriver {

/// Send sub-command, and data (sub-command's arguments) with `Command` and `SubCommand`
/// This returns ACK packet for the command or Error.
fn send_sub_command(&mut self, sub_command: SubCommand, data: &[u8]) -> JoyConResult<[u8; 362]> {
fn send_sub_command(&mut self, sub_command: SubCommand, data: &[u8]) -> JoyConResult<SubCommandReply<[u8;362]>> {
self.send_sub_command_raw(sub_command as u8, data)
}

Expand Down
19 changes: 19 additions & 0 deletions src/joycon/driver/simple_joycon_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub struct SimpleJoyConDriver {
pub rotation: Rotation,
rumble: (Option<Rumble>, Option<Rumble>),
enabled_features: HashSet<JoyConFeature>,
valid_reply: bool,
/// Increment by 1 for each packet sent. It loops in 0x0 - 0xF range.
global_packet_number: GlobalPacketNumber,
}
Expand All @@ -52,6 +53,16 @@ impl SimpleJoyConDriver {
rotation: Rotation::Portrait,
rumble: (None, None),
enabled_features: HashSet::new(),
valid_reply: {
let device = match joycon.lock() {
Ok(j) => j,
Err(e) => e.into_inner(),
}.device_type();
match device {
JoyConDeviceType::ProCon => false,
_ => true,
}
},
global_packet_number: GlobalPacketNumber::default(),
};

Expand Down Expand Up @@ -86,6 +97,14 @@ impl SimpleJoyConDriver {


impl JoyConDriver for SimpleJoyConDriver {
fn valid_reply(&self) -> bool {
self.valid_reply
}

fn set_valid_reply(&mut self, valid: bool) {
self.valid_reply = valid;
}

fn write(&self, data: &[u8]) -> JoyConResult<usize> {
let joycon = self.joycon();
Ok(joycon.write(data)?)
Expand Down
1 change: 1 addition & 0 deletions src/joycon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use driver::{
Rotation,
Rumble,
joycon_features,
SubCommandReply,
JoyConDriver,
GlobalPacketNumber,
SimpleJoyConDriver,
Expand Down

0 comments on commit d185f82

Please sign in to comment.