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, pub available_buttons: EnumSet, 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 { 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];