Merge pull request #13 from cccb/fonts
More CP437, PrimitiveGrid::map, renamings
This commit is contained in:
		
						commit
						30d74ff07d
					
				
					 45 changed files with 2419 additions and 1580 deletions
				
			
		
							
								
								
									
										10
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -147,9 +147,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.1.29" | ||||
| version = "1.1.30" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1" | ||||
| checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
|  "libc", | ||||
|  | @ -610,7 +610,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint" | ||||
| version = "0.9.1" | ||||
| version = "0.10.0" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "bzip2", | ||||
|  | @ -626,7 +626,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.9.1" | ||||
| version = "0.10.0" | ||||
| dependencies = [ | ||||
|  "cbindgen", | ||||
|  "servicepoint", | ||||
|  | @ -634,7 +634,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_cs" | ||||
| version = "0.9.1" | ||||
| version = "0.10.0" | ||||
| dependencies = [ | ||||
|  "csbindgen", | ||||
|  "servicepoint", | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ members = [ | |||
| ] | ||||
| 
 | ||||
| [workspace.package] | ||||
| version = "0.9.1" | ||||
| version = "0.10.0" | ||||
| 
 | ||||
| [workspace.lints.rust] | ||||
| missing-docs = "warn" | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ cargo add servicepoint | |||
| or | ||||
| ```toml | ||||
| [dependencies] | ||||
| servicepoint = "0.9.1" | ||||
| servicepoint = "0.10.0" | ||||
| ``` | ||||
| 
 | ||||
| ## Examples | ||||
|  |  | |||
|  | @ -6,16 +6,26 @@ use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin}; | |||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|     #[arg(short, long, default_value = "localhost:2342")] | ||||
|     #[arg(
 | ||||
|         short, | ||||
|         long, | ||||
|         default_value = "localhost:2342", | ||||
|         help = "Address of the display" | ||||
|     )] | ||||
|     destination: String, | ||||
|     #[arg(short, long, num_args = 1.., value_delimiter = '\n')] | ||||
|     #[arg(short, long, num_args = 1.., value_delimiter = '\n',
 | ||||
|         help = "Text to send - specify multiple times for multiple lines")] | ||||
|     text: Vec<String>, | ||||
|     #[arg(short, long, default_value_t = true)] | ||||
|     #[arg(
 | ||||
|         short, | ||||
|         long, | ||||
|         default_value_t = true, | ||||
|         help = "Clear screen before sending text" | ||||
|     )] | ||||
|     clear: bool, | ||||
| } | ||||
| 
 | ||||
| /// example: `cargo run -- --text "Hallo,
 | ||||
| /// CCCB"`
 | ||||
| /// example: `cargo run -- --text "Hallo" --text "CCCB"`
 | ||||
