From e0647bacd6e1e4654861d3d6139c2b2274d0a67f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 23 Jun 2024 13:38:46 +0200 Subject: [PATCH] encode origin unit in type --- crates/servicepoint/examples/announce.rs | 2 +- .../examples/brightness_tester.rs | 4 +- crates/servicepoint/examples/game_of_life.rs | 2 +- crates/servicepoint/examples/moving_line.rs | 2 +- crates/servicepoint/src/command.rs | 84 ++++++++----------- crates/servicepoint/src/connection.rs | 21 +++-- crates/servicepoint/src/lib.rs | 4 +- crates/servicepoint/src/origin.rs | 48 +++++++++++ crates/servicepoint_binding_c/src/command.rs | 15 ++-- .../ServicePoint/BindGen/ServicePoint.g.cs | 14 ++-- 10 files changed, 120 insertions(+), 76 deletions(-) create mode 100644 crates/servicepoint/src/origin.rs diff --git a/crates/servicepoint/examples/announce.rs b/crates/servicepoint/examples/announce.rs index 4f291ad..ceb7ca2 100644 --- a/crates/servicepoint/examples/announce.rs +++ b/crates/servicepoint/examples/announce.rs @@ -44,6 +44,6 @@ fn main() { } connection - .send(Command::Cp437Data(Origin(0, 0), chars)) + .send(Command::Cp437Data(Origin::new(0, 0), chars)) .expect("sending text failed"); } diff --git a/crates/servicepoint/examples/brightness_tester.rs b/crates/servicepoint/examples/brightness_tester.rs index 798e94f..b36b42c 100644 --- a/crates/servicepoint/examples/brightness_tester.rs +++ b/crates/servicepoint/examples/brightness_tester.rs @@ -21,7 +21,7 @@ fn main() { connection .send(BitmapLinearWin( - Origin(0, 0), + Origin::new(0, 0), pixels, CompressionCode::Uncompressed, )) @@ -33,6 +33,6 @@ fn main() { } connection - .send(Command::CharBrightness(Origin(0, 0), brightnesses)) + .send(Command::CharBrightness(Origin::new(0, 0), brightnesses)) .expect("send failed"); } diff --git a/crates/servicepoint/examples/game_of_life.rs b/crates/servicepoint/examples/game_of_life.rs index 47f1139..61ce108 100644 --- a/crates/servicepoint/examples/game_of_life.rs +++ b/crates/servicepoint/examples/game_of_life.rs @@ -25,7 +25,7 @@ fn main() { loop { connection .send(Command::BitmapLinearWin( - Origin(0, 0), + Origin::new(0, 0), field.clone(), CompressionCode::Lzma, )) diff --git a/crates/servicepoint/examples/moving_line.rs b/crates/servicepoint/examples/moving_line.rs index d2b4455..caeb92d 100644 --- a/crates/servicepoint/examples/moving_line.rs +++ b/crates/servicepoint/examples/moving_line.rs @@ -25,7 +25,7 @@ fn main() { } connection .send(Command::BitmapLinearWin( - Origin(0, 0), + Origin::new(0, 0), pixels.clone(), CompressionCode::Lzma, )) diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index ef6d348..2b28d1c 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -1,26 +1,12 @@ use bitvec::prelude::BitVec; -use crate::command_code::CommandCode; -use crate::compression::{into_compressed, into_decompressed}; use crate::{ - Brightness, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, - SpBitVec, TILE_SIZE, + command_code::CommandCode, + compression::{into_compressed, into_decompressed}, + Brightness, ByteGrid, CompressionCode, Grid, Header, Origin, Packet, + PixelGrid, Pixels, SpBitVec, Tiles, TILE_SIZE, }; -/// An origin marks the top left position of a window sent to the display. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Origin(pub usize, pub usize); - -impl std::ops::Add for Origin { - type Output = Origin; - - fn add(self, rhs: Origin) -> Self::Output { - let Origin(x1, y1) = self; - let Origin(x2, y2) = rhs; - Origin(x1 + x2, y1 + y2) - } -} - /// Type alias for documenting the meaning of the u16 in enum values pub type Offset = usize; @@ -32,24 +18,20 @@ pub enum Command { /// Show text on the screen. /// - /// The origin is in tiles. - /// ///
/// The library does not currently convert between UTF-8 and CP-437. /// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now. ///
- Cp437Data(Origin, ByteGrid), + Cp437Data(Origin, ByteGrid), /// Sets a window of pixels to the specified values - BitmapLinearWin(Origin, PixelGrid, CompressionCode), + BitmapLinearWin(Origin, PixelGrid, CompressionCode), /// Set the brightness of all tiles to the same value. Brightness(Brightness), /// Set the brightness of individual tiles in a rectangular area of the display. - /// - /// The origin is in tiles. - CharBrightness(Origin, ByteGrid), + CharBrightness(Origin, ByteGrid), /// Set pixel data starting at the pixel offset on screen. /// @@ -116,11 +98,11 @@ impl From for Packet { Command::BitmapLegacy => { Command::command_code_only(CommandCode::BitmapLegacy) } - Command::CharBrightness(Origin(x, y), grid) => Packet( + Command::CharBrightness(origin, grid) => Packet( Header( CommandCode::CharBrightness.into(), - x as u16, - y as u16, + origin.x as u16, + origin.y as u16, grid.width() as u16, grid.height() as u16, ), @@ -171,11 +153,11 @@ impl From for Packet { bits.into(), ) } - Command::Cp437Data(Origin(x, y), grid) => Packet( + Command::Cp437Data(origin, grid) => Packet( Header( CommandCode::Cp437Data.into(), - x as u16, - y as u16, + origin.x as u16, + origin.y as u16, grid.width() as u16, grid.height() as u16, ), @@ -187,15 +169,14 @@ impl From for Packet { #[allow(clippy::cast_possible_truncation)] fn bitmap_win_into_packet( - origin: Origin, + origin: Origin, pixels: PixelGrid, compression: CompressionCode, ) -> Packet { - let Origin(pixel_x, pixel_y) = origin; - debug_assert_eq!(pixel_x % 8, 0); + debug_assert_eq!(origin.x % 8, 0); debug_assert_eq!(pixels.width() % 8, 0); - let tile_x = (pixel_x / TILE_SIZE) as u16; + let tile_x = (origin.x / TILE_SIZE) as u16; let tile_w = (pixels.width() / TILE_SIZE) as u16; let pixel_h = pixels.height() as u16; let payload = into_compressed(compression, pixels.into()); @@ -214,7 +195,7 @@ fn bitmap_win_into_packet( }; Packet( - Header(command.into(), tile_x, pixel_y as u16, tile_w, pixel_h), + Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h), payload, ) } @@ -289,14 +270,14 @@ impl TryFrom for Command { CommandCode::Cp437Data => { let Packet(_, payload) = packet; Ok(Command::Cp437Data( - Origin(a as usize, b as usize), + Origin::new(a as usize, b as usize), ByteGrid::load(c as usize, d as usize, &payload), )) } CommandCode::CharBrightness => { let Packet(_, payload) = packet; Ok(Command::CharBrightness( - Origin(a as usize, b as usize), + Origin::new(a as usize, b as usize), ByteGrid::load(c as usize, d as usize, &payload), )) } @@ -362,7 +343,7 @@ impl Command { }; Ok(Command::BitmapLinearWin( - Origin(tiles_x as usize * TILE_SIZE, pixels_y as usize), + Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), PixelGrid::load( tile_w as usize * TILE_SIZE, pixel_h as usize, @@ -441,13 +422,10 @@ impl Command { #[cfg(test)] mod tests { - use bitvec::prelude::BitVec; - - use crate::command::TryFromPacketError; - use crate::command_code::CommandCode; use crate::{ - Brightness, ByteGrid, Command, CompressionCode, Header, Origin, Packet, - PixelGrid, + bitvec::prelude::BitVec, command::TryFromPacketError, + command_code::CommandCode, origin::Pixels, Brightness, ByteGrid, + Command, CompressionCode, Header, Origin, Packet, PixelGrid, }; fn round_trip(original: Command) { @@ -501,12 +479,15 @@ mod tests { #[test] fn round_trip_char_brightness() { - round_trip(Command::CharBrightness(Origin(5, 2), ByteGrid::new(7, 5))); + round_trip(Command::CharBrightness( + Origin::new(5, 2), + ByteGrid::new(7, 5), + )); } #[test] fn round_trip_cp437_data() { - round_trip(Command::Cp437Data(Origin(5, 2), ByteGrid::new(7, 5))); + round_trip(Command::Cp437Data(Origin::new(5, 2), ByteGrid::new(7, 5))); } #[test] @@ -533,7 +514,7 @@ mod tests { compression, )); round_trip(Command::BitmapLinearWin( - Origin(0, 0), + Origin::new(0, 0), PixelGrid::max_sized(), compression, )); @@ -619,7 +600,7 @@ mod tests { fn error_decompression_failed_win() { for compression in all_compressions().to_owned() { let p: Packet = Command::BitmapLinearWin( - Origin(16, 8), + Origin::new(16, 8), PixelGrid::new(8, 8), compression, ) @@ -743,6 +724,9 @@ mod tests { #[test] fn origin_add() { - assert_eq!(Origin(4, 2), Origin(1, 0) + Origin(3, 2)); + assert_eq!( + Origin::::new(4, 2), + Origin::new(1, 0) + Origin::new(3, 2) + ); } } diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 356f97a..2637b41 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -46,20 +46,27 @@ impl Connection { /// # Examples /// /// ```rust - /// use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; - /// let connection = servicepoint::Connection::open("172.23.42.29:2342") - /// .expect("connection failed"); + /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; + /// # let connection = servicepoint::Connection::open("172.23.42.29:2342") + /// # .expect("connection failed"); /// - /// // turn off all pixels + /// // turn off all pixels on display /// connection.send(Command::Clear) /// .expect("send failed"); /// - /// // turn on all pixels + /// // turn on all pixels in a grid /// let mut pixels = PixelGrid::max_sized(); /// pixels.fill(true); /// - /// // send pixels to display - /// connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed)) + /// // create command to send pixels + /// let command = Command::BitmapLinearWin( + /// servicepoint::Origin::new(0, 0), + /// pixels, + /// CompressionCode::Uncompressed + /// ); + /// + /// // send command to display + /// connection.send(command) /// .expect("send failed"); /// ``` pub fn send( diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index 6a0a5e5..e8edea6 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -7,11 +7,12 @@ use bitvec::prelude::{BitVec, Msb0}; pub use crate::brightness::Brightness; pub use crate::byte_grid::ByteGrid; -pub use crate::command::{Command, Offset, Origin}; +pub use crate::command::{Command, Offset}; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; pub use crate::data_ref::DataRef; pub use crate::grid::Grid; +pub use crate::origin::{Origin, Pixels, Tiles}; pub use crate::packet::{Header, Packet, Payload}; pub use crate::pixel_grid::PixelGrid; @@ -26,6 +27,7 @@ mod compression_code; mod connection; mod data_ref; mod grid; +mod origin; mod packet; mod pixel_grid; diff --git a/crates/servicepoint/src/origin.rs b/crates/servicepoint/src/origin.rs new file mode 100644 index 0000000..af78dfd --- /dev/null +++ b/crates/servicepoint/src/origin.rs @@ -0,0 +1,48 @@ +use std::marker::PhantomData; + +/// An origin marks the top left position of a window sent to the display. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Origin { + /// position in the width direction + pub x: usize, + /// position in the height direction + pub y: usize, + phantom_data: PhantomData, +} + +impl Origin { + /// Create a new `Origin` instance for the provided position. + pub fn new(x: usize, y: usize) -> Self { + Self { + x, + y, + phantom_data: PhantomData::default(), + } + } +} + +impl std::ops::Add> for Origin { + type Output = Origin; + + fn add(self, rhs: Origin) -> Self::Output { + Origin { + x: self.x + rhs.x, + y: self.y + rhs.y, + phantom_data: PhantomData::default(), + } + } +} + +pub trait DisplayUnit {} + +/// Marks something to be measured in number of pixels. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Pixels(); + +/// Marks something to be measured in number of iles. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Tiles(); + +impl DisplayUnit for Pixels {} + +impl DisplayUnit for Tiles {} diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index f71ed6f..8882162 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -105,9 +105,9 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { /// - the returned `Command` instance is freed in some way, either by using a consuming function or /// by explicitly calling `sp_command_dealloc`. #[no_mangle] -pub unsafe extern "C" fn sp_command_brightness( - brightness: Brightness, -) -> *mut Command { +pub unsafe extern "C" fn sp_command_brightness(brightness: u8) -> *mut Command { + let brightness = + Brightness::try_from(brightness).expect("invalid brightness"); Box::into_raw(Box::new(Command::Brightness(brightness))) } @@ -129,7 +129,10 @@ pub unsafe extern "C" fn sp_command_char_brightness( byte_grid: *mut ByteGrid, ) -> *mut Command { let byte_grid = *Box::from_raw(byte_grid); - Box::into_raw(Box::new(Command::CharBrightness(Origin(x, y), byte_grid))) + Box::into_raw(Box::new(Command::CharBrightness( + Origin::new(x, y), + byte_grid, + ))) } /// Allocates a new `Command::BitmapLinear` instance. @@ -254,7 +257,7 @@ pub unsafe extern "C" fn sp_command_cp437_data( byte_grid: *mut ByteGrid, ) -> *mut Command { let byte_grid = *Box::from_raw(byte_grid); - Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid))) + Box::into_raw(Box::new(Command::Cp437Data(Origin::new(x, y), byte_grid))) } /// Allocates a new `Command::BitmapLinearWin` instance. @@ -278,7 +281,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( ) -> *mut Command { let byte_grid = *Box::from_raw(pixel_grid); Box::into_raw(Box::new(Command::BitmapLinearWin( - Origin(x, y), + Origin::new(x, y), byte_grid, compression_code, ))) diff --git a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs index 5e958ad..5af738e 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs @@ -121,7 +121,7 @@ namespace ServicePoint.BindGen [DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern Command* sp_command_fade_out(); - /// Allocates a new `Command::Brightness` instance. # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. + /// Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the same value. # Panics - When the provided brightness value is out of range (0-11). # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern Command* sp_command_brightness(byte brightness); @@ -262,17 +262,17 @@ namespace ServicePoint.BindGen public enum Command { Clear, - HardReset, - FadeOut, - CharBrightness, + Cp437Data, + BitmapLinearWin, Brightness, - BitmapLegacy, + CharBrightness, BitmapLinear, BitmapLinearAnd, BitmapLinearOr, BitmapLinearXor, - Cp437Data, - BitmapLinearWin, + HardReset, + FadeOut, + BitmapLegacy, } public enum CompressionCode : ushort