diff --git a/crates/servicepoint/examples/announce.rs b/crates/servicepoint/examples/announce.rs index ab2657a..4159384 100644 --- a/crates/servicepoint/examples/announce.rs +++ b/crates/servicepoint/examples/announce.rs @@ -1,8 +1,7 @@ //! An example for how to send text to the display. use clap::Parser; - -use servicepoint::{CharGrid, Command, Connection, Origin, TILE_WIDTH}; +use servicepoint::*; #[derive(Parser, Debug)] struct Cli { diff --git a/crates/servicepoint/examples/brightness_tester.rs b/crates/servicepoint/examples/brightness_tester.rs index 8a31ee8..ec78415 100644 --- a/crates/servicepoint/examples/brightness_tester.rs +++ b/crates/servicepoint/examples/brightness_tester.rs @@ -1,7 +1,6 @@ //! Show a brightness level test pattern on screen use clap::Parser; - use servicepoint::*; #[derive(Parser, Debug)] diff --git a/crates/servicepoint/examples/game_of_life.rs b/crates/servicepoint/examples/game_of_life.rs index 76e8c70..ab4f63b 100644 --- a/crates/servicepoint/examples/game_of_life.rs +++ b/crates/servicepoint/examples/game_of_life.rs @@ -1,11 +1,9 @@ //! A simple game of life implementation to show how to render graphics to the display. -use std::thread; - use clap::Parser; use rand::{distributions, Rng}; - use servicepoint::*; +use std::thread; #[derive(Parser, Debug)] struct Cli { diff --git a/crates/servicepoint/examples/moving_line.rs b/crates/servicepoint/examples/moving_line.rs index 50cfcca..3ebd6b0 100644 --- a/crates/servicepoint/examples/moving_line.rs +++ b/crates/servicepoint/examples/moving_line.rs @@ -1,10 +1,8 @@ //! A simple example for how to send pixel data to the display. -use std::thread; - use clap::Parser; - use servicepoint::*; +use std::thread; #[derive(Parser, Debug)] struct Cli { diff --git a/crates/servicepoint/examples/random_brightness.rs b/crates/servicepoint/examples/random_brightness.rs index 00f4d56..0f976c4 100644 --- a/crates/servicepoint/examples/random_brightness.rs +++ b/crates/servicepoint/examples/random_brightness.rs @@ -1,13 +1,10 @@ //! A simple example for how to set brightnesses for tiles on the screen. //! Continuously changes the tiles in a random window to random brightnesses. -use std::time::Duration; - use clap::Parser; use rand::Rng; - -use servicepoint::Command::{BitmapLinearWin, Brightness, CharBrightness}; use servicepoint::*; +use std::time::Duration; #[derive(Parser, Debug)] struct Cli { @@ -31,14 +28,17 @@ fn main() { let mut filled_grid = Bitmap::max_sized(); filled_grid.fill(true); - let command = - BitmapLinearWin(Origin::ZERO, filled_grid, CompressionCode::Lzma); + let command = Command::BitmapLinearWin( + Origin::ZERO, + filled_grid, + CompressionCode::Lzma, + ); connection.send(command).expect("send failed"); } // set all pixels to the same random brightness let mut rng = rand::thread_rng(); - connection.send(Brightness(rng.gen())).unwrap(); + connection.send(Command::Brightness(rng.gen())).unwrap(); // continuously update random windows to new random brightness loop { @@ -58,7 +58,9 @@ fn main() { } } - connection.send(CharBrightness(origin, luma)).unwrap(); + connection + .send(Command::CharBrightness(origin, luma)) + .unwrap(); std::thread::sleep(wait_duration); } } diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs index 21733bf..101a9ae 100644 --- a/crates/servicepoint/examples/wiping_clear.rs +++ b/crates/servicepoint/examples/wiping_clear.rs @@ -1,11 +1,9 @@ //! An example on how to modify the image on screen without knowing the current content. +use clap::Parser; +use servicepoint::*; use std::thread; use std::time::Duration; -use clap::Parser; - -use servicepoint::*; - #[derive(Parser, Debug)] struct Cli { #[arg(short, long, default_value = "localhost:2342")] diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs index a0c03b4..76170b3 100644 --- a/crates/servicepoint/src/bitmap.rs +++ b/crates/servicepoint/src/bitmap.rs @@ -1,8 +1,11 @@ -use bitvec::order::Msb0; -use bitvec::prelude::BitSlice; -use bitvec::slice::IterMut; +//! Implementation of [Bitmap] -use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH}; +use crate::data_ref::DataRef; +use crate::BitVec; +use crate::*; +use ::bitvec::order::Msb0; +use ::bitvec::prelude::BitSlice; +use ::bitvec::slice::IterMut; /// A grid of pixels stored in packed bytes. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/servicepoint/src/bitvec.rs b/crates/servicepoint/src/bitvec.rs new file mode 100644 index 0000000..7006105 --- /dev/null +++ b/crates/servicepoint/src/bitvec.rs @@ -0,0 +1,3 @@ +pub use bitvec::prelude::*; +/// An alias for the specific type of [bitvec::prelude::BitVec] used. +pub type BitVec = bitvec::prelude::BitVec; diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs index 73cbaf6..4e12326 100644 --- a/crates/servicepoint/src/brightness.rs +++ b/crates/servicepoint/src/brightness.rs @@ -1,5 +1,3 @@ -use crate::primitive_grid::PrimitiveGrid; -use crate::{ByteGrid, Grid}; #[cfg(feature = "rand")] use rand::{ distributions::{Distribution, Standard}, @@ -22,29 +20,6 @@ use rand::{ #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Brightness(u8); -/// A grid containing brightness values. -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin}; -/// let mut grid = BrightnessGrid::new(2,2); -/// grid.set(0, 0, Brightness::MIN); -/// grid.set(1, 1, Brightness::MIN); -/// -/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); -/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap() -/// ``` -pub type BrightnessGrid = PrimitiveGrid; - -impl BrightnessGrid { - /// Like [Self::load], but ignoring any out-of-range brightness values - pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { - PrimitiveGrid::load(width, height, data) - .map(Brightness::saturating_from) - } -} - impl From for u8 { fn from(brightness: Brightness) -> Self { Self::from(&brightness) @@ -93,41 +68,6 @@ impl Default for Brightness { } } -impl From for Vec { - fn from(value: PrimitiveGrid) -> Self { - value - .iter() - .map(|brightness| (*brightness).into()) - .collect() - } -} - -impl From<&BrightnessGrid> for ByteGrid { - fn from(value: &PrimitiveGrid) -> Self { - let u8s = value - .iter() - .map(|brightness| (*brightness).into()) - .collect::>(); - PrimitiveGrid::load(value.width(), value.height(), &u8s) - } -} - -impl TryFrom for BrightnessGrid { - type Error = u8; - - fn try_from(value: ByteGrid) -> Result { - let brightnesses = value - .iter() - .map(|b| Brightness::try_from(*b)) - .collect::, _>>()?; - Ok(BrightnessGrid::load( - value.width(), - value.height(), - &brightnesses, - )) - } -} - #[cfg(feature = "rand")] impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Brightness { @@ -138,7 +78,6 @@ impl Distribution for Standard { #[cfg(test)] mod tests { use super::*; - use crate::DataRef; #[test] fn brightness_from_u8() { @@ -155,35 +94,9 @@ mod tests { } } - #[test] - fn to_u8_grid() { - let mut grid = BrightnessGrid::new(2, 2); - grid.set(1, 0, Brightness::MIN); - grid.set(0, 1, Brightness::MAX); - let actual = PrimitiveGrid::from(&grid); - assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); - } - #[test] fn saturating_convert() { assert_eq!(Brightness::MAX, Brightness::saturating_from(100)); assert_eq!(Brightness(5), Brightness::saturating_from(5)); } - - #[test] - fn saturating_load() { - assert_eq!( - BrightnessGrid::load( - 2, - 2, - &[ - Brightness::MAX, - Brightness::MAX, - Brightness::MIN, - Brightness::MAX - ] - ), - BrightnessGrid::saturating_load(2, 2, &[255u8, 23, 0, 42]) - ); - } } diff --git a/crates/servicepoint/src/brightness_grid.rs b/crates/servicepoint/src/brightness_grid.rs new file mode 100644 index 0000000..122df35 --- /dev/null +++ b/crates/servicepoint/src/brightness_grid.rs @@ -0,0 +1,93 @@ +use crate::brightness::Brightness; +use crate::grid::Grid; +use crate::value_grid::ValueGrid; +use crate::ByteGrid; + +/// A grid containing brightness values. +/// +/// # Examples +/// +/// ```rust +/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin}; +/// let mut grid = BrightnessGrid::new(2,2); +/// grid.set(0, 0, Brightness::MIN); +/// grid.set(1, 1, Brightness::MIN); +/// +/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); +/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap() +/// ``` +pub type BrightnessGrid = ValueGrid; + +impl BrightnessGrid { + /// Like [Self::load], but ignoring any out-of-range brightness values + pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { + ValueGrid::load(width, height, data).map(Brightness::saturating_from) + } +} + +impl From for Vec { + fn from(value: ValueGrid) -> Self { + value + .iter() + .map(|brightness| (*brightness).into()) + .collect() + } +} + +impl From<&BrightnessGrid> for ByteGrid { + fn from(value: &ValueGrid) -> Self { + let u8s = value + .iter() + .map(|brightness| (*brightness).into()) + .collect::>(); + ValueGrid::load(value.width(), value.height(), &u8s) + } +} + +impl TryFrom for BrightnessGrid { + type Error = u8; + + fn try_from(value: ByteGrid) -> Result { + let brightnesses = value + .iter() + .map(|b| Brightness::try_from(*b)) + .collect::, _>>()?; + Ok(BrightnessGrid::load( + value.width(), + value.height(), + &brightnesses, + )) + } +} + +#[cfg(test)] +mod tests { + use crate::value_grid::ValueGrid; + use crate::{Brightness, BrightnessGrid, DataRef, Grid}; + + #[test] + fn to_u8_grid() { + let mut grid = BrightnessGrid::new(2, 2); + grid.set(1, 0, Brightness::MIN); + grid.set(0, 1, Brightness::MAX); + let actual = ValueGrid::from(&grid); + assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); + } + + #[test] + fn saturating_load() { + assert_eq!( + BrightnessGrid::load( + 2, + 2, + &[ + Brightness::MAX, + Brightness::MAX, + Brightness::MIN, + Brightness::MAX + ] + ), + BrightnessGrid::saturating_load(2, 2, &[255u8, 23, 0, 42]) + ); + } +} diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs new file mode 100644 index 0000000..6ebf882 --- /dev/null +++ b/crates/servicepoint/src/byte_grid.rs @@ -0,0 +1,2 @@ +/// A simple grid of bytes - see [primitive_grid::PrimitiveGrid]. +pub type ByteGrid = crate::value_grid::ValueGrid; diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs index 97eb170..7d55cf3 100644 --- a/crates/servicepoint/src/char_grid.rs +++ b/crates/servicepoint/src/char_grid.rs @@ -1,11 +1,9 @@ -use crate::primitive_grid::{ - PrimitiveGrid, SeriesError, TryLoadPrimitiveGridError, -}; +use crate::value_grid::{SetValueSeriesError, TryLoadPrimitiveGridError, ValueGrid}; use crate::Grid; use std::string::FromUtf8Error; /// A grid containing UTF-8 characters. -pub type CharGrid = PrimitiveGrid; +pub type CharGrid = ValueGrid; impl CharGrid { /// Copies a column from the grid as a String. @@ -24,23 +22,23 @@ impl CharGrid { /// Overwrites a row in the grid with a str. /// - /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size. + /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size. pub fn set_row_str( &mut self, y: usize, value: &str, - ) -> Result<(), SeriesError> { + ) -> Result<(), SetValueSeriesError> { self.set_row(y, value.chars().collect::>().as_ref()) } /// Overwrites a column in the grid with a str. /// - /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size. + /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size. pub fn set_col_str( &mut self, x: usize, value: &str, - ) -> Result<(), SeriesError> { + ) -> Result<(), SetValueSeriesError> { self.set_col(x, value.chars().collect::>().as_ref()) } @@ -129,7 +127,6 @@ impl From for Vec { #[cfg(test)] mod test { use super::*; - use crate::Grid; #[test] fn col_str() { let mut grid = CharGrid::new(2, 3); @@ -146,7 +143,7 @@ mod test { assert_eq!(grid.get_row_str(1), Some(String::from("\0\0"))); assert_eq!( grid.set_row_str(1, "abc"), - Err(SeriesError::InvalidLength { + Err(SetValueSeriesError::InvalidLength { expected: 2, actual: 3 }) diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index 25603ff..0807c80 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -1,11 +1,7 @@ -use crate::primitive_grid::PrimitiveGrid; -use crate::{ - command_code::CommandCode, - compression::into_decompressed, - packet::{Header, Packet}, - BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, CompressionCode, - Cp437Grid, Origin, Pixels, Tiles, TILE_SIZE, -}; +use crate::command_code::CommandCode; +use crate::compression::into_decompressed; +use crate::value_grid::ValueGrid; +use crate::*; /// Type alias for documenting the meaning of the u16 in enum values pub type Offset = usize; @@ -42,7 +38,7 @@ pub type Offset = usize; /// # Examples /// /// ```rust -/// # use servicepoint::{Brightness, Command, Connection, packet::Packet}; +/// use servicepoint::{Brightness, Command, Connection, Packet}; /// # /// // create command /// let command = Command::Brightness(Brightness::MAX); @@ -80,9 +76,8 @@ pub enum Command { /// # Examples /// /// ```rust - /// # use servicepoint::{Command, Connection, Origin}; + /// # use servicepoint::{Command, Connection, Origin, CharGrid}; /// # let connection = Connection::Fake; - /// use servicepoint::{CharGrid}; /// let grid = CharGrid::from("Hello,\nWorld!"); /// connection.send(Command::Utf8Data(Origin::ZERO, grid)).expect("send failed"); /// ``` @@ -97,9 +92,8 @@ pub enum Command { /// # Examples /// /// ```rust - /// # use servicepoint::{Command, Connection, Origin}; + /// # use servicepoint::{Command, Connection, Origin, CharGrid, Cp437Grid}; /// # let connection = Connection::Fake; - /// use servicepoint::{CharGrid, Cp437Grid}; /// let grid = CharGrid::from("Hello,\nWorld!"); /// let grid = Cp437Grid::from(&grid); /// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed"); @@ -447,8 +441,7 @@ impl Command { payload, } = packet; - let grid = - PrimitiveGrid::load(*width as usize, *height as usize, payload); + let grid = ValueGrid::load(*width as usize, *height as usize, payload); let grid = match BrightnessGrid::try_from(grid) { Ok(grid) => grid, Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)), @@ -536,14 +529,11 @@ impl Command { #[cfg(test)] mod tests { + use crate::command::TryFromPacketError; + use crate::command_code::CommandCode; use crate::{ - bitvec::prelude::BitVec, - command::TryFromPacketError, - command_code::CommandCode, - origin::Pixels, - packet::{Header, Packet}, - Bitmap, Brightness, BrightnessGrid, CharGrid, Command, CompressionCode, - Cp437Grid, Origin, + BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, Command, + CompressionCode, Cp437Grid, Header, Origin, Packet, Pixels, }; fn round_trip(original: Command) { diff --git a/crates/servicepoint/src/compression.rs b/crates/servicepoint/src/compression.rs index 12c79d5..2e78073 100644 --- a/crates/servicepoint/src/compression.rs +++ b/crates/servicepoint/src/compression.rs @@ -8,7 +8,7 @@ use flate2::{FlushCompress, FlushDecompress, Status}; #[cfg(feature = "compression_zstd")] use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder}; -use crate::{packet::Payload, CompressionCode}; +use crate::{CompressionCode, Payload}; pub(crate) fn into_decompressed( kind: CompressionCode, diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index a0a6f11..417fd1d 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -165,7 +165,6 @@ impl Drop for Connection { #[cfg(test)] mod tests { use super::*; - use crate::packet::*; #[test] fn send_fake() { diff --git a/crates/servicepoint/src/constants.rs b/crates/servicepoint/src/constants.rs new file mode 100644 index 0000000..95c8684 --- /dev/null +++ b/crates/servicepoint/src/constants.rs @@ -0,0 +1,75 @@ +use std::time::Duration; + +/// size of a single tile in one dimension +pub const TILE_SIZE: usize = 8; + +/// Display tile count in the x-direction +/// +/// # Examples +/// +/// ```rust +/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; +/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); +/// ``` +pub const TILE_WIDTH: usize = 56; + +/// Display tile count in the y-direction +/// +/// # Examples +/// +/// ```rust +/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; +/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); +/// ``` +pub const TILE_HEIGHT: usize = 20; + +/// Display width in pixels +/// +/// # Examples +/// +/// ```rust +/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; +/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); +/// ``` +pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; + +/// Display height in pixels +/// +/// # Examples +/// +/// ```rust +/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; +/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); +/// ``` +pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; + +/// pixel count on whole screen +pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; + +/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. +/// +/// # Examples +/// +/// ```rust +/// # use std::time::Instant; +/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap}; +/// # let connection = servicepoint::Connection::Fake; +/// # let pixels = Bitmap::max_sized(); +/// loop { +/// let start = Instant::now(); +/// +/// // Change pixels here +/// +/// connection.send(Command::BitmapLinearWin( +/// Origin::new(0,0), +/// pixels, +/// CompressionCode::Lzma +/// )) +/// .expect("send failed"); +/// +/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby +/// std::thread::sleep(FRAME_PACING - start.elapsed()); +/// # break; // prevent doctest from hanging +/// } +/// ``` +pub const FRAME_PACING: Duration = Duration::from_millis(30); diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs index 17b5d45..1129c85 100644 --- a/crates/servicepoint/src/cp437.rs +++ b/crates/servicepoint/src/cp437.rs @@ -2,94 +2,14 @@ //! //! Most of the functionality is only available with feature "cp437" enabled. -use crate::{Grid, primitive_grid::PrimitiveGrid}; use std::collections::HashMap; -/// A grid containing codepage 437 characters. -/// -/// The encoding is currently not enforced. -pub type Cp437Grid = PrimitiveGrid; - -/// The error occurring when loading an invalid character -#[derive(Debug, PartialEq, thiserror::Error)] -#[error( - "The character {char:?} at position {index} is not a valid CP437 character" -)] -pub struct InvalidCharError { - /// invalid character is at this position in input - index: usize, - /// the invalid character - char: char, -} - -impl Cp437Grid { - /// Load an ASCII-only [&str] into a [Cp437Grid] of specified width. - /// - /// # Panics - /// - /// - for width == 0 - /// - on empty strings - pub fn load_ascii( - value: &str, - width: usize, - wrap: bool, - ) -> Result { - assert!(width > 0); - assert!(!value.is_empty()); - - let mut chars = { - let mut x = 0; - let mut y = 0; - - for (index, char) in value.chars().enumerate() { - if !char.is_ascii() { - return Err(InvalidCharError { index, char }); - } - - let is_lf = char == '\n'; - if is_lf || (wrap && x == width) { - y += 1; - x = 0; - if is_lf { - continue; - } - } - - x += 1; - } - - Cp437Grid::new(width, y + 1) - }; - - let mut x = 0; - let mut y = 0; - for char in value.chars().map(move |c| c as u8) { - let is_lf = char == b'\n'; - if is_lf || (wrap && x == width) { - y += 1; - x = 0; - if is_lf { - continue; - } - } - - if wrap || x < width { - chars.set(x, y, char); - } - x += 1; - } - - Ok(chars) - } -} - #[allow(unused)] // depends on features pub use feature_cp437::*; #[cfg(feature = "cp437")] mod feature_cp437 { use super::*; - use crate::CharGrid; /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters /// @@ -129,24 +49,6 @@ mod feature_cp437 { const MISSING_CHAR_CP437: u8 = 0x3F; // '?' - impl From<&Cp437Grid> for CharGrid { - fn from(value: &Cp437Grid) -> Self { - value.map(cp437_to_char) - } - } - - impl From<&CharGrid> for Cp437Grid { - fn from(value: &CharGrid) -> Self { - value.map(char_to_cp437) - } - } - - impl From for Cp437Grid { - fn from(value: CharGrid) -> Self { - Cp437Grid::from(&value) - } - } - /// Convert the provided bytes to UTF-8. pub fn cp437_to_str(cp437: &[u8]) -> String { cp437.iter().map(move |char| cp437_to_char(*char)).collect() @@ -170,57 +72,10 @@ mod feature_cp437 { } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn load_ascii_nowrap() { - let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] - .map(move |c| c as u8); - let expected = Cp437Grid::load(5, 2, &chars); - - let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap(); - // comma will be removed because line is too long and wrap is off - assert_eq!(actual, expected); - } - - #[test] - fn load_ascii_wrap() { - let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] - .map(move |c| c as u8); - let expected = Cp437Grid::load(5, 2, &chars); - - let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap(); - // line break will be added - assert_eq!(actual, expected); - } - - #[test] - fn load_ascii_invalid() { - assert_eq!( - Err(InvalidCharError { - char: '🥶', - index: 2 - }), - Cp437Grid::load_ascii("?#🥶42", 3, false) - ); - } -} - #[cfg(test)] #[cfg(feature = "cp437")] mod tests_feature_cp437 { use super::*; - use crate::CharGrid; - - #[test] - fn round_trip_cp437() { - let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); - let cp437 = Cp437Grid::from(&utf8); - let actual = CharGrid::from(&cp437); - assert_eq!(actual, utf8); - } #[test] fn convert_str() { diff --git a/crates/servicepoint/src/cp437_grid.rs b/crates/servicepoint/src/cp437_grid.rs new file mode 100644 index 0000000..a19e014 --- /dev/null +++ b/crates/servicepoint/src/cp437_grid.rs @@ -0,0 +1,160 @@ +/// A grid containing codepage 437 characters. +/// +/// The encoding is currently not enforced. +pub type Cp437Grid = crate::value_grid::ValueGrid; + +/// The error occurring when loading an invalid character +#[derive(Debug, PartialEq, thiserror::Error)] +#[error( + "The character {char:?} at position {index} is not a valid CP437 character" +)] +pub struct InvalidCharError { + /// invalid character is at this position in input + index: usize, + /// the invalid character + char: char, +} + +impl Cp437Grid { + /// Load an ASCII-only [&str] into a [Cp437Grid] of specified width. + /// + /// # Panics + /// + /// - for width == 0 + /// - on empty strings + pub fn load_ascii( + value: &str, + width: usize, + wrap: bool, + ) -> Result { + assert!(width > 0); + assert!(!value.is_empty()); + + let mut chars = { + let mut x = 0; + let mut y = 0; + + for (index, char) in value.chars().enumerate() { + if !char.is_ascii() { + return Err(InvalidCharError { index, char }); + } + + let is_lf = char == '\n'; + if is_lf || (wrap && x == width) { + y += 1; + x = 0; + if is_lf { + continue; + } + } + + x += 1; + } + + Cp437Grid::new(width, y + 1) + }; + + let mut x = 0; + let mut y = 0; + for char in value.chars().map(move |c| c as u8) { + let is_lf = char == b'\n'; + if is_lf || (wrap && x == width) { + y += 1; + x = 0; + if is_lf { + continue; + } + } + + if wrap || x < width { + chars.set(x, y, char); + } + x += 1; + } + + Ok(chars) + } +} + +use crate::Grid; +#[allow(unused)] // depends on features +pub use feature_cp437::*; + +#[cfg(feature = "cp437")] +mod feature_cp437 { + use super::*; + use crate::{ + cp437::{char_to_cp437, cp437_to_char}, + CharGrid, + }; + + impl From<&Cp437Grid> for CharGrid { + fn from(value: &Cp437Grid) -> Self { + value.map(cp437_to_char) + } + } + + impl From<&CharGrid> for Cp437Grid { + fn from(value: &CharGrid) -> Self { + value.map(char_to_cp437) + } + } + + impl From for Cp437Grid { + fn from(value: CharGrid) -> Self { + Cp437Grid::from(&value) + } + } +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn load_ascii_nowrap() { + let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] + .map(move |c| c as u8); + let expected = Cp437Grid::load(5, 2, &chars); + + let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap(); + // comma will be removed because line is too long and wrap is off + assert_eq!(actual, expected); + } + + #[test] + fn load_ascii_wrap() { + let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] + .map(move |c| c as u8); + let expected = Cp437Grid::load(5, 2, &chars); + + let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap(); + // line break will be added + assert_eq!(actual, expected); + } + + #[test] + fn load_ascii_invalid() { + assert_eq!( + Err(InvalidCharError { + char: '🥶', + index: 2 + }), + Cp437Grid::load_ascii("?#🥶42", 3, false) + ); + } +} + +#[cfg(test)] +#[cfg(feature = "cp437")] +mod tests_feature_cp437 { + use super::*; + use crate::CharGrid; + + #[test] + fn round_trip_cp437() { + let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); + let cp437 = Cp437Grid::from(&utf8); + let actual = CharGrid::from(&cp437); + assert_eq!(actual, utf8); + } +} diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index d89e0d7..8cb04d2 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -35,115 +35,44 @@ //! connection.send(command).expect("send failed"); //! ``` -use std::time::Duration; - -pub use bitvec; - pub use crate::bitmap::Bitmap; -pub use crate::brightness::{Brightness, BrightnessGrid}; +pub use crate::bitvec::BitVec; +pub use crate::brightness::Brightness; +pub use crate::brightness_grid::BrightnessGrid; +pub use crate::byte_grid::ByteGrid; pub use crate::char_grid::CharGrid; pub use crate::command::{Command, Offset}; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; -pub use crate::cp437::Cp437Grid; +pub use crate::constants::*; +pub use crate::cp437_grid::Cp437Grid; pub use crate::data_ref::DataRef; pub use crate::grid::Grid; pub use crate::origin::{Origin, Pixels, Tiles}; - -/// An alias for the specific type of [bitvec::prelude::BitVec] used. -pub type BitVec = bitvec::prelude::BitVec; - -/// A simple grid of bytes - see [primitive_grid::PrimitiveGrid]. -pub type ByteGrid = primitive_grid::PrimitiveGrid; +pub use crate::packet::{Header, Packet, Payload}; +pub use crate::value_grid::{ + IterGridRows, SetValueSeriesError, TryLoadPrimitiveGridError, Value, ValueGrid, +}; mod bitmap; +mod bitvec; mod brightness; +mod brightness_grid; +mod byte_grid; mod char_grid; mod command; mod command_code; mod compression; mod compression_code; mod connection; +mod constants; pub mod cp437; +mod cp437_grid; mod data_ref; mod grid; mod origin; -pub mod packet; -pub mod primitive_grid; - -/// size of a single tile in one dimension -pub const TILE_SIZE: usize = 8; - -/// Display tile count in the x-direction -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; -/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); -/// ``` -pub const TILE_WIDTH: usize = 56; - -/// Display tile count in the y-direction -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; -/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); -/// ``` -pub const TILE_HEIGHT: usize = 20; - -/// Display width in pixels -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; -/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); -/// ``` -pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; - -/// Display height in pixels -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; -/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); -/// ``` -pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; - -/// pixel count on whole screen -pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; - -/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. -/// -/// # Examples -/// -/// ```rust -/// # use std::time::Instant; -/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap}; -/// # let connection = servicepoint::Connection::Fake; -/// # let pixels = Bitmap::max_sized(); -/// loop { -/// let start = Instant::now(); -/// -/// // Change pixels here -/// -/// connection.send(Command::BitmapLinearWin( -/// Origin::new(0,0), -/// pixels, -/// CompressionCode::Lzma -/// )) -/// .expect("send failed"); -/// -/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby -/// std::thread::sleep(FRAME_PACING - start.elapsed()); -/// # break; // prevent doctest from hanging -/// } -/// ``` -pub const FRAME_PACING: Duration = Duration::from_millis(30); +mod packet; +mod value_grid; // include README.md in doctest #[doc = include_str!("../README.md")] diff --git a/crates/servicepoint/src/packet.rs b/crates/servicepoint/src/packet.rs index ee66426..099c50c 100644 --- a/crates/servicepoint/src/packet.rs +++ b/crates/servicepoint/src/packet.rs @@ -7,7 +7,7 @@ //! Converting a packet to a command and back: //! //! ```rust -//! use servicepoint::{Command, packet::Packet}; +//! use servicepoint::{Command, Packet}; //! # let command = Command::Clear; //! let packet: Packet = command.into(); //! let command: Command = Command::try_from(packet).expect("could not read command from packet"); @@ -16,20 +16,20 @@ //! Converting a packet to bytes and back: //! //! ```rust -//! use servicepoint::{Command, packet::Packet}; +//! use servicepoint::{Command, Packet}; //! # let command = Command::Clear; //! # let packet: Packet = command.into(); //! let bytes: Vec = packet.into(); //! let packet = Packet::try_from(bytes).expect("could not read packet from bytes"); //! ``` -use std::mem::size_of; - +use crate::command_code::CommandCode; use crate::compression::into_compressed; use crate::{ - command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset, - Origin, Pixels, Tiles, TILE_SIZE, + Bitmap, Command, CompressionCode, Grid, Offset, Origin, Pixels, Tiles, + TILE_SIZE, }; +use std::mem::size_of; /// A raw header. /// diff --git a/crates/servicepoint/src/primitive_grid.rs b/crates/servicepoint/src/value_grid.rs similarity index 78% rename from crates/servicepoint/src/primitive_grid.rs rename to crates/servicepoint/src/value_grid.rs index a6eaf5c..b01067f 100644 --- a/crates/servicepoint/src/primitive_grid.rs +++ b/crates/servicepoint/src/value_grid.rs @@ -1,17 +1,17 @@ -//! This module contains the implementation of the [PrimitiveGrid]. +//! This module contains the implementation of the [ValueGrid]. use std::fmt::Debug; use std::slice::{Iter, IterMut}; -use crate::{DataRef, Grid}; +use crate::*; -/// A type that can be stored in a [PrimitiveGrid], e.g. [char], [u8]. -pub trait PrimitiveGridType: Sized + Default + Copy + Clone + Debug {} -impl PrimitiveGridType for T {} +/// A type that can be stored in a [ValueGrid], e.g. [char], [u8]. +pub trait Value: Sized + Default + Copy + Clone + Debug {} +impl Value for T {} /// A 2D grid of bytes #[derive(Debug, Clone, PartialEq)] -pub struct PrimitiveGrid { +pub struct ValueGrid { width: usize, height: usize, data: Vec, @@ -19,7 +19,7 @@ pub struct PrimitiveGrid { /// Error type for methods that change a whole column or row at once #[derive(thiserror::Error, Debug, PartialEq)] -pub enum SeriesError { +pub enum SetValueSeriesError { #[error("The index {index} was out of bounds for size {size}")] /// The index {index} was out of bounds for size {size} OutOfBounds { @@ -38,15 +38,15 @@ pub enum SeriesError { }, } -impl PrimitiveGrid { - /// Creates a new [PrimitiveGrid] with the specified dimensions. +impl ValueGrid { + /// Creates a new [ValueGrid] with the specified dimensions. /// /// # Arguments /// /// - width: size in x-direction /// - height: size in y-direction /// - /// returns: [PrimitiveGrid] initialized to default value. + /// returns: [ValueGrid] initialized to default value. pub fn new(width: usize, height: usize) -> Self { Self { data: vec![Default::default(); width * height], @@ -55,9 +55,9 @@ impl PrimitiveGrid { } } - /// Loads a [PrimitiveGrid] with the specified dimensions from the provided data. + /// Loads a [ValueGrid] with the specified dimensions from the provided data. /// - /// returns: [PrimitiveGrid] that contains a copy of the provided data + /// returns: [ValueGrid] that contains a copy of the provided data /// /// # Panics /// @@ -76,9 +76,9 @@ impl PrimitiveGrid { } } - /// Loads a [PrimitiveGrid] with the specified dimensions from the provided data. + /// Loads a [ValueGrid] with the specified dimensions from the provided data. /// - /// returns: [PrimitiveGrid] that contains a copy of the provided data or [TryLoadPrimitiveGridError]. + /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadPrimitiveGridError]. pub fn try_load( width: usize, height: usize, @@ -95,7 +95,7 @@ impl PrimitiveGrid { }) } - /// Iterate over all cells in [PrimitiveGrid]. + /// Iterate over all cells in [ValueGrid]. /// /// Order is equivalent to the following loop: /// ``` @@ -111,9 +111,9 @@ impl PrimitiveGrid { self.data.iter() } - /// Iterate over all rows in [PrimitiveGrid] top to bottom. - pub fn iter_rows(&self) -> IterRows { - IterRows { + /// Iterate over all rows in [ValueGrid] top to bottom. + pub fn iter_rows(&self) -> IterGridRows { + IterGridRows { byte_grid: self, row: 0, } @@ -176,9 +176,9 @@ impl PrimitiveGrid { /// ``` /// [Brightness]: [crate::Brightness] /// [Command]: [crate::Command] - pub fn map(&self, f: F) -> PrimitiveGrid + pub fn map(&self, f: F) -> ValueGrid where - TConverted: PrimitiveGridType, + TConverted: Value, F: Fn(T) -> TConverted, { let data = self @@ -186,7 +186,7 @@ impl PrimitiveGrid { .iter() .map(|elem| f(*elem)) .collect::>(); - PrimitiveGrid::load(self.width(), self.height(), &data) + ValueGrid::load(self.width(), self.height(), &data) } /// Copies a row from the grid. @@ -212,9 +212,13 @@ impl PrimitiveGrid { /// Overwrites a column in the grid. /// /// Returns [Err] if x is out of bounds or `col` is not of the correct size. - pub fn set_col(&mut self, x: usize, col: &[T]) -> Result<(), SeriesError> { + pub fn set_col( + &mut self, + x: usize, + col: &[T], + ) -> Result<(), SetValueSeriesError> { if col.len() != self.height() { - return Err(SeriesError::InvalidLength { + return Err(SetValueSeriesError::InvalidLength { expected: self.height(), actual: col.len(), }); @@ -231,7 +235,7 @@ impl PrimitiveGrid { { Ok(()) } else { - Err(SeriesError::OutOfBounds { + Err(SetValueSeriesError::OutOfBounds { index: x, size: width, }) @@ -241,10 +245,14 @@ impl PrimitiveGrid { /// Overwrites a row in the grid. /// /// Returns [Err] if y is out of bounds or `row` is not of the correct size. - pub fn set_row(&mut self, y: usize, row: &[T]) -> Result<(), SeriesError> { + pub fn set_row( + &mut self, + y: usize, + row: &[T], + ) -> Result<(), SetValueSeriesError> { let width = self.width(); if row.len() != width { - return Err(SeriesError::InvalidLength { + return Err(SetValueSeriesError::InvalidLength { expected: width, actual: row.len(), }); @@ -253,7 +261,7 @@ impl PrimitiveGrid { let chunk = match self.data.chunks_exact_mut(width).nth(y) { Some(row) => row, None => { - return Err(SeriesError::OutOfBounds { + return Err(SetValueSeriesError::OutOfBounds { size: self.height(), index: y, }) @@ -271,7 +279,7 @@ pub enum TryLoadPrimitiveGridError { InvalidDimensions, } -impl Grid for PrimitiveGrid { +impl Grid for ValueGrid { /// Sets the value of the cell at the specified position in the `PrimitiveGrid. /// /// # Arguments @@ -314,7 +322,7 @@ impl Grid for PrimitiveGrid { } } -impl DataRef for PrimitiveGrid { +impl DataRef for ValueGrid { /// Get the underlying byte rows mutable fn data_ref_mut(&mut self) -> &mut [T] { self.data.as_mut_slice() @@ -326,20 +334,20 @@ impl DataRef for PrimitiveGrid { } } -impl From> for Vec { +impl From> for Vec { /// Turn into the underlying [`Vec`] containing the rows of bytes. - fn from(value: PrimitiveGrid) -> Self { + fn from(value: ValueGrid) -> Self { value.data } } -/// An iterator iver the rows in a [PrimitiveGrid] -pub struct IterRows<'t, T: PrimitiveGridType> { - byte_grid: &'t PrimitiveGrid, +/// An iterator iver the rows in a [ValueGrid] +pub struct IterGridRows<'t, T: Value> { + byte_grid: &'t ValueGrid, row: usize, } -impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { +impl<'t, T: Value> Iterator for IterGridRows<'t, T> { type Item = Iter<'t, T>; fn next(&mut self) -> Option { @@ -357,12 +365,14 @@ impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { #[cfg(test)] mod tests { - use crate::primitive_grid::{PrimitiveGrid, SeriesError}; - use crate::{DataRef, Grid}; + use crate::{ + value_grid::{SetValueSeriesError, ValueGrid}, + *, + }; #[test] fn fill() { - let mut grid = PrimitiveGrid::::new(2, 2); + let mut grid = ValueGrid::::new(2, 2); assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]); grid.fill(42); @@ -371,7 +381,7 @@ mod tests { #[test] fn get_set() { - let mut grid = PrimitiveGrid::new(2, 2); + let mut grid = ValueGrid::new(2, 2); assert_eq!(grid.get(0, 0), 0); assert_eq!(grid.get(1, 1), 0); @@ -386,7 +396,7 @@ mod tests { #[test] fn load() { - let mut grid = PrimitiveGrid::new(2, 3); + let mut grid = ValueGrid::new(2, 3); for x in 0..grid.width { for y in 0..grid.height { grid.set(x, y, (x + y) as u8); @@ -397,13 +407,13 @@ mod tests { let data: Vec = grid.into(); - let grid = PrimitiveGrid::load(2, 3, &data); + let grid = ValueGrid::load(2, 3, &data); assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); } #[test] fn mut_data_ref() { - let mut vec = PrimitiveGrid::new(2, 2); + let mut vec = ValueGrid::new(2, 2); let data_ref = vec.data_ref_mut(); data_ref.copy_from_slice(&[1, 2, 3, 4]); @@ -414,7 +424,7 @@ mod tests { #[test] fn iter() { - let mut vec = PrimitiveGrid::new(2, 2); + let mut vec = ValueGrid::new(2, 2); vec.set(1, 1, 5); let mut iter = vec.iter(); @@ -426,7 +436,7 @@ mod tests { #[test] fn iter_mut() { - let mut vec = PrimitiveGrid::new(2, 3); + let mut vec = ValueGrid::new(2, 3); for (index, cell) in vec.iter_mut().enumerate() { *cell = index as u8; } @@ -436,7 +446,7 @@ mod tests { #[test] fn iter_rows() { - let vec = PrimitiveGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); + let vec = ValueGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); for (y, row) in vec.iter_rows().enumerate() { for (x, val) in row.enumerate() { assert_eq!(*val, (x + y) as u8); @@ -447,20 +457,20 @@ mod tests { #[test] #[should_panic] fn out_of_bounds_x() { - let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); + let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); vec.set(2, 1, 5); } #[test] #[should_panic] fn out_of_bounds_y() { - let vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); + let vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); vec.get(1, 2); } #[test] fn ref_mut() { - let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); + let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); let top_left = vec.get_ref_mut(0, 0); *top_left += 5; @@ -471,7 +481,7 @@ mod tests { #[test] fn optional() { - let mut grid = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); + let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]); grid.set_optional(0, 0, 5); grid.set_optional(-1, 0, 8); grid.set_optional(0, 8, 42); @@ -483,18 +493,18 @@ mod tests { #[test] fn col() { - let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); + let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); assert_eq!(grid.get_col(0), Some(vec![0, 2, 4])); assert_eq!(grid.get_col(1), Some(vec![1, 3, 5])); assert_eq!(grid.get_col(2), None); assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(())); assert_eq!( grid.set_col(2, &[5, 7, 9]), - Err(SeriesError::OutOfBounds { size: 2, index: 2 }) + Err(SetValueSeriesError::OutOfBounds { size: 2, index: 2 }) ); assert_eq!( grid.set_col(0, &[5, 7]), - Err(SeriesError::InvalidLength { + Err(SetValueSeriesError::InvalidLength { expected: 3, actual: 2 }) @@ -504,7 +514,7 @@ mod tests { #[test] fn row() { - let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); + let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); assert_eq!(grid.get_row(0), Some(vec![0, 1])); assert_eq!(grid.get_row(2), Some(vec![4, 5])); assert_eq!(grid.get_row(3), None); @@ -512,11 +522,11 @@ mod tests { assert_eq!(grid.get_row(0), Some(vec![5, 7])); assert_eq!( grid.set_row(3, &[5, 7]), - Err(SeriesError::OutOfBounds { size: 3, index: 3 }) + Err(SetValueSeriesError::OutOfBounds { size: 3, index: 3 }) ); assert_eq!( grid.set_row(2, &[5, 7, 3]), - Err(SeriesError::InvalidLength { + Err(SetValueSeriesError::InvalidLength { expected: 2, actual: 3 }) diff --git a/crates/servicepoint_binding_c/src/bitmap.rs b/crates/servicepoint_binding_c/src/bitmap.rs index 2956de3..3313385 100644 --- a/crates/servicepoint_binding_c/src/bitmap.rs +++ b/crates/servicepoint_binding_c/src/bitmap.rs @@ -2,8 +2,8 @@ //! //! prefix `sp_bitmap_` -use std::ptr::NonNull; use servicepoint::{DataRef, Grid}; +use std::ptr::NonNull; use crate::byte_slice::SPByteSlice; @@ -43,9 +43,7 @@ pub unsafe extern "C" fn sp_bitmap_new( width: usize, height: usize, ) -> NonNull { - let result = Box::new(SPBitmap(servicepoint::Bitmap::new( - width, height, - ))); + let result = Box::new(SPBitmap(servicepoint::Bitmap::new(width, height))); NonNull::from(Box::leak(result)) } @@ -96,9 +94,8 @@ pub unsafe extern "C" fn sp_bitmap_load( ) -> NonNull { assert!(!data.is_null()); let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPBitmap(servicepoint::Bitmap::load( - width, height, data, - ))); + let result = + Box::new(SPBitmap(servicepoint::Bitmap::load(width, height, data))); NonNull::from(Box::leak(result)) } diff --git a/crates/servicepoint_binding_c/src/bitvec.rs b/crates/servicepoint_binding_c/src/bitvec.rs index 4ee5f52..484e849 100644 --- a/crates/servicepoint_binding_c/src/bitvec.rs +++ b/crates/servicepoint_binding_c/src/bitvec.rs @@ -2,9 +2,8 @@ //! //! prefix `sp_bitvec_` -use std::ptr::NonNull; use crate::SPByteSlice; -use servicepoint::bitvec::prelude::{BitVec, Msb0}; +use std::ptr::NonNull; /// A vector of bits /// @@ -14,15 +13,15 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0}; /// sp_bitvec_set(vec, 5, true); /// sp_bitvec_free(vec); /// ``` -pub struct SPBitVec(BitVec); +pub struct SPBitVec(servicepoint::BitVec); -impl From> for SPBitVec { - fn from(actual: BitVec) -> Self { +impl From for SPBitVec { + fn from(actual: servicepoint::BitVec) -> Self { Self(actual) } } -impl From for BitVec { +impl From for servicepoint::BitVec { fn from(value: SPBitVec) -> Self { value.0 } @@ -54,7 +53,7 @@ impl Clone for SPBitVec { /// by explicitly calling `sp_bitvec_free`. #[no_mangle] pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull { - let result = Box::new(SPBitVec(BitVec::repeat(false, size))); + let result = Box::new(SPBitVec(servicepoint::BitVec::repeat(false, size))); NonNull::from(Box::leak(result)) } @@ -81,7 +80,7 @@ pub unsafe extern "C" fn sp_bitvec_load( ) -> NonNull { assert!(!data.is_null()); let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPBitVec(BitVec::from_slice(data))); + let result = Box::new(SPBitVec(servicepoint::BitVec::from_slice(data))); NonNull::from(Box::leak(result)) } diff --git a/crates/servicepoint_binding_c/src/brightness_grid.rs b/crates/servicepoint_binding_c/src/brightness_grid.rs index d8c4310..93880a1 100644 --- a/crates/servicepoint_binding_c/src/brightness_grid.rs +++ b/crates/servicepoint_binding_c/src/brightness_grid.rs @@ -3,7 +3,7 @@ //! prefix `sp_brightness_grid_` use crate::SPByteSlice; -use servicepoint::{Brightness, ByteGrid, DataRef, Grid}; +use servicepoint::{DataRef, Grid}; use std::convert::Into; use std::intrinsics::transmute; use std::ptr::NonNull; @@ -48,9 +48,9 @@ pub unsafe extern "C" fn sp_brightness_grid_new( width: usize, height: usize, ) -> NonNull { - let result = Box::new(SPBrightnessGrid( - servicepoint::BrightnessGrid::new(width, height), - )); + let result = Box::new(SPBrightnessGrid(servicepoint::BrightnessGrid::new( + width, height, + ))); NonNull::from(Box::leak(result)) } @@ -80,7 +80,7 @@ pub unsafe extern "C" fn sp_brightness_grid_load( ) -> NonNull { assert!(!data.is_null()); let data = std::slice::from_raw_parts(data, data_length); - let grid = ByteGrid::load(width, height, data); + let grid = servicepoint::ByteGrid::load(width, height, data); let grid = servicepoint::BrightnessGrid::try_from(grid) .expect("invalid brightness value"); let result = Box::new(SPBrightnessGrid(grid)); @@ -203,8 +203,8 @@ pub unsafe extern "C" fn sp_brightness_grid_set( value: u8, ) { assert!(!brightness_grid.is_null()); - let brightness = - Brightness::try_from(value).expect("invalid brightness value"); + let brightness = servicepoint::Brightness::try_from(value) + .expect("invalid brightness value"); (*brightness_grid).0.set(x, y, brightness); } @@ -232,8 +232,8 @@ pub unsafe extern "C" fn sp_brightness_grid_fill( value: u8, ) { assert!(!brightness_grid.is_null()); - let brightness = - Brightness::try_from(value).expect("invalid brightness value"); + let brightness = servicepoint::Brightness::try_from(value) + .expect("invalid brightness value"); (*brightness_grid).0.fill(brightness); } @@ -311,7 +311,7 @@ pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( brightness_grid: *mut SPBrightnessGrid, ) -> SPByteSlice { assert!(!brightness_grid.is_null()); - assert_eq!(core::mem::size_of::(), 1); + assert_eq!(core::mem::size_of::(), 1); let data = (*brightness_grid).0.data_ref_mut(); // this assumes more about the memory layout than rust guarantees. yikes! let data: &mut [u8] = transmute(data); diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index 1c56d07..3da0cbb 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -4,8 +4,6 @@ use std::ptr::{null_mut, NonNull}; -use servicepoint::{Brightness, Origin}; - use crate::{ SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, SPPacket, @@ -164,11 +162,10 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { pub unsafe extern "C" fn sp_command_brightness( brightness: u8, ) -> NonNull { - let brightness = - Brightness::try_from(brightness).expect("invalid brightness"); - let result = Box::new(SPCommand( - servicepoint::Command::Brightness(brightness), - )); + let brightness = servicepoint::Brightness::try_from(brightness) + .expect("invalid brightness"); + let result = + Box::new(SPCommand(servicepoint::Command::Brightness(brightness))); NonNull::from(Box::leak(result)) } @@ -198,9 +195,10 @@ pub unsafe extern "C" fn sp_command_char_brightness( ) -> NonNull { assert!(!grid.is_null()); let byte_grid = *Box::from_raw(grid); - let result = Box::new(SPCommand( - servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0), - )); + let result = Box::new(SPCommand(servicepoint::Command::CharBrightness( + servicepoint::Origin::new(x, y), + byte_grid.0, + ))); NonNull::from(Box::leak(result)) } @@ -237,13 +235,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( ) -> NonNull { assert!(!bit_vec.is_null()); let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand( - servicepoint::Command::BitmapLinear( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ), - )); + let result = Box::new(SPCommand(servicepoint::Command::BitmapLinear( + offset, + bit_vec.into(), + compression.try_into().expect("invalid compression code"), + ))); NonNull::from(Box::leak(result)) } @@ -280,13 +276,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( ) -> NonNull { assert!(!bit_vec.is_null()); let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand( - servicepoint::Command::BitmapLinearAnd( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ), - )); + let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( + offset, + bit_vec.into(), + compression.try_into().expect("invalid compression code"), + ))); NonNull::from(Box::leak(result)) } @@ -323,13 +317,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( ) -> NonNull { assert!(!bit_vec.is_null()); let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand( - servicepoint::Command::BitmapLinearOr( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ), - )); + let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( + offset, + bit_vec.into(), + compression.try_into().expect("invalid compression code"), + ))); NonNull::from(Box::leak(result)) } @@ -366,13 +358,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( ) -> NonNull { assert!(!bit_vec.is_null()); let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand( - servicepoint::Command::BitmapLinearXor( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ), - )); + let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( + offset, + bit_vec.into(), + compression.try_into().expect("invalid compression code"), + ))); NonNull::from(Box::leak(result)) } @@ -402,9 +392,10 @@ pub unsafe extern "C" fn sp_command_cp437_data( ) -> NonNull { assert!(!grid.is_null()); let grid = *Box::from_raw(grid); - let result = Box::new(SPCommand( - servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0), - )); + let result = Box::new(SPCommand(servicepoint::Command::Cp437Data( + servicepoint::Origin::new(x, y), + grid.0, + ))); NonNull::from(Box::leak(result)) } @@ -437,15 +428,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( ) -> NonNull { assert!(!bitmap.is_null()); let byte_grid = (*Box::from_raw(bitmap)).0; - let result = Box::new(SPCommand( - servicepoint::Command::BitmapLinearWin( - Origin::new(x, y), - byte_grid, - compression_code - .try_into() - .expect("invalid compression code"), - ), - )); + let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( + servicepoint::Origin::new(x, y), + byte_grid, + compression_code + .try_into() + .expect("invalid compression code"), + ))); NonNull::from(Box::leak(result)) } diff --git a/crates/servicepoint_binding_c/src/cp437_grid.rs b/crates/servicepoint_binding_c/src/cp437_grid.rs index 32e2642..9b366c8 100644 --- a/crates/servicepoint_binding_c/src/cp437_grid.rs +++ b/crates/servicepoint_binding_c/src/cp437_grid.rs @@ -2,9 +2,9 @@ //! //! prefix `sp_cp437_grid_` -use std::ptr::NonNull; use crate::SPByteSlice; use servicepoint::{DataRef, Grid}; +use std::ptr::NonNull; /// A C-wrapper for grid containing codepage 437 characters. /// @@ -41,9 +41,8 @@ pub unsafe extern "C" fn sp_cp437_grid_new( width: usize, height: usize, ) -> NonNull { - let result = Box::new(SPCp437Grid( - servicepoint::Cp437Grid::new(width, height), - )); + let result = + Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height))); NonNull::from(Box::leak(result)) } @@ -73,9 +72,9 @@ pub unsafe extern "C" fn sp_cp437_grid_load( ) -> NonNull { assert!(data.is_null()); let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPCp437Grid( - servicepoint::Cp437Grid::load(width, height, data), - )); + let result = Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( + width, height, data, + ))); NonNull::from(Box::leak(result)) } diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs index 0e0ddd0..42442f5 100644 --- a/crates/servicepoint_binding_c/src/lib.rs +++ b/crates/servicepoint_binding_c/src/lib.rs @@ -25,8 +25,8 @@ //! } //! ``` -pub use crate::bitvec::*; pub use crate::bitmap::*; +pub use crate::bitvec::*; pub use crate::brightness_grid::*; pub use crate::byte_slice::*; pub use crate::command::*; @@ -35,8 +35,8 @@ pub use crate::constants::*; pub use crate::cp437_grid::*; pub use crate::packet::*; -mod bitvec; mod bitmap; +mod bitvec; mod brightness_grid; mod byte_slice; mod command; diff --git a/crates/servicepoint_binding_c/src/packet.rs b/crates/servicepoint_binding_c/src/packet.rs index 1f7082a..9293a8a 100644 --- a/crates/servicepoint_binding_c/src/packet.rs +++ b/crates/servicepoint_binding_c/src/packet.rs @@ -7,7 +7,7 @@ use std::ptr::{null_mut, NonNull}; use crate::SPCommand; /// The raw packet -pub struct SPPacket(pub(crate) servicepoint::packet::Packet); +pub struct SPPacket(pub(crate) servicepoint::Packet); /// Turns a [SPCommand] into a [SPPacket]. /// The [SPCommand] gets consumed. @@ -59,7 +59,7 @@ pub unsafe extern "C" fn sp_packet_try_load( ) -> *mut SPPacket { assert!(!data.is_null()); let data = std::slice::from_raw_parts(data, length); - match servicepoint::packet::Packet::try_from(data) { + match servicepoint::Packet::try_from(data) { Err(_) => null_mut(), Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), } @@ -108,8 +108,8 @@ pub unsafe extern "C" fn sp_packet_from_parts( Vec::from(payload) }; - let packet = servicepoint::packet::Packet { - header: servicepoint::packet::Header { + let packet = servicepoint::Packet { + header: servicepoint::Header { command_code, a, b, diff --git a/crates/servicepoint_binding_uniffi/src/char_grid.rs b/crates/servicepoint_binding_uniffi/src/char_grid.rs index ad686b4..e5d59a8 100644 --- a/crates/servicepoint_binding_uniffi/src/char_grid.rs +++ b/crates/servicepoint_binding_uniffi/src/char_grid.rs @@ -1,5 +1,5 @@ use crate::cp437_grid::Cp437Grid; -use servicepoint::{Grid, primitive_grid::SeriesError}; +use servicepoint::{Grid, SetValueSeriesError}; use std::convert::Into; use std::sync::{Arc, RwLock}; @@ -107,7 +107,10 @@ impl CharGrid { .unwrap() .get_row(y as usize) .map(String::from_iter) - .ok_or(CharGridError::OutOfBounds {index: y, size: self.height()}) + .ok_or(CharGridError::OutOfBounds { + index: y, + size: self.height(), + }) } pub fn get_col(&self, x: u64) -> Result { @@ -116,11 +119,16 @@ impl CharGrid { .unwrap() .get_col(x as usize) .map(String::from_iter) - .ok_or(CharGridError::OutOfBounds {index: x, size: self.width()}) + .ok_or(CharGridError::OutOfBounds { + index: x, + size: self.width(), + }) } pub fn to_cp437(&self) -> Arc { - Cp437Grid::internal_new(servicepoint::Cp437Grid::from(&*self.actual.read().unwrap())) + Cp437Grid::internal_new(servicepoint::Cp437Grid::from( + &*self.actual.read().unwrap(), + )) } } @@ -133,9 +141,7 @@ impl CharGrid { fn str_to_char(value: String) -> Result { if value.len() != 1 { - return Err(CharGridError::StringNotOneChar { - value, - }); + return Err(CharGridError::StringNotOneChar { value }); } let value = value.chars().nth(0).unwrap(); @@ -143,16 +149,16 @@ impl CharGrid { } } -impl From for CharGridError { - fn from(e: SeriesError) -> Self { +impl From for CharGridError { + fn from(e: SetValueSeriesError) -> Self { match e { - SeriesError::OutOfBounds { index, size } => { + SetValueSeriesError::OutOfBounds { index, size } => { CharGridError::OutOfBounds { index: index as u64, size: size as u64, } } - SeriesError::InvalidLength { actual, expected } => { + SetValueSeriesError::InvalidLength { actual, expected } => { CharGridError::InvalidSeriesLength { actual: actual as u64, expected: expected as u64, diff --git a/crates/servicepoint_binding_uniffi/src/constants.rs b/crates/servicepoint_binding_uniffi/src/constants.rs index be89a9c..3d0cfcb 100644 --- a/crates/servicepoint_binding_uniffi/src/constants.rs +++ b/crates/servicepoint_binding_uniffi/src/constants.rs @@ -1,4 +1,6 @@ -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record)] +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record, +)] pub struct Constants { pub tile_size: u64, pub tile_width: u64, @@ -10,7 +12,7 @@ pub struct Constants { #[uniffi::export] fn get_constants() -> Constants { -Constants { + Constants { tile_size: servicepoint::TILE_SIZE as u64, tile_width: servicepoint::TILE_WIDTH as u64, tile_height: servicepoint::TILE_HEIGHT as u64, diff --git a/crates/servicepoint_binding_uniffi/src/cp437_grid.rs b/crates/servicepoint_binding_uniffi/src/cp437_grid.rs index 3819176..b201238 100644 --- a/crates/servicepoint_binding_uniffi/src/cp437_grid.rs +++ b/crates/servicepoint_binding_uniffi/src/cp437_grid.rs @@ -1,6 +1,6 @@ +use crate::char_grid::CharGrid; use servicepoint::{DataRef, Grid}; use std::sync::{Arc, RwLock}; -use crate::char_grid::CharGrid; #[derive(uniffi::Object)] pub struct Cp437Grid { @@ -72,6 +72,8 @@ impl Cp437Grid { } pub fn to_utf8(&self) -> Arc { - CharGrid::internal_new(servicepoint::CharGrid::from(&*self.actual.read().unwrap())) + CharGrid::internal_new(servicepoint::CharGrid::from( + &*self.actual.read().unwrap(), + )) } } diff --git a/crates/servicepoint_binding_uniffi/src/lib.rs b/crates/servicepoint_binding_uniffi/src/lib.rs index 1c932e6..2ecad45 100644 --- a/crates/servicepoint_binding_uniffi/src/lib.rs +++ b/crates/servicepoint_binding_uniffi/src/lib.rs @@ -7,6 +7,6 @@ mod char_grid; mod command; mod compression_code; mod connection; +mod constants; mod cp437_grid; mod errors; -mod constants;