use super::{*, input_report_mode::sub_command_mode::*};
use crate::joycon::driver::input_report_mode::StandardInputReport;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum LightUp {
LED0 = 0x01,
LED1 = 0x02,
LED2 = 0x04,
LED3 = 0x08,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Flash {
LED0 = 0x10,
LED1 = 0x20,
LED2 = 0x40,
LED3 = 0x80,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct LightsStatus {
pub light_up: Vec<LightUp>,
pub flash: Vec<Flash>,
}
const LIGHT_UP: [LightUp; 4] =
[LightUp::LED0, LightUp::LED1, LightUp::LED2, LightUp::LED3];
const FLASH: [Flash; 4] =
[Flash::LED0, Flash::LED1, Flash::LED2, Flash::LED3];
impl TryFrom<[u8; 35]> for LightsStatus {
type Error = JoyConError;
fn try_from(value: [u8; 35]) -> Result<Self, Self::Error> {
let value = value[0];
let light_up = LIGHT_UP.iter()
.filter(|&&l| {
let light = l as u8;
value & light == light
})
.cloned()
.collect();
let flash = FLASH.iter()
.filter(|&&f| {
let flash = f as u8;
value & flash == flash
})
.cloned()
.collect();
Ok(LightsStatus { light_up, flash })
}
}
impl SubCommandReplyData for LightsStatus {
type ArgsType = [u8; 0];
const SUB_COMMAND: SubCommand = SubCommand::GetPlayerLights;
const ARGS: Self::ArgsType = [];
}
pub mod home_button {
use super::*;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct u4(u8);
impl u4 {
const MAX: Self = u4(15);
}
impl From<u8> for u4 {
fn from(v: u8) -> Self {
let value = u4(v);
if value > Self::MAX {
Self::MAX
} else { value }
}
}
impl Into<u8> for u4 {
fn into(self) -> u8 {
self.0
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct LightEmittingPhase {
pub led_intensity: u4,
pub fading_transition_duration: u4,
pub led_duration: u4,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct LightEmittingPattern {
phases_len: Option<u4>,
phases: Vec<LightEmittingPhase>,
global_mini_cycle_duration: u4,
led_start_intensity: u4,
repeat_count: u4,
}
impl LightEmittingPattern {
pub fn new(global_mini_cycle_duration: u8, led_start_intensity: u8, repeat_count: u4) -> Self {
let global_mini_cycle_duration = if global_mini_cycle_duration == 0 {
0.into()
} else {
((global_mini_cycle_duration - 7) / 12 + 1).into()
};
let led_start_intensity = {
let saturated = if 100 < led_start_intensity { 100 } else { led_start_intensity } as f32;
((saturated / 6.25) as u8).into()
};
LightEmittingPattern {
phases_len: None,
phases: Vec::with_capacity(15),
global_mini_cycle_duration,
led_start_intensity,
repeat_count,
}
}
pub fn push_phase(&mut self, phase: LightEmittingPhase) {
self.phases.push(phase);
}
pub fn phases(&self) -> &Vec<LightEmittingPhase> {
&self.phases
}
pub fn add_phase(mut self, led_intensity: u8, fading_transition_duration: u16, led_duration: u16) -> Self {
let led_intensity = {
let saturated = if 100 < led_intensity { 100 } else { led_intensity } as f32;
((saturated / 6.25) as u8).into()
};
let fading_transition_duration: u4 = {
let gmcd: u8 = self.global_mini_cycle_duration.into();
(fading_transition_duration / gmcd as u16) as u8
}.into();
let led_duration = {
let gmcd: u8 = self.global_mini_cycle_duration.into();
(led_duration / gmcd as u16) as u8
}.into();
let phase = LightEmittingPhase {
led_intensity,
fading_transition_duration,
led_duration,
};
self.push_phase(phase);
self
}
pub fn once(global_mini_cycle_duration: u8, led_start_intensity: u8,
led_intensity: u8, fading_transition_duration: u16, led_duration: u16) -> Self {
let mut pattern = LightEmittingPattern::new(global_mini_cycle_duration, led_start_intensity, 0u8.into());
pattern.phases_len = Some(0u8.into());
pattern.add_phase(led_intensity, fading_transition_duration, led_duration)
}
}
impl Into<[u8; 25]> for LightEmittingPattern {
fn into(self) -> [u8; 25] {
fn nibbles_to_u8(high: u4, low: u4) -> u8 {
let high = {
let high: u8 = high.into();
(high & 0x0F) << 4
};
let low = {
let low: u8 = low.into();
low & 0x0F
};
high | low
}
let mut buf = [0u8; 25];
let number_of_phases =
if let Some(p) = self.phases_len {
p
} else {
(self.phases.len() as u8).into()
};
buf[0] = nibbles_to_u8(number_of_phases, self.global_mini_cycle_duration);
buf[1] = nibbles_to_u8(self.led_start_intensity, self.repeat_count);
let mut even_phases = self.phases.iter()
.take(15)
.enumerate()
.filter(|(idx, _)| idx % 2 == 0)
.map(|e| e.1);
let mut odd_phases = self.phases.iter()
.take(15)
.enumerate()
.filter(|(idx, _)| idx % 2 == 1)
.map(|e| e.1);
let mut buf_index = 2;
while let (Some(even), odd) = (even_phases.next(), odd_phases.next()) {
{
let even_led_intensity = even.led_intensity;
let odd_led_intensity = odd.map(|odd| odd.led_intensity)
.unwrap_or_else(|| 0u8.into());
buf[buf_index] = nibbles_to_u8(even_led_intensity, odd_led_intensity);
buf_index += 1;
}
{
let fading = even.fading_transition_duration;
let led = even.led_duration;
buf[buf_index] = nibbles_to_u8(fading, led);
buf_index += 1;
}
if let Some(odd) = odd {
let fading = odd.fading_transition_duration;
let led = odd.led_duration;
buf[buf_index] = nibbles_to_u8(fading, led);
buf_index += 1;
}
}
buf
}
}
}
pub trait Lights: JoyConDriver {
const LIGHT_UP: [LightUp; 4] = LIGHT_UP;
const FLASH: [Flash; 4] = FLASH;
fn set_player_lights(&mut self, light_up: &[LightUp], flash: &[Flash]) -> JoyConResult<SubCommandReply<[u8; 362]>> {
let arg = light_up.iter()
.map(|&lu| lu as u8)
.sum::<u8>()
+ flash.iter()
.map(|&f| f as u8)
.sum::<u8>();
let reply = self.send_sub_command(SubCommand::SetPlayerLights, &[arg])?;
Ok(reply)
}
fn get_player_lights(&mut self) -> JoyConResult<SubCommandReply<StandardInputReport<SubCommandReport<LightsStatus>>>>
where Self: std::marker::Sized
{
LightsStatus::once(self)
}
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)
}
}
impl<D> Lights for D where D: JoyConDriver {}