servicepoint-binding-uniffi/src/command.rs

230 lines
8 KiB
Rust
Raw Normal View History

use crate::{BitVec, ByteGrid, Header, Packet, PixelGrid, TILE_SIZE};
use crate::command_codes::CommandCode;
2024-05-10 00:53:12 +02:00
/// An origin marks the top left position of the
/// data sent to the display.
2024-05-10 12:24:07 +02:00
#[derive(Debug, Clone, Copy)]
2024-05-10 00:53:12 +02:00
pub struct Origin(pub u16, pub u16);
impl Origin {
pub fn top_left() -> Self {
Self(0, 0)
}
}
2024-05-10 00:53:12 +02:00
/// Size defines the width and height of a window
2024-05-10 12:24:07 +02:00
#[derive(Debug, Clone, Copy)]
2024-05-10 00:53:12 +02:00
pub struct Size(pub u16, pub u16);
type Offset = u16;
type Brightness = u8;
#[derive(Debug)]
pub enum Command {
Clear,
HardReset,
FadeOut,
2024-05-11 12:43:17 +02:00
CharBrightness(Origin, ByteGrid),
2024-05-10 00:53:12 +02:00
Brightness(Brightness),
2024-05-10 21:27:34 +02:00
#[deprecated]
BitmapLegacy,
2024-05-10 00:53:12 +02:00
BitmapLinear(Offset, BitVec),
BitmapLinearAnd(Offset, BitVec),
BitmapLinearOr(Offset, BitVec),
BitmapLinearXor(Offset, BitVec),
2024-05-11 12:43:17 +02:00
Cp437Data(Origin, ByteGrid),
2024-05-10 12:24:07 +02:00
BitmapLinearWin(Origin, PixelGrid),
2024-05-10 00:53:12 +02:00
}
2024-05-10 19:55:18 +02:00
fn offset_and_payload(command: CommandCode, offset: Offset, payload: Vec<u8>) -> Packet {
2024-05-10 18:33:51 +02:00
Packet(Header(command.to_primitive(), offset, payload.len() as u16, 0, 0), payload)
2024-05-10 00:53:12 +02:00
}
fn origin_size_payload(command: CommandCode, origin: Origin, size: Size, payload: Vec<u8>) -> Packet {
let Origin(x, y) = origin;
let Size(w, h) = size;
2024-05-10 18:33:51 +02:00
Packet(Header(command.to_primitive(), x, y, w, h), payload.into())
}
2024-05-10 19:55:18 +02:00
fn command_code_only(code: CommandCode) -> Packet {
2024-05-10 18:33:51 +02:00
Packet(Header(code.to_primitive(), 0x0000, 0x0000, 0x0000, 0x0000), vec!())
2024-05-10 00:53:12 +02:00
}
2024-05-10 19:55:18 +02:00
impl Into<Packet> for Command {
fn into(self) -> Packet {
2024-05-10 00:53:12 +02:00
match self {
2024-05-10 19:55:18 +02:00
Command::Clear => command_code_only(CommandCode::Clear),
Command::FadeOut => command_code_only(CommandCode::FadeOut),
Command::HardReset => command_code_only(CommandCode::HardReset),
2024-05-10 21:27:34 +02:00
#[allow(deprecated)]
Command::BitmapLegacy => command_code_only(CommandCode::BitmapLegacy),
2024-05-10 18:33:51 +02:00
2024-05-11 12:43:17 +02:00
Command::CharBrightness(origin, grid) => {
origin_size_payload(CommandCode::CharBrightness,
origin,
Size(grid.width as u16, grid.height as u16),
grid.into())
2024-05-10 18:33:51 +02:00
}
Command::Brightness(brightness) => {
2024-05-10 19:55:18 +02:00
Packet(Header(CommandCode::Brightness.to_primitive(), 0x00000, 0x0000, 0x0000, 0x0000), vec!(brightness))
2024-05-10 18:33:51 +02:00
}
Command::BitmapLinear(offset, bits) => {
2024-05-10 19:55:18 +02:00
offset_and_payload(CommandCode::BitmapLinear, offset, bits.into())
2024-05-10 18:33:51 +02:00
}
2024-05-10 12:24:07 +02:00
Command::BitmapLinearWin(Origin(pixel_x, pixel_y), pixels) => {
debug_assert_eq!(pixel_x % 8, 0);
debug_assert_eq!(pixels.width % 8, 0);
2024-05-10 19:55:18 +02:00
Packet(
2024-05-11 12:43:17 +02:00
Header(CommandCode::BitmapLinearWin.to_primitive(),
pixel_x / TILE_SIZE,
pixel_y,
pixels.width as u16 / TILE_SIZE,
pixels.height as u16),
2024-05-10 19:55:18 +02:00
pixels.into())
2024-05-10 18:33:51 +02:00
}
Command::BitmapLinearAnd(offset, bits) => {
2024-05-10 19:55:18 +02:00
offset_and_payload(CommandCode::BitmapLinearAnd, offset, bits.into())
2024-05-10 18:33:51 +02:00
}
Command::BitmapLinearOr(offset, bits) => {
2024-05-10 19:55:18 +02:00
offset_and_payload(CommandCode::BitmapLinearOr, offset, bits.into())
2024-05-10 18:33:51 +02:00
}
Command::BitmapLinearXor(offset, bits) => {
2024-05-10 19:55:18 +02:00
offset_and_payload(CommandCode::BitmapLinearXor, offset, bits.into())
2024-05-10 18:33:51 +02:00
}
2024-05-11 12:43:17 +02:00
Command::Cp437Data(origin, grid) => {
origin_size_payload(CommandCode::Cp437Data,
origin,
Size(grid.width as u16, grid.height as u16),
grid.into())
2024-05-10 12:24:07 +02:00
}
2024-05-10 00:53:12 +02:00
}
}
}
2024-05-10 19:55:18 +02:00
#[derive(Debug)]
pub enum TryFromPacketError {
InvalidCommand(u16),
2024-05-10 21:45:33 +02:00
UnexpectedPayloadSize(usize, usize),
2024-05-10 19:55:18 +02:00
ExtraneousHeaderValues,
2024-05-10 21:45:33 +02:00
UnsupportedSubcommand(u16),
2024-05-10 19:55:18 +02:00
}
fn check_empty_header(packet: &Packet) -> Option<TryFromPacketError> {
let Packet(Header(_, a, b, c, d), _) = &packet;
if *a != 0 || *b != 0 || *c != 0 || *d != 0 {
Some(TryFromPacketError::ExtraneousHeaderValues)
} else {
None
}
}
fn check_command_only(packet: &Packet) -> Option<TryFromPacketError> {
let Packet(_, payload) = packet;
if payload.len() != 0 {
2024-05-10 21:45:33 +02:00
Some(TryFromPacketError::UnexpectedPayloadSize(0, payload.len()))
2024-05-10 19:55:18 +02:00
} else {
check_empty_header(packet)
}
}
2024-05-10 21:45:33 +02:00
fn check_linear_bitmap(packet: &Packet) -> Option<TryFromPacketError> {
2024-05-11 12:43:17 +02:00
let Packet(Header(_, _, length, sub, reserved), payload) = packet;
2024-05-10 21:45:33 +02:00
if *reserved != 0 {
return Some(TryFromPacketError::ExtraneousHeaderValues);
}
if *sub != 0 {
return Some(TryFromPacketError::UnsupportedSubcommand(*sub));
}
if payload.len() != *length as usize {
return Some(TryFromPacketError::UnexpectedPayloadSize(*length as usize, payload.len()));
}
None
}
2024-05-10 19:55:18 +02:00
impl TryFrom<Packet> for Command {
type Error = TryFromPacketError;
fn try_from(value: Packet) -> Result<Self, Self::Error> {
let Packet(Header(command_u16, a, b, c, d), payload) = &value;
let command_code = match CommandCode::from_primitive(*command_u16) {
None => return Err(TryFromPacketError::InvalidCommand(*command_u16)),
Some(value) => value
};
match command_code {
CommandCode::Clear => {
if let Some(err) = check_command_only(&value) {
return Err(err);
}
Ok(Command::Clear)
}
CommandCode::Brightness => {
if let Some(err) = check_empty_header(&value) {
return Err(err);
}
Ok(Command::Brightness(payload[0]))
}
CommandCode::HardReset => {
if let Some(err) = check_command_only(&value) {
return Err(err);
}
Ok(Command::HardReset)
}
CommandCode::FadeOut => {
if let Some(err) = check_command_only(&value) {
return Err(err);
}
Ok(Command::FadeOut)
}
CommandCode::Cp437Data => {
Ok(Command::Cp437Data(
2024-05-11 12:43:17 +02:00
Origin(*a, *b),
ByteGrid::load(*c as usize, *d as usize, payload),
2024-05-10 19:55:18 +02:00
))
}
CommandCode::CharBrightness => {
Ok(Command::CharBrightness(
2024-05-11 12:43:17 +02:00
Origin(*a, *b),
ByteGrid::load(*c as usize, *d as usize, payload),
2024-05-10 19:55:18 +02:00
))
}
2024-05-10 21:27:34 +02:00
#[allow(deprecated)]
CommandCode::BitmapLegacy => {
Ok(Command::BitmapLegacy)
}
2024-05-10 19:55:18 +02:00
CommandCode::BitmapLinearWin => {
Ok(Command::BitmapLinearWin(
Origin(*a * TILE_SIZE, *b),
PixelGrid::load(*c as usize * TILE_SIZE as usize, *d as usize, payload),
))
}
2024-05-10 21:45:33 +02:00
CommandCode::BitmapLinear => {
if let Some(err) = check_linear_bitmap(&value) {
return Err(err);
}
Ok(Command::BitmapLinear(*a, BitVec::load(payload)))
}
CommandCode::BitmapLinearAnd => {
if let Some(err) = check_linear_bitmap(&value) {
return Err(err);
}
Ok(Command::BitmapLinearAnd(*a, BitVec::load(payload)))
}
CommandCode::BitmapLinearOr => {
if let Some(err) = check_linear_bitmap(&value) {
return Err(err);
}
Ok(Command::BitmapLinearOr(*a, BitVec::load(payload)))
}
CommandCode::BitmapLinearXor => {
if let Some(err) = check_linear_bitmap(&value) {
return Err(err);
}
Ok(Command::BitmapLinearXor(*a, BitVec::load(payload)))
}
2024-05-10 19:55:18 +02:00
}
}
}