use super::*;
pub trait DeviceSpec {
const BOUNDING_BOX_WIDTH: u32;
const BOUNDING_BOX_HEIGHT: u32;
const COLOR_PRECISION: u16;
type Input: crate::InputDevice;
type Output: crate::OutputDevice;
fn is_valid(x: u32, y: u32) -> bool;
fn flush(
canvas: &mut crate::DeviceCanvas<Self>,
changes: &[(u32, u32, (u8, u8, u8))])
-> Result<(), crate::MidiError>
where Self: Sized;
fn convert_message(msg: <Self::Input as crate::InputDevice>::Message) -> Option<CanvasMessage>;
fn setup(output: &mut Self::Output) -> Result<(), crate::MidiError> {
let _ = output;
Ok(())
}
}
pub struct DeviceCanvasPoller {
receiver: std::sync::mpsc::Receiver<CanvasMessage>,
}
impl crate::MsgPollingWrapper for DeviceCanvasPoller {
type Message = CanvasMessage;
fn receiver(&self) -> &std::sync::mpsc::Receiver<Self::Message> { &self.receiver }
}
pub struct DeviceCanvas<Spec: DeviceSpec> {
_input: crate::InputDeviceHandler,
pub(crate) output: Spec::Output,
curr_state: crate::util::Array2d<crate::Color>,
new_state: crate::util::Array2d<crate::Color>,
num_sent_changes: usize,
}
impl<Spec: DeviceSpec> DeviceCanvas<Spec> {
pub fn guess(
mut callback: impl FnMut(CanvasMessage) + Send + 'static
) -> Result<Self, crate::MidiError> {
use crate::midi_io::{InputDevice, OutputDevice};
let _input = Spec::Input::guess(move |msg| {
if let Some(msg) = Spec::convert_message(msg) {
(callback)(msg);
}
})?;
let mut output = Spec::Output::guess()?;
Spec::setup(&mut output)?;
let curr_state = crate::util::Array2d::new(
Spec::BOUNDING_BOX_WIDTH as usize,
Spec::BOUNDING_BOX_HEIGHT as usize,
);
let new_state = crate::util::Array2d::new(
Spec::BOUNDING_BOX_WIDTH as usize,
Spec::BOUNDING_BOX_HEIGHT as usize,
);
Ok(Self { _input, output, curr_state, new_state, num_sent_changes: 0 })
}
pub fn guess_polling() -> Result<(Self, DeviceCanvasPoller), crate::MidiError> {
let (sender, receiver) = std::sync::mpsc::channel();
let canvas = Self::guess(move |msg| {
sender.send(msg)
.expect("Message receiver has hung up (this shouldn't happen)")
})?;
let poller = DeviceCanvasPoller { receiver };
Ok((canvas, poller))
}
}
#[doc(hidden)] pub trait DeviceCanvasTrait {
type Spec: DeviceSpec;
}
impl<S: DeviceSpec> DeviceCanvasTrait for DeviceCanvas<S> {
type Spec = S;
}
impl_traits_for_canvas!(<S: DeviceSpec>, DeviceCanvas);
impl<Spec: DeviceSpec> crate::Canvas for DeviceCanvas<Spec> {
fn bounding_box_width(&self) -> u32 { Spec::BOUNDING_BOX_WIDTH }
fn bounding_box_height(&self) -> u32 { Spec::BOUNDING_BOX_HEIGHT }
fn is_valid(&self, x: u32, y: u32) -> bool { Spec::is_valid(x, y) }
fn lowest_visible_brightness(&self) -> f32 { 1.0 / Spec::COLOR_PRECISION as f32 }
fn get_old_unchecked_ref(&self, x: u32, y: u32) -> &Color {
self.curr_state.get_ref(x as usize, y as usize)
}
fn get_new_unchecked_mut(&mut self, x: u32, y: u32) -> &mut Color {
self.new_state.get_mut(x as usize, y as usize)
}
fn get_new_unchecked_ref(&self, x: u32, y: u32) -> &Color {
self.new_state.get_ref(x as usize, y as usize)
}
fn flush(&mut self) -> Result<(), crate::MidiError> {
let mut changes: Vec<(u32, u32, (u8, u8, u8))> = Vec::with_capacity(9 * 9);
for pad in self.iter() {
let old = self[pad].quantize(Spec::COLOR_PRECISION);
let new = self.at_new(pad).quantize(Spec::COLOR_PRECISION);
if new != old {
changes.push((pad.x as u32, pad.y as u32, new));
}
}
if !changes.is_empty() {
use crate::midi_io::OutputDevice;
self.num_sent_changes += changes.len();
if self.num_sent_changes / 1000 != (self.num_sent_changes - changes.len()) / 1000 {
println!("{}: we're at {} total transmitted changes now",
Spec::Output::MIDI_DEVICE_KEYWORD,
self.num_sent_changes,
);
}
Spec::flush(self, &changes)?;
}
self.curr_state = self.new_state.clone();
return Ok(());
}
}