deckster/loupedeck_serial/src/characteristics.rs
2024-01-02 20:18:47 +01:00

164 lines
4.7 KiB
Rust

use enum_ordinalize::Ordinalize;
use enumset::{enum_set, EnumSet, EnumSetType};
use crate::util::Endianness;
#[derive(Debug, Ordinalize, EnumSetType)]
#[repr(u8)]
pub enum LoupedeckKnob {
KnobLeftTop = 0x01,
KnobLeftMiddle = 0x02,
KnobLeftBottom = 0x03,
KnobRightTop = 0x04,
KnobRightMiddle = 0x05,
KnobRightBottom = 0x06,
}
#[derive(Debug, Ordinalize, EnumSetType)]
#[repr(u8)]
pub enum LoupedeckButton {
N0 = 0x07,
N1 = 0x08,
N2 = 0x09,
N3 = 0x0a,
N4 = 0x0b,
N5 = 0x0c,
N6 = 0x0d,
N7 = 0x0e,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct LoupedeckDeviceDisplayConfiguration {
pub id: u8,
pub name: &'static str,
pub width: u16,
pub height: u16,
pub local_offset_x: u16,
pub local_offset_y: u16,
pub global_offset_x: u16,
pub global_offset_y: u16,
pub endianness: Endianness,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct LoupedeckDeviceCharacteristics {
pub vendor_id: u16,
pub product_id: u16,
pub name: &'static str,
pub available_knobs: EnumSet<LoupedeckKnob>,
pub available_buttons: EnumSet<LoupedeckButton>,
pub key_grid_rows: u8,
pub key_grid_columns: u8,
pub key_grid_display: LoupedeckDeviceDisplayConfiguration,
pub additional_displays: &'static [LoupedeckDeviceDisplayConfiguration],
}
impl LoupedeckDeviceCharacteristics {
pub fn key_size(&self) -> (u16, u16) {
// Assuming the sizes are integers
(
self.key_grid_display.width / self.key_grid_columns as u16,
self.key_grid_display.height / self.key_grid_rows as u16,
)
}
pub fn get_display_at_coordinates(&self, x: u16, y: u16) -> Option<&LoupedeckDeviceDisplayConfiguration> {
let check = |display: &&LoupedeckDeviceDisplayConfiguration| {
x >= display.global_offset_x
&& x <= display.global_offset_x + display.width
&& y >= display.global_offset_y
&& y <= display.global_offset_y + display.height
};
if check(&&self.key_grid_display) {
Some(&self.key_grid_display)
} else {
self.additional_displays.iter().find(check)
}
}
pub fn get_key_at_coordinates(&self, x: u16, y: u16) -> Option<u8> {
let (column_width, row_height) = self.key_size();
if x < self.key_grid_display.global_offset_x || y < self.key_grid_display.global_offset_y {
return None;
}
let local_x = x - self.key_grid_display.global_offset_x;
let local_y = y - self.key_grid_display.global_offset_y;
if local_x >= self.key_grid_display.width || local_y >= self.key_grid_display.height {
return None;
}
let column = (local_x / column_width) as u8;
let row = (local_y / row_height) as u8;
Some(row * self.key_grid_columns + column)
}
}
static LOUPEDECK_LIVE_CHARACTERISTIC: LoupedeckDeviceCharacteristics = LoupedeckDeviceCharacteristics {
vendor_id: 0x2ec2,
product_id: 0x0004,
name: "Loupedeck Live",
available_knobs: enum_set!(
LoupedeckKnob::KnobLeftTop
| LoupedeckKnob::KnobLeftMiddle
| LoupedeckKnob::KnobLeftBottom
| LoupedeckKnob::KnobRightTop
| LoupedeckKnob::KnobRightMiddle
| LoupedeckKnob::KnobRightBottom
),
available_buttons: enum_set!(
LoupedeckButton::N0
| LoupedeckButton::N1
| LoupedeckButton::N2
| LoupedeckButton::N3
| LoupedeckButton::N4
| LoupedeckButton::N5
| LoupedeckButton::N6
| LoupedeckButton::N7
),
key_grid_rows: 3,
key_grid_columns: 4,
key_grid_display: LoupedeckDeviceDisplayConfiguration {
id: 0x4d,
name: "center",
width: 360,
height: 270,
local_offset_x: 60,
local_offset_y: 0,
global_offset_x: 60,
global_offset_y: 0,
endianness: Endianness::LittleEndian,
},
additional_displays: &[
LoupedeckDeviceDisplayConfiguration {
id: 0x4d,
name: "left",
width: 60,
height: 270,
local_offset_x: 0,
local_offset_y: 0,
global_offset_x: 0,
global_offset_y: 0,
endianness: Endianness::LittleEndian,
},
LoupedeckDeviceDisplayConfiguration {
id: 0x4d,
name: "right",
width: 60,
height: 270,
local_offset_x: 420,
local_offset_y: 0,
global_offset_x: 420,
global_offset_y: 0,
endianness: Endianness::LittleEndian,
},
],
};
pub static CHARACTERISTICS: [&LoupedeckDeviceCharacteristics; 1] = [&LOUPEDECK_LIVE_CHARACTERISTIC];