| fn main() { | ||||
|     let mut cli = Cli::parse(); | ||||
|     if cli.text.is_empty() { | ||||
|  | @ -31,15 +41,11 @@ fn main() { | |||
|             .expect("sending clear failed"); | ||||
|     } | ||||
| 
 | ||||
|     let text = cli.text.iter().fold(String::new(), move |str, line| { | ||||
|         let is_first = str.is_empty(); | ||||
|         str + if is_first { "" } else { "\n" } + line | ||||
|     }); | ||||
| 
 | ||||
|     let text = cli.text.join("\n"); | ||||
|     let grid = CharGrid::from(&*text); | ||||
|     let cp437_grid = Cp437Grid::from(&grid); | ||||
| 
 | ||||
|     connection | ||||
|         .send(Command::Cp437Data(Origin::new(0, 0), cp437_grid)) | ||||
|         .send(Command::Cp437Data(Origin::ZERO, cp437_grid)) | ||||
|         .expect("sending text failed"); | ||||
| } | ||||
|  |  | |||
|  | @ -15,23 +15,24 @@ fn main() { | |||
|     let connection = Connection::open(cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut pixels = PixelGrid::max_sized(); | ||||
|     let mut pixels = Bitmap::max_sized(); | ||||
|     pixels.fill(true); | ||||
| 
 | ||||
|     let command = Command::BitmapLinearWin( | ||||
|         Origin::new(0, 0), | ||||
|         Origin::ZERO, | ||||
|         pixels, | ||||
|         CompressionCode::Uncompressed, | ||||
|     ); | ||||
|     connection.send(command).expect("send failed"); | ||||
| 
 | ||||
|     let max_brightness = usize::from(u8::from(Brightness::MAX)); | ||||
|     let max_brightness: u8 = Brightness::MAX.into(); | ||||
|     let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT); | ||||
|     for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { | ||||
|         *byte = Brightness::try_from((index % max_brightness) as u8).unwrap(); | ||||
|         let level = index as u8 % max_brightness; | ||||
|         *byte = Brightness::try_from(level).unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     connection | ||||
|         .send(Command::CharBrightness(Origin::new(0, 0), brightnesses)) | ||||
|         .send(Command::CharBrightness(Origin::ZERO, brightnesses)) | ||||
|         .expect("send failed"); | ||||
| } | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ fn main() { | |||
| 
 | ||||
|     loop { | ||||
|         let command = Command::BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             Origin::ZERO, | ||||
|             field.clone(), | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|  | @ -34,7 +34,7 @@ fn main() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn iteration(field: PixelGrid) -> PixelGrid { | ||||
| fn iteration(field: Bitmap) -> Bitmap { | ||||
|     let mut next = field.clone(); | ||||
|     for x in 0..field.width() { | ||||
|         for y in 0..field.height() { | ||||
|  | @ -51,7 +51,7 @@ fn iteration(field: PixelGrid) -> PixelGrid { | |||
|     next | ||||
| } | ||||
| 
 | ||||
| fn count_neighbors(field: &PixelGrid, x: i32, y: i32) -> i32 { | ||||
| fn count_neighbors(field: &Bitmap, x: i32, y: i32) -> i32 { | ||||
|     let mut count = 0; | ||||
|     for nx in x - 1..=x + 1 { | ||||
|         for ny in y - 1..=y + 1 { | ||||
|  | @ -78,8 +78,8 @@ fn count_neighbors(field: &PixelGrid, x: i32, y: i32) -> i32 { | |||
|     count | ||||
| } | ||||
| 
 | ||||
| fn make_random_field(probability: f64) -> PixelGrid { | ||||
|     let mut field = PixelGrid::max_sized(); | ||||
| fn make_random_field(probability: f64) -> Bitmap { | ||||
|     let mut field = Bitmap::max_sized(); | ||||
|     let mut rng = rand::thread_rng(); | ||||
|     let d = distributions::Bernoulli::new(probability).unwrap(); | ||||
|     for x in 0..field.width() { | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ fn main() { | |||
|     let connection = Connection::open(Cli::parse().destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut pixels = PixelGrid::max_sized(); | ||||
|     let mut pixels = Bitmap::max_sized(); | ||||
|     for x_offset in 0..usize::MAX { | ||||
|         pixels.fill(false); | ||||
| 
 | ||||
|  | @ -25,7 +25,7 @@ fn main() { | |||
|         } | ||||
| 
 | ||||
|         let command = Command::BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             Origin::ZERO, | ||||
|             pixels.clone(), | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|  |  | |||
|  | @ -28,11 +28,11 @@ fn main() { | |||
| 
 | ||||
|     // put all pixels in on state
 | ||||
|     if cli.enable_all { | ||||
|         let mut filled_grid = PixelGrid::max_sized(); | ||||
|         let mut filled_grid = Bitmap::max_sized(); | ||||
|         filled_grid.fill(true); | ||||
| 
 | ||||
|         let command = BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             Origin::ZERO, | ||||
|             filled_grid, | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| //! Example for how to use the WebSocket connection
 | ||||
| 
 | ||||
| use servicepoint::{ | ||||
|     Command, CompressionCode, Connection, Grid, Origin, PixelGrid, | ||||
|     Bitmap, Command, CompressionCode, Connection, Grid, Origin, | ||||
| }; | ||||
| 
 | ||||
| fn main() { | ||||
|  | @ -13,7 +13,7 @@ fn main() { | |||
|     // use send_mut instead of send
 | ||||
|     connection.send_mut(Command::Clear).unwrap(); | ||||
| 
 | ||||
|     let mut pixels = PixelGrid::max_sized(); | ||||
|     let mut pixels = Bitmap::max_sized(); | ||||
|     pixels.fill(true); | ||||
| 
 | ||||
|     // use send_mut instead of send
 | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ fn main() { | |||
|     let connection = Connection::open(cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut enabled_pixels = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); | ||||
|     let mut enabled_pixels = Bitmap::max_sized(); | ||||
|     enabled_pixels.fill(true); | ||||
| 
 | ||||
|     for x_offset in 0..PIXEL_WIDTH { | ||||
|  |  | |||
|  | @ -6,21 +6,21 @@ use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH}; | |||
| 
 | ||||
| /// A grid of pixels stored in packed bytes.
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct PixelGrid { | ||||
| pub struct Bitmap { | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     bit_vec: SpBitVec, | ||||
| } | ||||
| 
 | ||||
| impl PixelGrid { | ||||
|     /// Creates a new [PixelGrid] with the specified dimensions.
 | ||||
| impl Bitmap { | ||||
|     /// Creates a new [Bitmap] with the specified dimensions.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// - `width`: size in pixels in x-direction
 | ||||
|     /// - `height`: size in pixels in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: [PixelGrid] initialized to all pixels off
 | ||||
|     /// returns: [Bitmap] initialized to all pixels off
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -40,14 +40,14 @@ impl PixelGrid { | |||
|         Self::new(PIXEL_WIDTH, PIXEL_HEIGHT) | ||||
|     } | ||||
| 
 | ||||
|     /// Loads a [PixelGrid] with the specified dimensions from the provided data.
 | ||||
|     /// Loads a [Bitmap] with the specified dimensions from the provided data.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// - `width`: size in pixels in x-direction
 | ||||
|     /// - `height`: size in pixels in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: [PixelGrid] that contains a copy of the provided data
 | ||||
|     /// returns: [Bitmap] that contains a copy of the provided data
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -64,12 +64,12 @@ impl PixelGrid { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all cells in [PixelGrid].
 | ||||
|     /// Iterate over all cells in [Bitmap].
 | ||||
|     ///
 | ||||
|     /// Order is equivalent to the following loop:
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::{PixelGrid, Grid};
 | ||||
|     /// # let grid = PixelGrid::new(8,2);
 | ||||
|     /// # use servicepoint::{Bitmap, Grid};
 | ||||
|     /// # let grid = Bitmap::new(8,2);
 | ||||
|     /// for y in 0..grid.height() {
 | ||||
|     ///     for x in 0..grid.width() {
 | ||||
|     ///         grid.get(x, y);
 | ||||
|  | @ -80,12 +80,12 @@ impl PixelGrid { | |||
|         self.bit_vec.iter().by_refs() | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all cells in [PixelGrid] mutably.
 | ||||
|     /// Iterate over all cells in [Bitmap] mutably.
 | ||||
|     ///
 | ||||
|     /// Order is equivalent to the following loop:
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::{PixelGrid, Grid};
 | ||||
|     /// # let mut grid = PixelGrid::new(8,2);
 | ||||
|     /// # use servicepoint::{Bitmap, Grid};
 | ||||
|     /// # let mut grid = Bitmap::new(8,2);
 | ||||
|     /// # let value = false;
 | ||||
|     /// for y in 0..grid.height() {
 | ||||
|     ///     for x in 0..grid.width() {
 | ||||
|  | @ -96,8 +96,8 @@ impl PixelGrid { | |||
|     ///
 | ||||
|     /// # Example
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::{PixelGrid, Grid};
 | ||||
|     /// # let mut grid = PixelGrid::new(8,2);
 | ||||
|     /// # use servicepoint::{Bitmap, Grid};
 | ||||
|     /// # let mut grid = Bitmap::new(8,2);
 | ||||
|     /// # let value = false;
 | ||||
|     /// for (index, mut pixel) in grid.iter_mut().enumerate() {
 | ||||
|     ///     pixel.set(index % 2 == 0)
 | ||||
|  | @ -107,17 +107,17 @@ impl PixelGrid { | |||
|         self.bit_vec.iter_mut() | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all rows in [PixelGrid] top to bottom.
 | ||||
|     /// Iterate over all rows in [Bitmap] top to bottom.
 | ||||
|     pub fn iter_rows(&self) -> IterRows { | ||||
|         IterRows { | ||||
|             pixel_grid: self, | ||||
|             bitmap: self, | ||||
|             row: 0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Grid<bool> for PixelGrid { | ||||
|     /// Sets the value of the specified position in the [PixelGrid].
 | ||||
| impl Grid<bool> for Bitmap { | ||||
|     /// Sets the value of the specified position in the [Bitmap].
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|  | @ -139,7 +139,7 @@ impl Grid<bool> for PixelGrid { | |||
|         self.bit_vec[x + y * self.width] | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the state of all pixels in the [PixelGrid].
 | ||||
|     /// Sets the state of all pixels in the [Bitmap].
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|  | @ -158,7 +158,7 @@ impl Grid<bool> for PixelGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl DataRef<u8> for PixelGrid { | ||||
| impl DataRef<u8> for Bitmap { | ||||
|     fn data_ref_mut(&mut self) -> &mut [u8] { | ||||
|         self.bit_vec.as_raw_mut_slice() | ||||
|     } | ||||
|  | @ -168,15 +168,15 @@ impl DataRef<u8> for PixelGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<PixelGrid> for Vec<u8> { | ||||
|     /// Turns a [PixelGrid] into the underlying [`Vec<u8>`].
 | ||||
|     fn from(value: PixelGrid) -> Self { | ||||
| impl From<Bitmap> for Vec<u8> { | ||||
|     /// Turns a [Bitmap] into the underlying [`Vec<u8>`].
 | ||||
|     fn from(value: Bitmap) -> Self { | ||||
|         value.bit_vec.into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IterRows<'t> { | ||||
|     pixel_grid: &'t PixelGrid, | ||||
|     bitmap: &'t Bitmap, | ||||
|     row: usize, | ||||
| } | ||||
| 
 | ||||
|  | @ -184,24 +184,24 @@ impl<'t> Iterator for IterRows<'t> { | |||
|     type Item = &'t BitSlice<u8, Msb0>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.row >= self.pixel_grid.height { | ||||
|         if self.row >= self.bitmap.height { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let start = self.row * self.pixel_grid.width; | ||||
|         let end = start + self.pixel_grid.width; | ||||
|         let start = self.row * self.bitmap.width; | ||||
|         let end = start + self.bitmap.width; | ||||
|         self.row += 1; | ||||
|         Some(&self.pixel_grid.bit_vec[start..end]) | ||||
|         Some(&self.bitmap.bit_vec[start..end]) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::{DataRef, Grid, PixelGrid}; | ||||
|     use crate::{Bitmap, DataRef, Grid}; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn fill() { | ||||
|         let mut grid = PixelGrid::new(8, 2); | ||||
|         let mut grid = Bitmap::new(8, 2); | ||||
|         assert_eq!(grid.data_ref(), [0x00, 0x00]); | ||||
| 
 | ||||
|         grid.fill(true); | ||||
|  | @ -213,7 +213,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn get_set() { | ||||
|         let mut grid = PixelGrid::new(8, 2); | ||||
|         let mut grid = Bitmap::new(8, 2); | ||||
|         assert!(!grid.get(0, 0)); | ||||
|         assert!(!grid.get(1, 1)); | ||||
| 
 | ||||
|  | @ -228,7 +228,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn load() { | ||||
|         let mut grid = PixelGrid::new(8, 3); | ||||
|         let mut grid = Bitmap::new(8, 3); | ||||
|         for x in 0..grid.width { | ||||
|             for y in 0..grid.height { | ||||
|                 grid.set(x, y, (x + y) % 2 == 0); | ||||
|  | @ -239,33 +239,33 @@ mod tests { | |||
| 
 | ||||
|         let data: Vec<u8> = grid.into(); | ||||
| 
 | ||||
|         let grid = PixelGrid::load(8, 3, &data); | ||||
|         let grid = Bitmap::load(8, 3, &data); | ||||
|         assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_x() { | ||||
|         let vec = PixelGrid::new(8, 2); | ||||
|         let vec = Bitmap::new(8, 2); | ||||
|         vec.get(8, 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_y() { | ||||
|         let mut vec = PixelGrid::new(8, 2); | ||||
|         let mut vec = Bitmap::new(8, 2); | ||||
|         vec.set(1, 2, false); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn iter() { | ||||
|         let grid = PixelGrid::new(8, 2); | ||||
|         let grid = Bitmap::new(8, 2); | ||||
|         assert_eq!(16, grid.iter().count()) | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn iter_rows() { | ||||
|         let grid = PixelGrid::load(8, 2, &[0x04, 0x40]); | ||||
|         let grid = Bitmap::load(8, 2, &[0x04, 0x40]); | ||||
|         let mut iter = grid.iter_rows(); | ||||
| 
 | ||||
|         assert_eq!(iter.next().unwrap().count_ones(), 1); | ||||
|  | @ -275,7 +275,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter_mut() { | ||||
|         let mut grid = PixelGrid::new(8, 2); | ||||
|         let mut grid = Bitmap::new(8, 2); | ||||
|         for (index, mut pixel) in grid.iter_mut().enumerate() { | ||||
|             pixel.set(index % 2 == 0); | ||||
|         } | ||||
|  | @ -284,7 +284,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn data_ref_mut() { | ||||
|         let mut grid = PixelGrid::new(8, 2); | ||||
|         let mut grid = Bitmap::new(8, 2); | ||||
|         let data = grid.data_ref_mut(); | ||||
|         data[1] = 0x0F; | ||||
|         assert!(grid.get(7, 1)); | ||||
|  | @ -60,6 +60,17 @@ impl Brightness { | |||
|     pub const MAX: Brightness = Brightness(11); | ||||
|     /// lowest possible brightness value, 0
 | ||||
|     pub const MIN: Brightness = Brightness(0); | ||||
| 
 | ||||
|     /// Create a brightness value without returning an error for brightnesses above [Brightness::MAX].
 | ||||
|     ///
 | ||||
|     /// returns: the specified value as a [Brightness], or [Brightness::MAX].
 | ||||
|     pub fn saturating_from(value: u8) -> Brightness { | ||||
|         if value > Brightness::MAX.into() { | ||||
|             Brightness::MAX | ||||
|         } else { | ||||
|             Brightness(value) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Default for Brightness { | ||||
|  | @ -138,4 +149,10 @@ mod tests { | |||
|         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)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ use crate::{ | |||
|     command_code::CommandCode, | ||||
|     compression::into_decompressed, | ||||
|     packet::{Header, Packet}, | ||||
|     Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, PixelGrid, | ||||
|     Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, | ||||
|     Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
|  | @ -76,12 +76,7 @@ pub enum Command { | |||
| 
 | ||||
|     /// Show text on the screen.
 | ||||
|     ///
 | ||||
|     /// The text is sent in the form of a 2D grid of characters.
 | ||||
|     ///
 | ||||
|     /// <div class="warning">
 | ||||
|     ///     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.
 | ||||
|     /// </div>
 | ||||
|     /// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|  | @ -100,6 +95,7 @@ pub enum Command { | |||
|     /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
 | ||||
|     /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
 | ||||
|     /// ```
 | ||||
|     /// [CP-437]: https://en.wikipedia.org/wiki/Code_page_437
 | ||||
|     Cp437Data(Origin<Tiles>, Cp437Grid), | ||||
| 
 | ||||
|     /// Overwrites a rectangular region of pixels.
 | ||||
|  | @ -109,23 +105,23 @@ pub enum Command { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
|     /// # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||
|     /// # let connection = servicepoint::Connection::Fake;
 | ||||
|     /// #
 | ||||
|     /// let mut pixels = PixelGrid::max_sized();
 | ||||
|     /// let mut pixels = Bitmap::max_sized();
 | ||||
|     /// // draw something to the pixels here
 | ||||
|     /// # pixels.set(2, 5, true);
 | ||||
|     ///
 | ||||
|     /// // create command to send pixels
 | ||||
|     /// let command = Command::BitmapLinearWin(
 | ||||
|     ///    servicepoint::Origin::new(0, 0),
 | ||||
|     ///    servicepoint::Origin::ZERO,
 | ||||
|     ///    pixels,
 | ||||
|     ///    CompressionCode::Uncompressed
 | ||||
|     /// );
 | ||||
|     ///
 | ||||
|     /// connection.send(command).expect("send failed");
 | ||||
|     /// ```
 | ||||
|     BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode), | ||||
|     BitmapLinearWin(Origin<Pixels>, Bitmap, CompressionCode), | ||||
| 
 | ||||
|     /// Set the brightness of all tiles to the same value.
 | ||||
|     ///
 | ||||
|  | @ -217,9 +213,8 @@ pub enum Command { | |||
|     BitmapLegacy, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| /// Err values for [Command::try_from].
 | ||||
| #[derive(PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum TryFromPacketError { | ||||
|     /// the contained command code does not correspond to a known command
 | ||||
|     InvalidCommand(u16), | ||||
|  | @ -342,7 +337,7 @@ impl Command { | |||
| 
 | ||||
|         Ok(Command::BitmapLinearWin( | ||||
|             Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), | ||||
|             PixelGrid::load( | ||||
|             Bitmap::load( | ||||
|                 tile_w as usize * TILE_SIZE, | ||||
|                 pixel_h as usize, | ||||
|                 &payload, | ||||
|  | @ -376,7 +371,7 @@ impl Command { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for Packets into `BitMapLinear*`-Commands
 | ||||
|     /// Helper method for Packets into `BitmapLinear*`-Commands
 | ||||
|     fn packet_into_linear_bitmap( | ||||
|         packet: Packet, | ||||
|     ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { | ||||
|  | @ -500,7 +495,8 @@ mod tests { | |||
|         command_code::CommandCode, | ||||
|         origin::Pixels, | ||||
|         packet::{Header, Packet}, | ||||
|         Brightness, Command, CompressionCode, Origin, PixelGrid, PrimitiveGrid, | ||||
|         Bitmap, Brightness, BrightnessGrid, Command, CompressionCode, Origin, | ||||
|         PrimitiveGrid, | ||||
|     }; | ||||
| 
 | ||||
|     fn round_trip(original: Command) { | ||||
|  | @ -592,8 +588,8 @@ mod tests { | |||
|                 compression, | ||||
|             )); | ||||
|             round_trip(Command::BitmapLinearWin( | ||||
|                 Origin::new(0, 0), | ||||
|                 PixelGrid::max_sized(), | ||||
|                 Origin::ZERO, | ||||
|                 Bitmap::max_sized(), | ||||
|                 compression, | ||||
|             )); | ||||
|         } | ||||
|  | @ -718,7 +714,7 @@ mod tests { | |||
|         for compression in all_compressions().to_owned() { | ||||
|             let p: Packet = Command::BitmapLinearWin( | ||||
|                 Origin::new(16, 8), | ||||
|                 PixelGrid::new(8, 8), | ||||
|                 Bitmap::new(8, 8), | ||||
|                 compression, | ||||
|             ) | ||||
|             .into(); | ||||
|  | @ -907,4 +903,28 @@ mod tests { | |||
|             Origin::new(1, 0) + Origin::new(3, 2) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn packet_into_char_brightness_invalid() { | ||||
|         let grid = BrightnessGrid::new(2, 2); | ||||
|         let command = Command::CharBrightness(Origin::ZERO, grid); | ||||
|         let mut packet: Packet = command.into(); | ||||
|         let slot = packet.payload.get_mut(1).unwrap(); | ||||
|         *slot = 23; | ||||
|         assert_eq!( | ||||
|             Command::try_from(packet), | ||||
|             Err(TryFromPacketError::InvalidBrightness(23)) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn packet_into_brightness_invalid() { | ||||
|         let mut packet: Packet = Command::Brightness(Brightness::MAX).into(); | ||||
|         let slot = packet.payload.get_mut(0).unwrap(); | ||||
|         *slot = 42; | ||||
|         assert_eq!( | ||||
|             Command::try_from(packet), | ||||
|             Err(TryFromPacketError::InvalidBrightness(42)) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -3,14 +3,14 @@ | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid};
 | ||||
| /// # use servicepoint::{Command, CompressionCode, Origin, Bitmap};
 | ||||
| /// // create command without payload compression
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed);
 | ||||
| /// # let pixels = Bitmap::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Uncompressed);
 | ||||
| ///
 | ||||
| /// // create command with payload compressed with lzma and appropriate header flags
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Lzma);
 | ||||
| /// # let pixels = Bitmap::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Lzma);
 | ||||
| /// ```
 | ||||
| #[repr(u16)] | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
|  |  | |||
|  | @ -1,4 +1,7 @@ | |||
| use crate::cp437::Cp437LoadError::InvalidChar; | ||||
| //! Conversion between UTF-8 and CP-437.
 | ||||
| //!
 | ||||
| //! Most of the functionality is only available with feature "cp437" enabled.
 | ||||
| 
 | ||||
| use crate::{Grid, PrimitiveGrid}; | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
|  | @ -10,9 +13,16 @@ pub type Cp437Grid = PrimitiveGrid<u8>; | |||
| /// A grid containing UTF-8 characters.
 | ||||
| pub type CharGrid = PrimitiveGrid<char>; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| /// Errors that can occur when loading CP-437.
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum Cp437LoadError { | ||||
|     InvalidChar { index: usize, char: char }, | ||||
|     /// Invalid character in input prevented loading
 | ||||
|     InvalidChar { | ||||
|         /// invalid character is at this position in input
 | ||||
|         index: usize, | ||||
|         /// the invalid character
 | ||||
|         char: char, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl Cp437Grid { | ||||
|  | @ -36,7 +46,7 @@ impl Cp437Grid { | |||
| 
 | ||||
|             for (index, char) in value.chars().enumerate() { | ||||
|                 if !char.is_ascii() { | ||||
|                     return Err(InvalidChar { index, char }); | ||||
|                     return Err(Cp437LoadError::InvalidChar { index, char }); | ||||
|                 } | ||||
| 
 | ||||
|                 let is_lf = char == '\n'; | ||||
|  | @ -85,21 +95,15 @@ mod feature_cp437 { | |||
| 
 | ||||
|     /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
 | ||||
|     ///
 | ||||
|     /// Mostly follows CP437, except for:
 | ||||
|     ///  * 0x0A & 0x0D are kept for use as line endings.
 | ||||
|     ///  * 0x1A is used for SAUCE.
 | ||||
|     ///  * 0x1B is used for ANSI escape sequences.
 | ||||
|     ///
 | ||||
|     /// These exclusions should be fine since most programs can't even use them
 | ||||
|     /// without issues. And this makes rendering simpler too.
 | ||||
|     /// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
 | ||||
|     ///
 | ||||
|     /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
 | ||||
|     ///
 | ||||
|     /// Copied from https://github.com/kip93/cp437-tools. License: GPL-3.0
 | ||||
|     /// Mostly copied from https://github.com/kip93/cp437-tools. License: GPL-3.0
 | ||||
|     #[rustfmt::skip] | ||||
|     const CP437_TO_UTF8: [char; 256] = [ | ||||
|         /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '\r', '♫', '☼', | ||||
|         /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '', '', '∟', '↔',  '▲', '▼', | ||||
|     pub const CP437_TO_UTF8: [char; 256] = [ | ||||
|         /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', | ||||
|         /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔',  '▲', '▼', | ||||
|         /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', | ||||
|         /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', | ||||
|         /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||||
|  | @ -116,7 +120,7 @@ mod feature_cp437 { | |||
|         /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', | ||||
|     ]; | ||||
| 
 | ||||
|     const UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> = | ||||
|     static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> = | ||||
|         once_cell::sync::Lazy::new(|| { | ||||
|             let pairs = CP437_TO_UTF8 | ||||
|                 .iter() | ||||
|  | @ -125,48 +129,34 @@ mod feature_cp437 { | |||
|             HashMap::from_iter(pairs) | ||||
|         }); | ||||
| 
 | ||||
|     const MISSING_CHAR_CP437: u8 = 0x3F; | ||||
|     const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
 | ||||
| 
 | ||||
|     impl From<&Cp437Grid> for CharGrid { | ||||
|         fn from(value: &Cp437Grid) -> Self { | ||||
|             let mut grid = Self::new(value.width(), value.height()); | ||||
| 
 | ||||
|             for y in 0..grid.height() { | ||||
|                 for x in 0..grid.width() { | ||||
|                     let converted = CP437_TO_UTF8[value.get(x, y) as usize]; | ||||
|                     grid.set(x, y, converted); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             grid | ||||
|             value.map(cp437_to_char) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<&CharGrid> for Cp437Grid { | ||||
|         fn from(value: &CharGrid) -> Self { | ||||
|             let mut grid = Self::new(value.width(), value.height()); | ||||
| 
 | ||||
|             for y in 0..grid.height() { | ||||
|                 for x in 0..grid.width() { | ||||
|                     let char = value.get(x, y); | ||||
|                     let converted = *UTF8_TO_CP437 | ||||
|                         .get(&char) | ||||
|                         .unwrap_or(&MISSING_CHAR_CP437); | ||||
|                     grid.set(x, y, converted); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             grid | ||||
|             value.map(char_to_cp437) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<&str> for CharGrid { | ||||
|         fn from(value: &str) -> Self { | ||||
|             let value = value.replace("\r\n", "\n"); | ||||
|             let lines = value.split('\n').collect::<Vec<_>>(); | ||||
|             let mut lines = value | ||||
|                 .split('\n') | ||||
|                 .map(move |line| line.trim_end()) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             let width = | ||||
|                 lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); | ||||
| 
 | ||||
|             while lines.last().is_some_and(move |line| line.is_empty()) { | ||||
|                 _ = lines.pop(); | ||||
|             } | ||||
| 
 | ||||
|             let mut grid = Self::new(width, lines.len()); | ||||
|             for (y, line) in lines.iter().enumerate() { | ||||
|                 for (x, char) in line.chars().enumerate() { | ||||
|  | @ -177,6 +167,44 @@ mod feature_cp437 { | |||
|             grid | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<&CharGrid> for String { | ||||
|         fn from(value: &CharGrid) -> Self { | ||||
|             value | ||||
|                 .iter_rows() | ||||
|                 .map(move |chars| { | ||||
|                     chars | ||||
|                         .collect::<String>() | ||||
|                         .replace('\0', " ") | ||||
|                         .trim_end() | ||||
|                         .to_string() | ||||
|                 }) | ||||
|                 .collect::<Vec<_>>() | ||||
|                 .join("\n") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// 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() | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a single CP-437 character to UTF-8.
 | ||||
|     pub fn cp437_to_char(cp437: u8) -> char { | ||||
|         CP437_TO_UTF8[cp437 as usize] | ||||
|     } | ||||
| 
 | ||||
|     /// Convert the provided text to CP-437 bytes.
 | ||||
|     ///
 | ||||
|     /// Characters that are not available are mapped to '?'.
 | ||||
|     pub fn str_to_cp437(utf8: &str) -> Vec<u8> { | ||||
|         utf8.chars().map(char_to_cp437).collect() | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a single UTF-8 character to CP-437.
 | ||||
|     pub fn char_to_cp437(utf8: char) -> u8 { | ||||
|         *UTF8_TO_CP437.get(&utf8).unwrap_or(&MISSING_CHAR_CP437) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  | @ -204,12 +232,23 @@ mod tests { | |||
|         // line break will be added
 | ||||
|         assert_eq!(actual, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_invalid() { | ||||
|         assert_eq!( | ||||
|             Err(Cp437LoadError::InvalidChar { | ||||
|                 char: '🥶', | ||||
|                 index: 2 | ||||
|             }), | ||||
|             Cp437Grid::load_ascii("?#🥶42", 3, false) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[cfg(feature = "cp437")] | ||||
| mod tests_feature_cp437 { | ||||
|     use crate::{CharGrid, Cp437Grid}; | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_cp437() { | ||||
|  | @ -218,4 +257,48 @@ mod tests_feature_cp437 { | |||
|         let actual = CharGrid::from(&cp437); | ||||
|         assert_eq!(actual, utf8); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn convert_str() { | ||||
|         // test text from https://int10h.org/oldschool-pc-fonts/fontlist/font?ibm_bios
 | ||||
|         let utf8 = r#"A quick brown fox jumps over the lazy dog.
 | ||||
|         0123456789 ¿?¡!`'"., <>()[]{} &@%*^#$\/
 | ||||
| 
 | ||||
|         * Wieniläinen sioux'ta puhuva ökyzombie diggaa Åsan roquefort-tacoja. | ||||
|         * Ça me fait peur de fêter noël là, sur cette île bizarroïde où une mère et sa môme essaient de me tuer avec un gâteau à la cigüe brûlé. | ||||
|         * Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich. | ||||
|         * El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro. | ||||
| 
 | ||||
|         ┌─┬─┐ ╔═╦═╗ ╒═╤═╕ ╓─╥─╖ | ||||
|         │ │ │ ║ ║ ║ │ │ │ ║ ║ ║ | ||||
|         ├─┼─┤ ╠═╬═╣ ╞═╪═╡ ╟─╫─╢ | ||||
|         └─┴─┘ ╚═╩═╝ ╘═╧═╛ ╙─╨─╜ | ||||
| 
 | ||||
|         ░░░░░ ▐▀█▀▌ .·∙•○°○•∙·. | ||||
|         ▒▒▒▒▒ ▐ █ ▌ ☺☻ ♥♦♣♠ ♪♫☼ | ||||
|         ▓▓▓▓▓ ▐▀█▀▌  $ ¢ £ ¥ ₧ | ||||
|         █████ ▐▄█▄▌ ◄►▲▼ ←→↑↓↕↨ | ||||
| 
 | ||||
|         ⌠ | ||||
|         │dx ≡ Σ √x²ⁿ·δx | ||||
|         ⌡"#;
 | ||||
| 
 | ||||
|         let cp437 = str_to_cp437(utf8); | ||||
|         let actual = cp437_to_str(&*cp437); | ||||
|         assert_eq!(utf8, actual) | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn convert_invalid() { | ||||
|         assert_eq!(cp437_to_char(char_to_cp437('😜')), '?'); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn str_to_char_grid() { | ||||
|         let original = "Hello\r\nWorld!\n...\n"; | ||||
|         let grid = CharGrid::from(original); | ||||
|         assert_eq!(3, grid.height()); | ||||
|         let actual = String::from(&grid); | ||||
|         assert_eq!("Hello\nWorld!\n...", actual); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -76,15 +76,9 @@ pub trait Grid<T> { | |||
|     ///
 | ||||
|     /// When the specified position is out of bounds for this grid.
 | ||||
|     fn assert_in_bounds(&self, x: usize, y: usize) { | ||||
|         assert!( | ||||
|             x < self.width(), | ||||
|             "cannot access index [{x}, {y}] because x is outside of bounds 0..{}", | ||||
|             self.width() - 1 | ||||
|         ); | ||||
|         assert!( | ||||
|             y < self.height(), | ||||
|             "cannot access index [{x}, {y}] because y is outside of bounds 0..{}", | ||||
|             self.height() - 1 | ||||
|         ); | ||||
|         let width = self.width(); | ||||
|         assert!(x < width, "cannot access index [{x}, {y}] because x is outside of bounds [0..{width})"); | ||||
|         let height = self.height(); | ||||
|         assert!(y < height, "cannot access index [{x}, {y}] because x is outside of bounds [0..{height})"); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| //! # Examples
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
| //! use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||
| //!
 | ||||
| //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
 | ||||
| //!     .expect("connection failed");
 | ||||
|  | @ -18,15 +18,15 @@ | |||
| //! ```
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
| //! # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||
| //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
 | ||||
| //!  // turn on all pixels in a grid
 | ||||
| //!  let mut pixels = PixelGrid::max_sized();
 | ||||
| //!  let mut pixels = Bitmap::max_sized();
 | ||||
| //!  pixels.fill(true);
 | ||||
| //!
 | ||||
| //!  // create command to send pixels
 | ||||
| //!  let command = Command::BitmapLinearWin(
 | ||||
| //!     servicepoint::Origin::new(0, 0),
 | ||||
| //!     servicepoint::Origin::ZERO,
 | ||||
| //!     pixels,
 | ||||
| //!     CompressionCode::Uncompressed
 | ||||
| //!  );
 | ||||
|  | @ -40,6 +40,7 @@ use std::time::Duration; | |||
| pub use bitvec; | ||||
| use bitvec::prelude::{BitVec, Msb0}; | ||||
| 
 | ||||
| pub use crate::bitmap::Bitmap; | ||||
| pub use crate::brightness::{Brightness, BrightnessGrid}; | ||||
| pub use crate::command::{Command, Offset}; | ||||
| pub use crate::compression_code::CompressionCode; | ||||
|  | @ -48,23 +49,22 @@ pub use crate::cp437::{CharGrid, Cp437Grid}; | |||
| pub use crate::data_ref::DataRef; | ||||
| pub use crate::grid::Grid; | ||||
| pub use crate::origin::{Origin, Pixels, Tiles}; | ||||
| pub use crate::pixel_grid::PixelGrid; | ||||
| pub use crate::primitive_grid::PrimitiveGrid; | ||||
| 
 | ||||
| type SpBitVec = BitVec<u8, Msb0>; | ||||
| 
 | ||||
| mod bitmap; | ||||
| mod brightness; | ||||
| mod command; | ||||
| mod command_code; | ||||
| mod compression; | ||||
| mod compression_code; | ||||
| mod connection; | ||||
| mod cp437; | ||||
| pub mod cp437; | ||||
| mod data_ref; | ||||
| mod grid; | ||||
| mod origin; | ||||
| pub mod packet; | ||||
| mod pixel_grid; | ||||
| mod primitive_grid; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
|  | @ -95,8 +95,8 @@ pub const TILE_HEIGHT: usize = 20; | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | ||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||
| 
 | ||||
|  | @ -105,8 +105,8 @@ pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | ||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | ||||
| 
 | ||||
|  | @ -119,10 +119,10 @@ pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; | |||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use std::time::Instant;
 | ||||
| /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, PixelGrid};
 | ||||
| /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
 | ||||
| /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
| /// #     .expect("connection failed");
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// # let pixels = Bitmap::max_sized();
 | ||||
| /// loop {
 | ||||
| ///    let start = Instant::now();
 | ||||
| ///
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ pub struct Origin<Unit: DisplayUnit> { | |||
| } | ||||
| 
 | ||||
| impl<Unit: DisplayUnit> Origin<Unit> { | ||||
|     /// Top-left. Equivalent to `Origin::new(0, 0)`.
 | ||||
|     /// Top-left. Equivalent to `Origin::ZERO`.
 | ||||
|     pub const ZERO: Self = Self { | ||||
|         x: 0, | ||||
|         y: 0, | ||||
|  |  | |||
|  | @ -27,8 +27,8 @@ use std::mem::size_of; | |||
| 
 | ||||
| use crate::compression::into_compressed; | ||||
| use crate::{ | ||||
|     command_code::CommandCode, Command, CompressionCode, Grid, Offset, Origin, | ||||
|     PixelGrid, Pixels, Tiles, TILE_SIZE, | ||||
|     command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset, | ||||
|     Origin, Pixels, Tiles, TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
| /// A raw header.
 | ||||
|  | @ -214,7 +214,7 @@ impl From<Command> for Packet { | |||
| } | ||||
| 
 | ||||
| impl Packet { | ||||
|     /// Helper method for `BitMapLinear*`-Commands into [Packet]
 | ||||
|     /// Helper method for `BitmapLinear*`-Commands into [Packet]
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn bitmap_linear_into_packet( | ||||
|         command: CommandCode, | ||||
|  | @ -239,7 +239,7 @@ impl Packet { | |||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn bitmap_win_into_packet( | ||||
|         origin: Origin<Pixels>, | ||||
|         pixels: PixelGrid, | ||||
|         pixels: Bitmap, | ||||
|         compression: CompressionCode, | ||||
|     ) -> Packet { | ||||
|         debug_assert_eq!(origin.x % 8, 0); | ||||
|  |  | |||
|  | @ -110,6 +110,34 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|             None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Convert between PrimitiveGrid types.
 | ||||
|     ///
 | ||||
|     /// See also [Iterator::map].
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command].
 | ||||
|     /// ```
 | ||||
|     /// # fn foo(grid: &mut PrimitiveGrid<u8>) {}
 | ||||
|     /// # use servicepoint::{Brightness, BrightnessGrid, Command, Origin, PrimitiveGrid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
|     /// let mut grid: PrimitiveGrid<u8> = PrimitiveGrid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
|     /// foo(&mut grid);
 | ||||
|     /// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
 | ||||
|     /// let command = Command::CharBrightness(Origin::ZERO, grid);
 | ||||
|     /// ```
 | ||||
|     pub fn map<TConverted, F>(&self, f: F) -> PrimitiveGrid<TConverted> | ||||
|     where | ||||
|         TConverted: PrimitiveGridType, | ||||
|         F: Fn(T) -> TConverted, | ||||
|     { | ||||
|         let data = self | ||||
|             .data_ref() | ||||
|             .iter() | ||||
|             .map(|elem| f(*elem)) | ||||
|             .collect::<Vec<_>>(); | ||||
|         PrimitiveGrid::load(self.width(), self.height(), &data) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] | |||
| cbindgen = "0.27.0" | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.9.1" | ||||
| version = "0.10.0" | ||||
| path = "../servicepoint" | ||||
| features = ["all_compressions"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,8 +21,8 @@ int main(void) { | |||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_pixel_grid_fill(pixels, true); | ||||
|     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ line_endings = "LF" | |||
| 
 | ||||
| ############################# Codegen Options ################################## | ||||
| 
 | ||||
| style = "both" | ||||
| style = "type" | ||||
| usize_is_size_t = true | ||||
| 
 | ||||
| # this is needed because otherwise the order in the C# bindings is different on different machines | ||||
|  | @ -31,3 +31,6 @@ all_features = true | |||
| [export] | ||||
| include = [] | ||||
| exclude = [] | ||||
| 
 | ||||
| [enum] | ||||
| rename_variants = "QualifiedScreamingSnakeCase" | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -6,10 +6,10 @@ int main(void) { | |||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_pixel_grid_fill(pixels, true); | ||||
|     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||
| 
 | ||||
|     sp_command_free(command); | ||||
|  |  | |||
							
								
								
									
										281
									
								
								crates/servicepoint_binding_c/src/bitmap.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								crates/servicepoint_binding_c/src/bitmap.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,281 @@ | |||
| //! C functions for interacting with [SPBitmap]s
 | ||||
| //!
 | ||||
| //! prefix `sp_bitmap_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| 
 | ||||
| use crate::byte_slice::SPByteSlice; | ||||
| 
 | ||||
| /// A grid of pixels.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// Cp437Grid grid = sp_bitmap_new(8, 3);
 | ||||
| /// sp_bitmap_fill(grid, true);
 | ||||
| /// sp_bitmap_set(grid, 0, 0, false);
 | ||||
| /// sp_bitmap_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPBitmap(pub(crate) servicepoint::Bitmap); | ||||
| 
 | ||||
| /// Creates a new [SPBitmap] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::new( | ||||
|         width, height, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPBitmap] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] that contains a copy of the provided data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the dimensions and data size do not match exactly.
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length` bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     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, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPBitmap].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bitmap_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_clone( | ||||
|     bitmap: *const SPBitmap, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let result = Box::new(SPBitmap((*bitmap).0.clone())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPBitmap].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not used concurrently or after bitmap call
 | ||||
| /// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     _ = Box::from_raw(bitmap); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position in the [SPBitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_get( | ||||
|     bitmap: *const SPBitmap, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> bool { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPBitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to write to
 | ||||
| /// - `x` and `y`: position of the cell
 | ||||
| /// - `value`: the value to write to the cell
 | ||||
| ///
 | ||||
| /// returns: old value of the cell
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_set( | ||||
|     bitmap: *mut SPBitmap, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the state of all pixels in the [SPBitmap].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to write to
 | ||||
| /// - `value`: the value to set all pixels to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - `bitmap` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_fill(bitmap: *mut SPBitmap, value: bool) { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width in pixels of the [SPBitmap] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_width(bitmap: *const SPBitmap) -> usize { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height in pixels of the [SPBitmap] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bitmap`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_height(bitmap: *const SPBitmap) -> usize { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     (*bitmap).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the [SPBitmap] instance.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bitmap` points to a valid [SPBitmap]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBitmap] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref( | ||||
|     bitmap: *mut SPBitmap, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let data = (*bitmap).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  | @ -1,7 +1,8 @@ | |||
| //! C functions for interacting with `SPBitVec`s
 | ||||
| //! C functions for interacting with [SPBitVec]s
 | ||||
| //!
 | ||||
| //! prefix `sp_bit_vec_`
 | ||||
| //! prefix `sp_bitvec_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::bitvec::prelude::{BitVec, Msb0}; | ||||
| 
 | ||||
|  | @ -9,9 +10,9 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0}; | |||
| ///
 | ||||
| /// # Examples
 | ||||
| /// ```C
 | ||||
| /// SPBitVec vec = sp_bit_vec_new(8);
 | ||||
| /// sp_bit_vec_set(vec, 5, true);
 | ||||
| /// sp_bit_vec_free(vec);
 | ||||
| /// SPBitVec vec = sp_bitvec_new(8);
 | ||||
| /// sp_bitvec_set(vec, 5, true);
 | ||||
| /// sp_bitvec_free(vec);
 | ||||
| /// ```
 | ||||
| pub struct SPBitVec(BitVec<u8, Msb0>); | ||||
| 
 | ||||
|  | @ -33,30 +34,37 @@ impl Clone for SPBitVec { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPBitVec` instance.
 | ||||
| /// Creates a new [SPBitVec] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `size`: size in bits.
 | ||||
| ///
 | ||||
| /// returns: `SPBitVec` with all bits set to false. Will never return NULL.
 | ||||
| /// returns: [SPBitVec] with all bits set to false. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When `size` is not divisible by 8.
 | ||||
| /// - when `size` is not divisible by 8.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec { | ||||
|     Box::into_raw(Box::new(SPBitVec(BitVec::repeat(false, size)))) | ||||
| pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> { | ||||
|     let result = Box::new(SPBitVec(BitVec::repeat(false, size))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Interpret the data as a series of bits and load then into a new `SPBitVec` instance.
 | ||||
| /// Interpret the data as a series of bits and load then into a new [SPBitVec] instance.
 | ||||
| ///
 | ||||
| /// returns: [SPBitVec] instance containing data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -65,48 +73,63 @@ pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec { | |||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_load( | ||||
| pub unsafe extern "C" fn sp_bitvec_load( | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut SPBitVec { | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(SPBitVec(BitVec::from_slice(data)))) | ||||
|     let result = Box::new(SPBitVec(BitVec::from_slice(data))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPBitVec`.
 | ||||
| /// Clones a [SPBitVec].
 | ||||
| ///
 | ||||
| /// returns: new [SPBitVec] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_clone( | ||||
| pub unsafe extern "C" fn sp_bitvec_clone( | ||||
|     bit_vec: *const SPBitVec, | ||||
| ) -> *mut SPBitVec { | ||||
|     Box::into_raw(Box::new((*bit_vec).clone())) | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let result = Box::new((*bit_vec).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPBitVec`.
 | ||||
| /// Deallocates a [SPBitVec].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `but_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `bit_vec` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| /// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) { | ||||
| pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     _ = Box::from_raw(bit_vec); | ||||
| } | ||||
| 
 | ||||
| /// Gets the value of a bit from the `SPBitVec`.
 | ||||
| /// Gets the value of a bit from the [SPBitVec].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -117,23 +140,25 @@ pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `index` out of bounds.
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| /// - when accessing `index` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_get( | ||||
| pub unsafe extern "C" fn sp_bitvec_get( | ||||
|     bit_vec: *const SPBitVec, | ||||
|     index: usize, | ||||
| ) -> bool { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     *(*bit_vec).0.get(index).unwrap() | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of a bit in the `SPBitVec`.
 | ||||
| /// Sets the value of a bit in the [SPBitVec].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -141,58 +166,68 @@ pub unsafe extern "C" fn sp_bit_vec_get( | |||
| /// - `index`: the bit index to edit
 | ||||
| /// - `value`: the value to set the bit to
 | ||||
| ///
 | ||||
| /// returns: old value of the bit
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `index` out of bounds.
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| /// - when accessing `index` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_set( | ||||
| pub unsafe extern "C" fn sp_bitvec_set( | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     index: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.set(index, value) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all bits in the `SPBitVec`.
 | ||||
| /// Sets the value of all bits in the [SPBitVec].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| /// - `value`: the value to set all bits to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_fill(bit_vec: *mut SPBitVec, value: bool) { | ||||
| pub unsafe extern "C" fn sp_bitvec_fill(bit_vec: *mut SPBitVec, value: bool) { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.fill(value) | ||||
| } | ||||
| 
 | ||||
| /// Gets the length of the `SPBitVec` in bits.
 | ||||
| /// Gets the length of the [SPBitVec] in bits.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_len(bit_vec: *const SPBitVec) -> usize { | ||||
| pub unsafe extern "C" fn sp_bitvec_len(bit_vec: *const SPBitVec) -> usize { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.len() | ||||
| } | ||||
| 
 | ||||
|  | @ -202,36 +237,46 @@ pub unsafe extern "C" fn sp_bit_vec_len(bit_vec: *const SPBitVec) -> usize { | |||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_is_empty(bit_vec: *const SPBitVec) -> bool { | ||||
| pub unsafe extern "C" fn sp_bitvec_is_empty(bit_vec: *const SPBitVec) -> bool { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     (*bit_vec).0.is_empty() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `SPBitVec` instance.
 | ||||
| /// Gets an unsafe reference to the data of the [SPBitVec] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is NULL
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPBitVec` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBitVec` or directly
 | ||||
| /// - `bit_vec` points to a valid [SPBitVec]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBitVec] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref( | ||||
| pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref( | ||||
|     bit_vec: *mut SPBitVec, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let data = (*bit_vec).0.as_raw_mut_slice(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  | @ -1,10 +1,19 @@ | |||
| //! C functions for interacting with `SPBrightnessGrid`s
 | ||||
| //! C functions for interacting with [SPBrightnessGrid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_brightness_grid_`
 | ||||
| 
 | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; | ||||
| use std::convert::Into; | ||||
| use std::intrinsics::transmute; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// see [Brightness::MIN]
 | ||||
| pub const SP_BRIGHTNESS_MIN: u8 = 0; | ||||
| /// see [Brightness::MAX]
 | ||||
| pub const SP_BRIGHTNESS_MAX: u8 = 11; | ||||
| /// Count of possible brightness values
 | ||||
| pub const SP_BRIGHTNESS_LEVELS: u8 = 12; | ||||
| 
 | ||||
| /// A grid containing brightness values.
 | ||||
| ///
 | ||||
|  | @ -21,17 +30,12 @@ use std::intrinsics::transmute; | |||
| /// SPCommand command = sp_command_char_brightness(grid);
 | ||||
| /// sp_connection_free(connection);
 | ||||
| /// ```
 | ||||
| #[derive(Clone)] | ||||
| pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); | ||||
| 
 | ||||
| impl Clone for SPBrightnessGrid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPBrightnessGrid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPBrightnessGrid` with the specified dimensions.
 | ||||
| /// Creates a new [SPBrightnessGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `SPBrightnessGrid` initialized to 0. Will never return NULL.
 | ||||
| /// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -43,17 +47,21 @@ impl Clone for SPBrightnessGrid { | |||
| pub unsafe extern "C" fn sp_brightness_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut SPBrightnessGrid { | ||||
|     Box::into_raw(Box::new(SPBrightnessGrid( | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     let result = Box::new(SPBrightnessGrid( | ||||
|         servicepoint::BrightnessGrid::new(width, height), | ||||
|     ))) | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `SPBrightnessGrid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// returns: new [SPBrightnessGrid] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When the provided `data_length` is not sufficient for the `height` and `width`
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -69,52 +77,67 @@ pub unsafe extern "C" fn sp_brightness_grid_load( | |||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut SPBrightnessGrid { | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let grid = PrimitiveGrid::load(width, height, data); | ||||
|     let grid = servicepoint::BrightnessGrid::try_from(grid) | ||||
|         .expect("invalid brightness value"); | ||||
|     Box::into_raw(Box::new(SPBrightnessGrid(grid))) | ||||
|     let result = Box::new(SPBrightnessGrid(grid)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPBrightnessGrid`.
 | ||||
| /// Clones a [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: new [SPBrightnessGrid] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_clone( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> *mut SPBrightnessGrid { | ||||
|     Box::into_raw(Box::new((*brightness_grid).clone())) | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let result = Box::new((*brightness_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPBrightnessGrid`.
 | ||||
| /// Deallocates a [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not used concurrently or after this call
 | ||||
| /// - `brightness_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| /// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_free( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     _ = Box::from_raw(brightness_grid); | ||||
| } | ||||
| 
 | ||||
|  | @ -125,15 +148,18 @@ pub unsafe extern "C" fn sp_brightness_grid_free( | |||
| /// - `brightness_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// returns: value at position
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `x` or `y` out of bounds.
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When accessing `x` or `y` out of bounds.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_get( | ||||
|  | @ -141,10 +167,11 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | |||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.get(x, y).into() | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `SPBrightnessGrid`.
 | ||||
| /// Sets the value of the specified position in the [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -156,6 +183,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When accessing `x` or `y` out of bounds.
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
|  | @ -163,7 +191,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBitVec`
 | ||||
| /// - `brightness_grid` points to a valid [SPBitVec]
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_set( | ||||
|  | @ -172,12 +200,13 @@ pub unsafe extern "C" fn sp_brightness_grid_set( | |||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.set(x, y, brightness); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `SPBrightnessGrid`.
 | ||||
| /// Sets the value of all cells in the [SPBrightnessGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -186,83 +215,106 @@ pub unsafe extern "C" fn sp_brightness_grid_set( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_fill( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.fill(brightness); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `SPBrightnessGrid` instance.
 | ||||
| /// Gets the width of the [SPBrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: width
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_width( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> usize { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `SPBrightnessGrid` instance.
 | ||||
| /// Gets the height of the [SPBrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// returns: height
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_height( | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> usize { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     (*brightness_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `SPBrightnessGrid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| /// returns: slice of bytes underlying the `brightness_grid`.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `brightness_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPBrightnessGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBrightnessGrid` or directly
 | ||||
| /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly
 | ||||
| #[no_mangle] | ||||
| 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::<Brightness>(), 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); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| //! FFI slice helper
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| #[repr(C)] | ||||
| /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
 | ||||
| ///
 | ||||
|  | @ -16,7 +18,7 @@ | |||
| ///   will try to free the memory of a potentially separate allocator.
 | ||||
| pub struct SPByteSlice { | ||||
|     /// The start address of the memory
 | ||||
|     pub start: *mut u8, | ||||
|     pub start: NonNull<u8>, | ||||
|     /// The amount of memory in bytes
 | ||||
|     pub length: usize, | ||||
| } | ||||
|  |  | |||
|  | @ -1,21 +1,21 @@ | |||
| //! C functions for interacting with `SPCommand`s
 | ||||
| //! C functions for interacting with [SPCommand]s
 | ||||
| //!
 | ||||
| //! prefix `sp_command_`
 | ||||
| 
 | ||||
| use std::ptr::null_mut; | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use servicepoint::{Brightness, Origin}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     SPBitVec, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, SPPacket, | ||||
|     SPPixelGrid, | ||||
|     SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, | ||||
|     SPPacket, | ||||
| }; | ||||
| 
 | ||||
| /// A low-level display command.
 | ||||
| ///
 | ||||
| /// This struct and associated functions implement the UDP protocol for the display.
 | ||||
| ///
 | ||||
| /// To send a `SPCommand`, use a `SPConnection`.
 | ||||
| /// To send a [SPCommand], use a [SPConnection].
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
|  | @ -31,20 +31,24 @@ impl Clone for SPCommand { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Tries to turn a `SPPacket` into a `SPCommand`.
 | ||||
| /// Tries to turn a [SPPacket] into a [SPCommand].
 | ||||
| ///
 | ||||
| /// The packet is deallocated in the process.
 | ||||
| ///
 | ||||
| /// Returns: pointer to new `SPCommand` instance or NULL
 | ||||
| /// Returns: pointer to new [SPCommand] instance or NULL if parsing failed.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `SPPacket` points to a valid instance of `SPPacket`
 | ||||
| /// - `SPPacket` is not used concurrently or after this call
 | ||||
| /// - [SPPacket] points to a valid instance of [SPPacket]
 | ||||
| /// - [SPPacket] is not used concurrently or after this call
 | ||||
| /// - the result is checked for NULL
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_try_from_packet( | ||||
|  | @ -57,28 +61,36 @@ pub unsafe extern "C" fn sp_command_try_from_packet( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPCommand` instance.
 | ||||
| /// Clones a [SPCommand] instance.
 | ||||
| ///
 | ||||
| /// returns: new [SPCommand] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `command` points to a valid instance of `SPCommand`
 | ||||
| /// - `command` points to a valid instance of [SPCommand]
 | ||||
| /// - `command` is not written to concurrently
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clone( | ||||
|     command: *const SPCommand, | ||||
| ) -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new((*command).clone())) | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!command.is_null()); | ||||
|     let result = Box::new((*command).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set all pixels to the off state.
 | ||||
| ///
 | ||||
| /// Does not affect brightness.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Clear` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::Clear] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
|  | @ -90,28 +102,30 @@ pub unsafe extern "C" fn sp_command_clone( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clear() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Clear))) | ||||
| pub unsafe extern "C" fn sp_command_clear() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Clear)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||
| ///
 | ||||
| /// Please do not send this in your normal program flow.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::HardReset` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::HardReset] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::HardReset))) | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::HardReset)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// A yet-to-be-tested command.
 | ||||
|  | @ -122,16 +136,17 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::FadeOut))) | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> { | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::FadeOut)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of all tiles to the same value.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Brightness` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::Brightness] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -141,44 +156,50 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_brightness( | ||||
|     brightness: u8, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     let brightness = | ||||
|         Brightness::try_from(brightness).expect("invalid brightness"); | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Brightness( | ||||
|         brightness, | ||||
|     )))) | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::Brightness(brightness), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set the brightness of individual tiles in a rectangular area of the display.
 | ||||
| ///
 | ||||
| /// The passed `SPBrightnessGrid` gets consumed.
 | ||||
| /// The passed [SPBrightnessGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::CharBrightness` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::CharBrightness] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of `SPBrightnessGrid`
 | ||||
| /// - `grid` points to a valid instance of [SPBrightnessGrid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_char_brightness( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPBrightnessGrid, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let byte_grid = *Box::from_raw(grid); | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::CharBrightness( | ||||
|         Origin::new(x, y), | ||||
|         byte_grid.0, | ||||
|     )))) | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data starting at the pixel offset on screen.
 | ||||
|  | @ -186,33 +207,42 @@ pub unsafe extern "C" fn sp_command_char_brightness( | |||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinear` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::BitmapLinear] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(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)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to an and-mask starting at the offset.
 | ||||
|  | @ -220,33 +250,42 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( | |||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearAnd` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_and( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(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)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to an or-mask starting at the offset.
 | ||||
|  | @ -254,33 +293,42 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( | |||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearOr` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_or( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(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)) | ||||
| } | ||||
| 
 | ||||
| /// Set pixel data according to a xor-mask starting at the offset.
 | ||||
|  | @ -288,100 +336,118 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( | |||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| /// The contained [SPBitVec] is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| /// The passed [SPBitVec] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearXor` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bit_vec` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of [SPBitVec]
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(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)) | ||||
| } | ||||
| 
 | ||||
| /// Show text on the screen.
 | ||||
| ///
 | ||||
| /// <div class="warning">
 | ||||
| ///     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.
 | ||||
| /// </div>
 | ||||
| /// The passed [SPCp437Grid] gets consumed.
 | ||||
| ///
 | ||||
| /// The passed `SPCp437Grid` gets consumed.///
 | ||||
| /// Returns: a new [Command::Cp437Data] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Cp437Data` instance. Will never return NULL.
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is null
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of `SPCp437Grid`
 | ||||
| /// - `grid` points to a valid instance of [SPCp437Grid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_cp437_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPCp437Grid, | ||||
| ) -> *mut SPCommand { | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Cp437Data( | ||||
|         Origin::new(x, y), | ||||
|         grid.0, | ||||
|     )))) | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Sets a window of pixels to the specified values.
 | ||||
| ///
 | ||||
| /// The passed `SPPixelGrid` gets consumed.
 | ||||
| /// The passed [SPBitmap] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearWin` instance. Will never return NULL.
 | ||||
| /// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `bitmap` is null
 | ||||
| /// - when `compression_code` is not a valid value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid instance of `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not used concurrently or after this call
 | ||||
| /// - `bitmap` points to a valid instance of [SPBitmap]
 | ||||
| /// - `bitmap` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_win( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     bitmap: *mut SPBitmap, | ||||
|     compression_code: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let byte_grid = (*Box::from_raw(pixel_grid)).0; | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( | ||||
|         Origin::new(x, y), | ||||
|         byte_grid, | ||||
|         compression_code | ||||
|             .try_into() | ||||
|             .expect("invalid compression code"), | ||||
|     )))) | ||||
| ) -> NonNull<SPCommand> { | ||||
|     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"), | ||||
|         ), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPCommand`.
 | ||||
| /// Deallocates a [SPCommand].
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
|  | @ -390,14 +456,19 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( | |||
| /// sp_command_free(c);
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `command` points to a valid `SPCommand`
 | ||||
| /// - `command` points to a valid [SPCommand]
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| /// - `command` was not passed to another consuming function, e.g. to create a `SPPacket`
 | ||||
| /// - `command` was not passed to another consuming function, e.g. to create a [SPPacket]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { | ||||
|     assert!(!command.is_null()); | ||||
|     _ = Box::from_raw(command); | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| //! C functions for interacting with `SPConnection`s
 | ||||
| //! C functions for interacting with [SPConnection]s
 | ||||
| //!
 | ||||
| //! prefix `sp_connection_`
 | ||||
| 
 | ||||
|  | @ -18,13 +18,13 @@ use crate::{SPCommand, SPPacket}; | |||
| /// ```
 | ||||
| pub struct SPConnection(pub(crate) servicepoint::Connection); | ||||
| 
 | ||||
| /// Creates a new instance of `SPConnection`.
 | ||||
| /// Creates a new instance of [SPConnection].
 | ||||
| ///
 | ||||
| /// returns: NULL if connection fails, or connected instance
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// Bad string encoding
 | ||||
| /// - when `host` is null or an invalid host
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -36,6 +36,7 @@ pub struct SPConnection(pub(crate) servicepoint::Connection); | |||
| pub unsafe extern "C" fn sp_connection_open( | ||||
|     host: *const c_char, | ||||
| ) -> *mut SPConnection { | ||||
|     assert!(!host.is_null()); | ||||
|     let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); | ||||
|     let connection = match servicepoint::Connection::open(host) { | ||||
|         Err(_) => return null_mut(), | ||||
|  | @ -45,59 +46,78 @@ pub unsafe extern "C" fn sp_connection_open( | |||
|     Box::into_raw(Box::new(SPConnection(connection))) | ||||
| } | ||||
| 
 | ||||
| /// Sends a `SPPacket` to the display using the `SPConnection`.
 | ||||
| /// Sends a [SPPacket] to the display using the [SPConnection].
 | ||||
| ///
 | ||||
| /// The passed `packet` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid instance of `SPConnection`
 | ||||
| /// - `packet` points to a valid instance of `SPPacket`
 | ||||
| /// - `connection` points to a valid instance of [SPConnection]
 | ||||
| /// - `packet` points to a valid instance of [SPPacket]
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_send_packet( | ||||
|     connection: *const SPConnection, | ||||
|     packet: *mut SPPacket, | ||||
| ) -> bool { | ||||
|     assert!(!connection.is_null()); | ||||
|     assert!(!packet.is_null()); | ||||
|     let packet = Box::from_raw(packet); | ||||
|     (*connection).0.send((*packet).0).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Sends a `SPCommand` to the display using the `SPConnection`.
 | ||||
| /// Sends a [SPCommand] to the display using the [SPConnection].
 | ||||
| ///
 | ||||
| /// The passed `command` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid instance of `SPConnection`
 | ||||
| /// - `command` points to a valid instance of `SPPacket`
 | ||||
| /// - `connection` points to a valid instance of [SPConnection]
 | ||||
| /// - `command` points to a valid instance of [SPPacket]
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_send_command( | ||||
|     connection: *const SPConnection, | ||||
|     command: *mut SPCommand, | ||||
| ) -> bool { | ||||
|     assert!(!connection.is_null()); | ||||
|     assert!(!command.is_null()); | ||||
|     let command = (*Box::from_raw(command)).0; | ||||
|     (*connection).0.send(command).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Closes and deallocates a `SPConnection`.
 | ||||
| /// Closes and deallocates a [SPConnection].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `connection` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid `SPConnection`
 | ||||
| /// - `connection` points to a valid [SPConnection]
 | ||||
| /// - `connection` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { | ||||
|     assert!(!connection.is_null()); | ||||
|     _ = Box::from_raw(connection); | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| //! C functions for interacting with `SPCp437Grid`s
 | ||||
| //! C functions for interacting with [SPCp437Grid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_cp437_grid_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| 
 | ||||
|  | @ -25,9 +26,9 @@ impl Clone for SPCp437Grid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPCp437Grid` with the specified dimensions.
 | ||||
| /// Creates a new [SPCp437Grid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `SPCp437Grid` initialized to 0.
 | ||||
| /// returns: [SPCp437Grid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -39,19 +40,21 @@ impl Clone for SPCp437Grid { | |||
| pub unsafe extern "C" fn sp_cp437_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut SPCp437Grid { | ||||
|     Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new( | ||||
|         width, height, | ||||
|     )))) | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     let result = Box::new(SPCp437Grid( | ||||
|         servicepoint::Cp437Grid::new(width, height), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `SPCp437Grid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a [SPCp437Grid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When the provided `data_length` is not sufficient for the `height` and `width`
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -67,43 +70,56 @@ pub unsafe extern "C" fn sp_cp437_grid_load( | |||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut SPCp437Grid { | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     assert!(data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(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)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPCp437Grid`.
 | ||||
| /// Clones a [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_clone( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> *mut SPCp437Grid { | ||||
|     Box::into_raw(Box::new((*cp437_grid).clone())) | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     let result = Box::new((*cp437_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPCp437Grid`.
 | ||||
| /// Deallocates a [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not used concurrently or after cp437_grid call
 | ||||
| /// - `cp437_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| /// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     _ = Box::from_raw(cp437_grid); | ||||
| } | ||||
| 
 | ||||
|  | @ -116,13 +132,14 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `x` or `y` out of bounds.
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_get( | ||||
|  | @ -130,10 +147,11 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | |||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `SPCp437Grid`.
 | ||||
| /// Sets the value of the specified position in the [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -145,13 +163,14 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `x` or `y` out of bounds.
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPBitVec`
 | ||||
| /// - `cp437_grid` points to a valid [SPBitVec]
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_set( | ||||
|  | @ -160,84 +179,104 @@ pub unsafe extern "C" fn sp_cp437_grid_set( | |||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `SPCp437Grid`.
 | ||||
| /// Sets the value of all cells in the [SPCp437Grid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_fill( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `SPCp437Grid` instance.
 | ||||
| /// Gets the width of the [SPCp437Grid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_width( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> usize { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `SPCp437Grid` instance.
 | ||||
| /// Gets the height of the [SPCp437Grid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_height( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> usize { | ||||
|     assert!(!cp437_grid.is_null()); | ||||
|     (*cp437_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `SPCp437Grid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the [SPCp437Grid] instance.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `cp437_grid` is NULL
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPCp437Grid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPCp437Grid` or directly
 | ||||
| /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||
| /// - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*cp437_grid).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -13,8 +13,8 @@ | |||
| //!     if (connection == NULL)
 | ||||
| //!         return 1;
 | ||||
| //!
 | ||||
| //!     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||
| //!     sp_pixel_grid_fill(pixels, true);
 | ||||
| //!     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||
| //!     sp_bitmap_fill(pixels, true);
 | ||||
| //!
 | ||||
| //!     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
 | ||||
| //!     while (sp_connection_send_command(connection, sp_command_clone(command)));
 | ||||
|  | @ -25,7 +25,8 @@ | |||
| //! }
 | ||||
| //! ```
 | ||||
| 
 | ||||
| pub use crate::bit_vec::*; | ||||
| pub use crate::bitvec::*; | ||||
| pub use crate::bitmap::*; | ||||
| pub use crate::brightness_grid::*; | ||||
| pub use crate::byte_slice::*; | ||||
| pub use crate::command::*; | ||||
|  | @ -33,9 +34,9 @@ pub use crate::connection::*; | |||
| pub use crate::constants::*; | ||||
| pub use crate::cp437_grid::*; | ||||
| pub use crate::packet::*; | ||||
| pub use crate::pixel_grid::*; | ||||
| 
 | ||||
| mod bit_vec; | ||||
| mod bitvec; | ||||
| mod bitmap; | ||||
| mod brightness_grid; | ||||
| mod byte_slice; | ||||
| mod command; | ||||
|  | @ -43,4 +44,3 @@ mod connection; | |||
| mod constants; | ||||
| mod cp437_grid; | ||||
| mod packet; | ||||
| mod pixel_grid; | ||||
|  |  | |||
|  | @ -1,53 +1,63 @@ | |||
| //! C functions for interacting with `SPPacket`s
 | ||||
| //! C functions for interacting with [SPPacket]s
 | ||||
| //!
 | ||||
| //! prefix `sp_packet_`
 | ||||
| 
 | ||||
| use std::ptr::null_mut; | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use crate::SPCommand; | ||||
| 
 | ||||
| /// The raw packet
 | ||||
| pub struct SPPacket(pub(crate) servicepoint::packet::Packet); | ||||
| 
 | ||||
| /// Turns a `SPCommand` into a `SPPacket`.
 | ||||
| /// The `SPCommand` gets consumed.
 | ||||
| /// Turns a [SPCommand] into a [SPPacket].
 | ||||
| /// The [SPCommand] gets consumed.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `command` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `SPCommand` points to a valid instance of `SPCommand`
 | ||||
| /// - `SPCommand` is not used concurrently or after this call
 | ||||
| /// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - [SPCommand] points to a valid instance of [SPCommand]
 | ||||
| /// - [SPCommand] is not used concurrently or after this call
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_command( | ||||
|     command: *mut SPCommand, | ||||
| ) -> *mut SPPacket { | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert!(!command.is_null()); | ||||
|     let command = *Box::from_raw(command); | ||||
|     let packet = SPPacket(command.0.into()); | ||||
|     Box::into_raw(Box::new(packet)) | ||||
|     let result = Box::new(SPPacket(command.0.into())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Tries to load a `SPPacket` from the passed array with the specified length.
 | ||||
| /// Tries to load a [SPPacket] from the passed array with the specified length.
 | ||||
| ///
 | ||||
| /// returns: NULL in case of an error, pointer to the allocated packet otherwise
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory region of at least `length` bytes
 | ||||
| /// - `data` is not written to concurrently
 | ||||
| /// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_try_load( | ||||
|     data: *const u8, | ||||
|     length: usize, | ||||
| ) -> *mut SPPacket { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, length); | ||||
|     match servicepoint::packet::Packet::try_from(data) { | ||||
|         Err(_) => null_mut(), | ||||
|  | @ -55,34 +65,45 @@ pub unsafe extern "C" fn sp_packet_try_load( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPPacket`.
 | ||||
| /// Clones a [SPPacket].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid `SPPacket`
 | ||||
| /// - `packet` points to a valid [SPPacket]
 | ||||
| /// - `packet` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_clone( | ||||
|     packet: *const SPPacket, | ||||
| ) -> *mut SPPacket { | ||||
|     Box::into_raw(Box::new(SPPacket((*packet).0.clone()))) | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert!(!packet.is_null()); | ||||
|     let result = Box::new(SPPacket((*packet).0.clone())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPPacket`.
 | ||||
| /// Deallocates a [SPPacket].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `sp_packet_free` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid `SPPacket`
 | ||||
| /// - `packet` points to a valid [SPPacket]
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { | ||||
|     assert!(!packet.is_null()); | ||||
|     _ = Box::from_raw(packet) | ||||
| } | ||||
|  |  | |||
|  | @ -1,248 +0,0 @@ | |||
| //! C functions for interacting with `SPPixelGrid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_pixel_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| 
 | ||||
| use crate::byte_slice::SPByteSlice; | ||||
| 
 | ||||
| /// A grid of pixels.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// Cp437Grid grid = sp_pixel_grid_new(8, 3);
 | ||||
| /// sp_pixel_grid_fill(grid, true);
 | ||||
| /// sp_pixel_grid_set(grid, 0, 0, false);
 | ||||
| /// sp_pixel_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPPixelGrid(pub(crate) servicepoint::PixelGrid); | ||||
| 
 | ||||
| /// Creates a new `SPPixelGrid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: `SPPixelGrid` initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut SPPixelGrid { | ||||
|     Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::new( | ||||
|         width, height, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `SPPixelGrid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: `SPPixelGrid` that contains a copy of the provided data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when the dimensions and data size do not match exactly.
 | ||||
| /// - when the width is not dividable by 8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length` bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut SPPixelGrid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::load( | ||||
|         width, height, data, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_clone( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> *mut SPPixelGrid { | ||||
|     Box::into_raw(Box::new(SPPixelGrid((*pixel_grid).0.clone()))) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not used concurrently or after pixel_grid call
 | ||||
| /// - `pixel_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_free(pixel_grid: *mut SPPixelGrid) { | ||||
|     _ = Box::from_raw(pixel_grid); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `x` or `y` out of bounds.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_get( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> bool { | ||||
|     (*pixel_grid).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `pixel_grid`: instance to write to
 | ||||
| /// - `x` and `y`: position of the cell
 | ||||
| /// - `value`: the value to write to the cell
 | ||||
| ///
 | ||||
| /// returns: old value of the cell
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When accessing `x` or `y` out of bounds.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_set( | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     (*pixel_grid).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the state of all pixels in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `pixel_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all pixels to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_fill( | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     value: bool, | ||||
| ) { | ||||
|     (*pixel_grid).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width in pixels of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_width( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> usize { | ||||
|     (*pixel_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height in pixels of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_height( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> usize { | ||||
|     (*pixel_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPPixelGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPPixelGrid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_unsafe_data_ref( | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*pixel_grid).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  | @ -13,8 +13,8 @@ test = false | |||
| csbindgen = "1.9.3" | ||||
| 
 | ||||
| [dependencies] | ||||
| servicepoint_binding_c = { version = "0.9.1", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.9.1", path = "../servicepoint" } | ||||
| servicepoint_binding_c = { version = "0.10.0", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.10.0", path = "../servicepoint" } | ||||
| 
 | ||||
| [lints] | ||||
| workspace = true | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ using ServicePoint; | |||
| 
 | ||||
| // using statement calls Dispose() on scope exit, which frees unmanaged instances | ||||
| using var connection = Connection.Open("127.0.0.1:2342"); | ||||
| using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight); | ||||
| using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight); | ||||
| 
 | ||||
| while (true) | ||||
| { | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -8,7 +8,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new BitVec(NativeMethods.sp_bit_vec_new((nuint)size)); | ||||
|             return new BitVec(NativeMethods.sp_bitvec_new((nuint)size)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -18,7 +18,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|         { | ||||
|             fixed (byte* bytesPtr = bytes) | ||||
|             { | ||||
|                 return new BitVec(NativeMethods.sp_bit_vec_load(bytesPtr, (nuint)bytes.Length)); | ||||
|                 return new BitVec(NativeMethods.sp_bitvec_load(bytesPtr, (nuint)bytes.Length)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -27,7 +27,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new BitVec(NativeMethods.sp_bit_vec_clone(Instance)); | ||||
|             return new BitVec(NativeMethods.sp_bitvec_clone(Instance)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -37,14 +37,14 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return NativeMethods.sp_bit_vec_get(Instance, (nuint)index); | ||||
|                 return NativeMethods.sp_bitvec_get(Instance, (nuint)index); | ||||
|             } | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 NativeMethods.sp_bit_vec_set(Instance, (nuint)index, value); | ||||
|                 NativeMethods.sp_bitvec_set(Instance, (nuint)index, value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -53,7 +53,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             NativeMethods.sp_bit_vec_fill(Instance, value); | ||||
|             NativeMethods.sp_bitvec_fill(Instance, value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -63,7 +63,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_bit_vec_len(Instance); | ||||
|                 return (int)NativeMethods.sp_bitvec_len(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -74,7 +74,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 var slice = NativeMethods.sp_bit_vec_unsafe_data_ref(Instance); | ||||
|                 var slice = NativeMethods.sp_bitvec_unsafe_data_ref(Instance); | ||||
|                 return new Span<byte>(slice.start, (int)slice.length); | ||||
|             } | ||||
|         } | ||||
|  | @ -84,5 +84,5 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | |||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_bit_vec_free(Instance); | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_bitvec_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -2,33 +2,33 @@ using ServicePoint.BindGen; | |||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | ||||
| public sealed class Bitmap : SpNativeInstance<BindGen.Bitmap> | ||||
| { | ||||
|     public static PixelGrid New(int width, int height) | ||||
|     public static Bitmap New(int width, int height) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new PixelGrid(NativeMethods.sp_pixel_grid_new((nuint)width, (nuint)height)); | ||||
|             return new Bitmap(NativeMethods.sp_bitmap_new((nuint)width, (nuint)height)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static PixelGrid Load(int width, int height, Span<byte> bytes) | ||||
|     public static Bitmap Load(int width, int height, Span<byte> bytes) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             fixed (byte* bytesPtr = bytes) | ||||
|             { | ||||
|                 return new PixelGrid(NativeMethods.sp_pixel_grid_load((nuint)width, (nuint)height, bytesPtr, | ||||
|                 return new Bitmap(NativeMethods.sp_bitmap_load((nuint)width, (nuint)height, bytesPtr, | ||||
|                     (nuint)bytes.Length)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public PixelGrid Clone() | ||||
|     public Bitmap Clone() | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new PixelGrid(NativeMethods.sp_pixel_grid_clone(Instance)); | ||||
|             return new Bitmap(NativeMethods.sp_bitmap_clone(Instance)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -38,14 +38,14 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return NativeMethods.sp_pixel_grid_get(Instance, (nuint)x, (nuint)y); | ||||
|                 return NativeMethods.sp_bitmap_get(Instance, (nuint)x, (nuint)y); | ||||
|             } | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 NativeMethods.sp_pixel_grid_set(Instance, (nuint)x, (nuint)y, value); | ||||
|                 NativeMethods.sp_bitmap_set(Instance, (nuint)x, (nuint)y, value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -54,7 +54,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             NativeMethods.sp_pixel_grid_fill(Instance, value); | ||||
|             NativeMethods.sp_bitmap_fill(Instance, value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -64,7 +64,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_pixel_grid_width(Instance); | ||||
|                 return (int)NativeMethods.sp_bitmap_width(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -75,7 +75,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_pixel_grid_height(Instance); | ||||
|                 return (int)NativeMethods.sp_bitmap_height(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -86,15 +86,15 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 var slice = NativeMethods.sp_pixel_grid_unsafe_data_ref(Instance); | ||||
|                 var slice = NativeMethods.sp_bitmap_unsafe_data_ref(Instance); | ||||
|                 return new Span<byte>(slice.start, (int)slice.length); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe PixelGrid(BindGen.PixelGrid* instance) : base(instance) | ||||
|     private unsafe Bitmap(BindGen.Bitmap* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_pixel_grid_free(Instance); | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_bitmap_free(Instance); | ||||
| } | ||||
|  | @ -105,11 +105,11 @@ public sealed class Command : SpNativeInstance<BindGen.Command> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Command BitmapLinearWin(int x, int y, PixelGrid pixelGrid, CompressionCode compression) | ||||
|     public static Command BitmapLinearWin(int x, int y, Bitmap bitmap, CompressionCode compression) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, pixelGrid.Into(), compression)); | ||||
|             return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, bitmap.Into(), compression)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,22 +1,24 @@ | |||
| using ServicePoint.BindGen; | ||||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public static class Constants | ||||
| { | ||||
|     /// size of a single tile in one dimension | ||||
|     public const int TileSize = 8; | ||||
|     public const nuint TileSize = NativeMethods.SP_TILE_SIZE; | ||||
| 
 | ||||
|     /// tile count in the x-direction | ||||
|     public const int TileWidth = 56; | ||||
|     public const nuint TileWidth = NativeMethods.SP_TILE_WIDTH; | ||||
| 
 | ||||
|     /// tile count in the y-direction | ||||
|     public const int TileHeight = 20; | ||||
|     public const nuint TileHeight = NativeMethods.SP_TILE_SIZE; | ||||
| 
 | ||||
|     /// screen width in pixels | ||||
|     public const int PixelWidth = TileWidth * TileSize; | ||||
|     public const nuint PixelWidth = TileWidth * TileSize; | ||||
| 
 | ||||
|     /// screen height in pixels | ||||
|     public const int PixelHeight = TileHeight * TileSize; | ||||
|     public const nuint PixelHeight = TileHeight * TileSize; | ||||
| 
 | ||||
|     /// pixel count on whole screen | ||||
|     public const int PixelCount = PixelWidth * PixelHeight; | ||||
|     public const nuint PixelCount = PixelWidth * PixelHeight; | ||||
| } | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <PackageId>ServicePoint</PackageId> | ||||
|         <Version>0.9.1</Version> | ||||
|         <Version>0.10.0</Version> | ||||
|         <Authors>Repository Authors</Authors> | ||||
|         <Company>None</Company> | ||||
|         <Product>ServicePoint</Product> | ||||
|  |  | |||
|  | @ -8,8 +8,12 @@ fn main() { | |||
| 
 | ||||
|     let mut builder = csbindgen::Builder::default(); | ||||
| 
 | ||||
|     for source in fs::read_dir("../servicepoint_binding_c/src").unwrap() { | ||||
|         let path = source.unwrap().path(); | ||||
|     let mut paths = fs::read_dir("../servicepoint_binding_c/src").unwrap() | ||||
|         .map(|x| x.unwrap().path()) | ||||
|         .collect::<Vec<_>>(); | ||||
|     paths.sort(); | ||||
| 
 | ||||
|     for path in paths { | ||||
|         println!("cargo:rerun-if-changed={}", path.display()); | ||||
|         builder = builder.input_extern_file(path); | ||||
|     } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ using var connection = Connection.Open("127.0.0.1:2342"); | |||
| connection.Send(Command.Clear().IntoPacket()); | ||||
| connection.Send(Command.Brightness(128).IntoPacket()); | ||||
| 
 | ||||
| using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight); | ||||
| using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight); | ||||
| 
 | ||||
| for (var offset = 0; offset < int.MaxValue; offset++) | ||||
| { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter