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]] | [[package]] | ||||||
| name = "cc" | name = "cc" | ||||||
| version = "1.1.29" | version = "1.1.30" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1" | checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "jobserver", |  "jobserver", | ||||||
|  "libc", |  "libc", | ||||||
|  | @ -610,7 +610,7 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "servicepoint" | name = "servicepoint" | ||||||
| version = "0.9.1" | version = "0.10.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitvec", |  "bitvec", | ||||||
|  "bzip2", |  "bzip2", | ||||||
|  | @ -626,7 +626,7 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "servicepoint_binding_c" | name = "servicepoint_binding_c" | ||||||
| version = "0.9.1" | version = "0.10.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cbindgen", |  "cbindgen", | ||||||
|  "servicepoint", |  "servicepoint", | ||||||
|  | @ -634,7 +634,7 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "servicepoint_binding_cs" | name = "servicepoint_binding_cs" | ||||||
| version = "0.9.1" | version = "0.10.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "csbindgen", |  "csbindgen", | ||||||
|  "servicepoint", |  "servicepoint", | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ members = [ | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [workspace.package] | [workspace.package] | ||||||
| version = "0.9.1" | version = "0.10.0" | ||||||
| 
 | 
 | ||||||
| [workspace.lints.rust] | [workspace.lints.rust] | ||||||
| missing-docs = "warn" | missing-docs = "warn" | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ cargo add servicepoint | ||||||
| or | or | ||||||
| ```toml | ```toml | ||||||
| [dependencies] | [dependencies] | ||||||
| servicepoint = "0.9.1" | servicepoint = "0.10.0" | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Examples | ## Examples | ||||||
|  |  | ||||||
|  | @ -6,16 +6,26 @@ use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin}; | ||||||
| 
 | 
 | ||||||
| #[derive(Parser, Debug)] | #[derive(Parser, Debug)] | ||||||
| struct Cli { | struct Cli { | ||||||
|     #[arg(short, long, default_value = "localhost:2342")] |     #[arg(
 | ||||||
|  |         short, | ||||||
|  |         long, | ||||||
|  |         default_value = "localhost:2342", | ||||||
|  |         help = "Address of the display" | ||||||
|  |     )] | ||||||
|     destination: String, |     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>, |     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, |     clear: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// example: `cargo run -- --text "Hallo,
 | /// example: `cargo run -- --text "Hallo" --text "CCCB"`
 | ||||||
| /// CCCB"`
 |  | ||||||
| fn main() { | fn main() { | ||||||
|     let mut cli = Cli::parse(); |     let mut cli = Cli::parse(); | ||||||
|     if cli.text.is_empty() { |     if cli.text.is_empty() { | ||||||
|  | @ -31,15 +41,11 @@ fn main() { | ||||||
|             .expect("sending clear failed"); |             .expect("sending clear failed"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let text = cli.text.iter().fold(String::new(), move |str, line| { |     let text = cli.text.join("\n"); | ||||||
|         let is_first = str.is_empty(); |  | ||||||
|         str + if is_first { "" } else { "\n" } + line |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     let grid = CharGrid::from(&*text); |     let grid = CharGrid::from(&*text); | ||||||
|     let cp437_grid = Cp437Grid::from(&grid); |     let cp437_grid = Cp437Grid::from(&grid); | ||||||
| 
 | 
 | ||||||
|     connection |     connection | ||||||
|         .send(Command::Cp437Data(Origin::new(0, 0), cp437_grid)) |         .send(Command::Cp437Data(Origin::ZERO, cp437_grid)) | ||||||
|         .expect("sending text failed"); |         .expect("sending text failed"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -15,23 +15,24 @@ fn main() { | ||||||
|     let connection = Connection::open(cli.destination) |     let connection = Connection::open(cli.destination) | ||||||
|         .expect("could not connect to display"); |         .expect("could not connect to display"); | ||||||
| 
 | 
 | ||||||
|     let mut pixels = PixelGrid::max_sized(); |     let mut pixels = Bitmap::max_sized(); | ||||||
|     pixels.fill(true); |     pixels.fill(true); | ||||||
| 
 | 
 | ||||||
|     let command = Command::BitmapLinearWin( |     let command = Command::BitmapLinearWin( | ||||||
|         Origin::new(0, 0), |         Origin::ZERO, | ||||||
|         pixels, |         pixels, | ||||||
|         CompressionCode::Uncompressed, |         CompressionCode::Uncompressed, | ||||||
|     ); |     ); | ||||||
|     connection.send(command).expect("send failed"); |     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); |     let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT); | ||||||
|     for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { |     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 |     connection | ||||||
|         .send(Command::CharBrightness(Origin::new(0, 0), brightnesses)) |         .send(Command::CharBrightness(Origin::ZERO, brightnesses)) | ||||||
|         .expect("send failed"); |         .expect("send failed"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ fn main() { | ||||||
| 
 | 
 | ||||||
|     loop { |     loop { | ||||||
|         let command = Command::BitmapLinearWin( |         let command = Command::BitmapLinearWin( | ||||||
|             Origin::new(0, 0), |             Origin::ZERO, | ||||||
|             field.clone(), |             field.clone(), | ||||||
|             CompressionCode::Lzma, |             CompressionCode::Lzma, | ||||||
|         ); |         ); | ||||||
|  | @ -34,7 +34,7 @@ fn main() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn iteration(field: PixelGrid) -> PixelGrid { | fn iteration(field: Bitmap) -> Bitmap { | ||||||
|     let mut next = field.clone(); |     let mut next = field.clone(); | ||||||
|     for x in 0..field.width() { |     for x in 0..field.width() { | ||||||
|         for y in 0..field.height() { |         for y in 0..field.height() { | ||||||
|  | @ -51,7 +51,7 @@ fn iteration(field: PixelGrid) -> PixelGrid { | ||||||
|     next |     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; |     let mut count = 0; | ||||||
|     for nx in x - 1..=x + 1 { |     for nx in x - 1..=x + 1 { | ||||||
|         for ny in y - 1..=y + 1 { |         for ny in y - 1..=y + 1 { | ||||||
|  | @ -78,8 +78,8 @@ fn count_neighbors(field: &PixelGrid, x: i32, y: i32) -> i32 { | ||||||
|     count |     count | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn make_random_field(probability: f64) -> PixelGrid { | fn make_random_field(probability: f64) -> Bitmap { | ||||||
|     let mut field = PixelGrid::max_sized(); |     let mut field = Bitmap::max_sized(); | ||||||
|     let mut rng = rand::thread_rng(); |     let mut rng = rand::thread_rng(); | ||||||
|     let d = distributions::Bernoulli::new(probability).unwrap(); |     let d = distributions::Bernoulli::new(probability).unwrap(); | ||||||
|     for x in 0..field.width() { |     for x in 0..field.width() { | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ fn main() { | ||||||
|     let connection = Connection::open(Cli::parse().destination) |     let connection = Connection::open(Cli::parse().destination) | ||||||
|         .expect("could not connect to display"); |         .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 { |     for x_offset in 0..usize::MAX { | ||||||
|         pixels.fill(false); |         pixels.fill(false); | ||||||
| 
 | 
 | ||||||
|  | @ -25,7 +25,7 @@ fn main() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let command = Command::BitmapLinearWin( |         let command = Command::BitmapLinearWin( | ||||||
|             Origin::new(0, 0), |             Origin::ZERO, | ||||||
|             pixels.clone(), |             pixels.clone(), | ||||||
|             CompressionCode::Lzma, |             CompressionCode::Lzma, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -28,11 +28,11 @@ fn main() { | ||||||
| 
 | 
 | ||||||
|     // put all pixels in on state
 |     // put all pixels in on state
 | ||||||
|     if cli.enable_all { |     if cli.enable_all { | ||||||
|         let mut filled_grid = PixelGrid::max_sized(); |         let mut filled_grid = Bitmap::max_sized(); | ||||||
|         filled_grid.fill(true); |         filled_grid.fill(true); | ||||||
| 
 | 
 | ||||||
|         let command = BitmapLinearWin( |         let command = BitmapLinearWin( | ||||||
|             Origin::new(0, 0), |             Origin::ZERO, | ||||||
|             filled_grid, |             filled_grid, | ||||||
|             CompressionCode::Lzma, |             CompressionCode::Lzma, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| //! Example for how to use the WebSocket connection
 | //! Example for how to use the WebSocket connection
 | ||||||
| 
 | 
 | ||||||
| use servicepoint::{ | use servicepoint::{ | ||||||
|     Command, CompressionCode, Connection, Grid, Origin, PixelGrid, |     Bitmap, Command, CompressionCode, Connection, Grid, Origin, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|  | @ -13,7 +13,7 @@ fn main() { | ||||||
|     // use send_mut instead of send
 |     // use send_mut instead of send
 | ||||||
|     connection.send_mut(Command::Clear).unwrap(); |     connection.send_mut(Command::Clear).unwrap(); | ||||||
| 
 | 
 | ||||||
|     let mut pixels = PixelGrid::max_sized(); |     let mut pixels = Bitmap::max_sized(); | ||||||
|     pixels.fill(true); |     pixels.fill(true); | ||||||
| 
 | 
 | ||||||
|     // use send_mut instead of send
 |     // use send_mut instead of send
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ fn main() { | ||||||
|     let connection = Connection::open(cli.destination) |     let connection = Connection::open(cli.destination) | ||||||
|         .expect("could not connect to display"); |         .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); |     enabled_pixels.fill(true); | ||||||
| 
 | 
 | ||||||
|     for x_offset in 0..PIXEL_WIDTH { |     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.
 | /// A grid of pixels stored in packed bytes.
 | ||||||
| #[derive(Debug, Clone, PartialEq)] | #[derive(Debug, Clone, PartialEq)] | ||||||
| pub struct PixelGrid { | pub struct Bitmap { | ||||||
|     width: usize, |     width: usize, | ||||||
|     height: usize, |     height: usize, | ||||||
|     bit_vec: SpBitVec, |     bit_vec: SpBitVec, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PixelGrid { | impl Bitmap { | ||||||
|     /// Creates a new [PixelGrid] with the specified dimensions.
 |     /// Creates a new [Bitmap] with the specified dimensions.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Arguments
 |     /// # Arguments
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// - `width`: size in pixels in x-direction
 |     /// - `width`: size in pixels in x-direction
 | ||||||
|     /// - `height`: size in pixels in y-direction
 |     /// - `height`: size in pixels in y-direction
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// returns: [PixelGrid] initialized to all pixels off
 |     /// returns: [Bitmap] initialized to all pixels off
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Panics
 |     /// # Panics
 | ||||||
|     ///
 |     ///
 | ||||||
|  | @ -40,14 +40,14 @@ impl PixelGrid { | ||||||
|         Self::new(PIXEL_WIDTH, PIXEL_HEIGHT) |         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
 |     /// # Arguments
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// - `width`: size in pixels in x-direction
 |     /// - `width`: size in pixels in x-direction
 | ||||||
|     /// - `height`: size in pixels in y-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
 |     /// # 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:
 |     /// Order is equivalent to the following loop:
 | ||||||
|     /// ```
 |     /// ```
 | ||||||
|     /// # use servicepoint::{PixelGrid, Grid};
 |     /// # use servicepoint::{Bitmap, Grid};
 | ||||||
|     /// # let grid = PixelGrid::new(8,2);
 |     /// # let grid = Bitmap::new(8,2);
 | ||||||
|     /// for y in 0..grid.height() {
 |     /// for y in 0..grid.height() {
 | ||||||
|     ///     for x in 0..grid.width() {
 |     ///     for x in 0..grid.width() {
 | ||||||
|     ///         grid.get(x, y);
 |     ///         grid.get(x, y);
 | ||||||
|  | @ -80,12 +80,12 @@ impl PixelGrid { | ||||||
|         self.bit_vec.iter().by_refs() |         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:
 |     /// Order is equivalent to the following loop:
 | ||||||
|     /// ```
 |     /// ```
 | ||||||
|     /// # use servicepoint::{PixelGrid, Grid};
 |     /// # use servicepoint::{Bitmap, Grid};
 | ||||||
|     /// # let mut grid = PixelGrid::new(8,2);
 |     /// # let mut grid = Bitmap::new(8,2);
 | ||||||
|     /// # let value = false;
 |     /// # let value = false;
 | ||||||
|     /// for y in 0..grid.height() {
 |     /// for y in 0..grid.height() {
 | ||||||
|     ///     for x in 0..grid.width() {
 |     ///     for x in 0..grid.width() {
 | ||||||
|  | @ -96,8 +96,8 @@ impl PixelGrid { | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Example
 |     /// # Example
 | ||||||
|     /// ```
 |     /// ```
 | ||||||
|     /// # use servicepoint::{PixelGrid, Grid};
 |     /// # use servicepoint::{Bitmap, Grid};
 | ||||||
|     /// # let mut grid = PixelGrid::new(8,2);
 |     /// # let mut grid = Bitmap::new(8,2);
 | ||||||
|     /// # let value = false;
 |     /// # let value = false;
 | ||||||
|     /// for (index, mut pixel) in grid.iter_mut().enumerate() {
 |     /// for (index, mut pixel) in grid.iter_mut().enumerate() {
 | ||||||
|     ///     pixel.set(index % 2 == 0)
 |     ///     pixel.set(index % 2 == 0)
 | ||||||
|  | @ -107,17 +107,17 @@ impl PixelGrid { | ||||||
|         self.bit_vec.iter_mut() |         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 { |     pub fn iter_rows(&self) -> IterRows { | ||||||
|         IterRows { |         IterRows { | ||||||
|             pixel_grid: self, |             bitmap: self, | ||||||
|             row: 0, |             row: 0, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Grid<bool> for PixelGrid { | impl Grid<bool> for Bitmap { | ||||||
|     /// Sets the value of the specified position in the [PixelGrid].
 |     /// Sets the value of the specified position in the [Bitmap].
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Arguments
 |     /// # Arguments
 | ||||||
|     ///
 |     ///
 | ||||||
|  | @ -139,7 +139,7 @@ impl Grid<bool> for PixelGrid { | ||||||
|         self.bit_vec[x + y * self.width] |         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
 |     /// # 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] { |     fn data_ref_mut(&mut self) -> &mut [u8] { | ||||||
|         self.bit_vec.as_raw_mut_slice() |         self.bit_vec.as_raw_mut_slice() | ||||||
|     } |     } | ||||||
|  | @ -168,15 +168,15 @@ impl DataRef<u8> for PixelGrid { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<PixelGrid> for Vec<u8> { | impl From<Bitmap> for Vec<u8> { | ||||||
|     /// Turns a [PixelGrid] into the underlying [`Vec<u8>`].
 |     /// Turns a [Bitmap] into the underlying [`Vec<u8>`].
 | ||||||
|     fn from(value: PixelGrid) -> Self { |     fn from(value: Bitmap) -> Self { | ||||||
|         value.bit_vec.into() |         value.bit_vec.into() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct IterRows<'t> { | pub struct IterRows<'t> { | ||||||
|     pixel_grid: &'t PixelGrid, |     bitmap: &'t Bitmap, | ||||||
|     row: usize, |     row: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -184,24 +184,24 @@ impl<'t> Iterator for IterRows<'t> { | ||||||
|     type Item = &'t BitSlice<u8, Msb0>; |     type Item = &'t BitSlice<u8, Msb0>; | ||||||
| 
 | 
 | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|         if self.row >= self.pixel_grid.height { |         if self.row >= self.bitmap.height { | ||||||
|             return None; |             return None; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let start = self.row * self.pixel_grid.width; |         let start = self.row * self.bitmap.width; | ||||||
|         let end = start + self.pixel_grid.width; |         let end = start + self.bitmap.width; | ||||||
|         self.row += 1; |         self.row += 1; | ||||||
|         Some(&self.pixel_grid.bit_vec[start..end]) |         Some(&self.bitmap.bit_vec[start..end]) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use crate::{DataRef, Grid, PixelGrid}; |     use crate::{Bitmap, DataRef, Grid}; | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn fill() { |     fn fill() { | ||||||
|         let mut grid = PixelGrid::new(8, 2); |         let mut grid = Bitmap::new(8, 2); | ||||||
|         assert_eq!(grid.data_ref(), [0x00, 0x00]); |         assert_eq!(grid.data_ref(), [0x00, 0x00]); | ||||||
| 
 | 
 | ||||||
|         grid.fill(true); |         grid.fill(true); | ||||||
|  | @ -213,7 +213,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn get_set() { |     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(0, 0)); | ||||||
|         assert!(!grid.get(1, 1)); |         assert!(!grid.get(1, 1)); | ||||||
| 
 | 
 | ||||||
|  | @ -228,7 +228,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn load() { |     fn load() { | ||||||
|         let mut grid = PixelGrid::new(8, 3); |         let mut grid = Bitmap::new(8, 3); | ||||||
|         for x in 0..grid.width { |         for x in 0..grid.width { | ||||||
|             for y in 0..grid.height { |             for y in 0..grid.height { | ||||||
|                 grid.set(x, y, (x + y) % 2 == 0); |                 grid.set(x, y, (x + y) % 2 == 0); | ||||||
|  | @ -239,33 +239,33 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|         let data: Vec<u8> = grid.into(); |         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]); |         assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     #[should_panic] |     #[should_panic] | ||||||
|     fn out_of_bounds_x() { |     fn out_of_bounds_x() { | ||||||
|         let vec = PixelGrid::new(8, 2); |         let vec = Bitmap::new(8, 2); | ||||||
|         vec.get(8, 1); |         vec.get(8, 1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     #[should_panic] |     #[should_panic] | ||||||
|     fn out_of_bounds_y() { |     fn out_of_bounds_y() { | ||||||
|         let mut vec = PixelGrid::new(8, 2); |         let mut vec = Bitmap::new(8, 2); | ||||||
|         vec.set(1, 2, false); |         vec.set(1, 2, false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn iter() { |     fn iter() { | ||||||
|         let grid = PixelGrid::new(8, 2); |         let grid = Bitmap::new(8, 2); | ||||||
|         assert_eq!(16, grid.iter().count()) |         assert_eq!(16, grid.iter().count()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn iter_rows() { |     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(); |         let mut iter = grid.iter_rows(); | ||||||
| 
 | 
 | ||||||
|         assert_eq!(iter.next().unwrap().count_ones(), 1); |         assert_eq!(iter.next().unwrap().count_ones(), 1); | ||||||
|  | @ -275,7 +275,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn iter_mut() { |     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() { |         for (index, mut pixel) in grid.iter_mut().enumerate() { | ||||||
|             pixel.set(index % 2 == 0); |             pixel.set(index % 2 == 0); | ||||||
|         } |         } | ||||||
|  | @ -284,7 +284,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn data_ref_mut() { |     fn data_ref_mut() { | ||||||
|         let mut grid = PixelGrid::new(8, 2); |         let mut grid = Bitmap::new(8, 2); | ||||||
|         let data = grid.data_ref_mut(); |         let data = grid.data_ref_mut(); | ||||||
|         data[1] = 0x0F; |         data[1] = 0x0F; | ||||||
|         assert!(grid.get(7, 1)); |         assert!(grid.get(7, 1)); | ||||||
|  | @ -60,6 +60,17 @@ impl Brightness { | ||||||
|     pub const MAX: Brightness = Brightness(11); |     pub const MAX: Brightness = Brightness(11); | ||||||
|     /// lowest possible brightness value, 0
 |     /// lowest possible brightness value, 0
 | ||||||
|     pub const MIN: Brightness = Brightness(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 { | impl Default for Brightness { | ||||||
|  | @ -138,4 +149,10 @@ mod tests { | ||||||
|         let actual = PrimitiveGrid::from(&grid); |         let actual = PrimitiveGrid::from(&grid); | ||||||
|         assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); |         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, |     command_code::CommandCode, | ||||||
|     compression::into_decompressed, |     compression::into_decompressed, | ||||||
|     packet::{Header, Packet}, |     packet::{Header, Packet}, | ||||||
|     Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, PixelGrid, |     Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, | ||||||
|     Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, |     Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -76,12 +76,7 @@ pub enum Command { | ||||||
| 
 | 
 | ||||||
|     /// Show text on the screen.
 |     /// Show text on the screen.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// The text is sent in the form of a 2D grid of characters.
 |     /// The text is sent in the form of a 2D grid of [CP-437] encoded 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>
 |  | ||||||
|     ///
 |     ///
 | ||||||
|     /// # Examples
 |     /// # Examples
 | ||||||
|     ///
 |     ///
 | ||||||
|  | @ -100,6 +95,7 @@ pub enum Command { | ||||||
|     /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
 |     /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
 | ||||||
|     /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).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), |     Cp437Data(Origin<Tiles>, Cp437Grid), | ||||||
| 
 | 
 | ||||||
|     /// Overwrites a rectangular region of pixels.
 |     /// Overwrites a rectangular region of pixels.
 | ||||||
|  | @ -109,23 +105,23 @@ pub enum Command { | ||||||
|     /// # Examples
 |     /// # Examples
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// ```rust
 |     /// ```rust
 | ||||||
|     /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 |     /// # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||||
|     /// # let connection = servicepoint::Connection::Fake;
 |     /// # let connection = servicepoint::Connection::Fake;
 | ||||||
|     /// #
 |     /// #
 | ||||||
|     /// let mut pixels = PixelGrid::max_sized();
 |     /// let mut pixels = Bitmap::max_sized();
 | ||||||
|     /// // draw something to the pixels here
 |     /// // draw something to the pixels here
 | ||||||
|     /// # pixels.set(2, 5, true);
 |     /// # pixels.set(2, 5, true);
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// // create command to send pixels
 |     /// // create command to send pixels
 | ||||||
|     /// let command = Command::BitmapLinearWin(
 |     /// let command = Command::BitmapLinearWin(
 | ||||||
|     ///    servicepoint::Origin::new(0, 0),
 |     ///    servicepoint::Origin::ZERO,
 | ||||||
|     ///    pixels,
 |     ///    pixels,
 | ||||||
|     ///    CompressionCode::Uncompressed
 |     ///    CompressionCode::Uncompressed
 | ||||||
|     /// );
 |     /// );
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// connection.send(command).expect("send failed");
 |     /// 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.
 |     /// Set the brightness of all tiles to the same value.
 | ||||||
|     ///
 |     ///
 | ||||||
|  | @ -217,9 +213,8 @@ pub enum Command { | ||||||
|     BitmapLegacy, |     BitmapLegacy, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] |  | ||||||
| /// Err values for [Command::try_from].
 | /// Err values for [Command::try_from].
 | ||||||
| #[derive(PartialEq)] | #[derive(Debug, PartialEq)] | ||||||
| pub enum TryFromPacketError { | pub enum TryFromPacketError { | ||||||
|     /// the contained command code does not correspond to a known command
 |     /// the contained command code does not correspond to a known command
 | ||||||
|     InvalidCommand(u16), |     InvalidCommand(u16), | ||||||
|  | @ -342,7 +337,7 @@ impl Command { | ||||||
| 
 | 
 | ||||||
|         Ok(Command::BitmapLinearWin( |         Ok(Command::BitmapLinearWin( | ||||||
|             Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), |             Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), | ||||||
|             PixelGrid::load( |             Bitmap::load( | ||||||
|                 tile_w as usize * TILE_SIZE, |                 tile_w as usize * TILE_SIZE, | ||||||
|                 pixel_h as usize, |                 pixel_h as usize, | ||||||
|                 &payload, |                 &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( |     fn packet_into_linear_bitmap( | ||||||
|         packet: Packet, |         packet: Packet, | ||||||
|     ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { |     ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { | ||||||
|  | @ -500,7 +495,8 @@ mod tests { | ||||||
|         command_code::CommandCode, |         command_code::CommandCode, | ||||||
|         origin::Pixels, |         origin::Pixels, | ||||||
|         packet::{Header, Packet}, |         packet::{Header, Packet}, | ||||||
|         Brightness, Command, CompressionCode, Origin, PixelGrid, PrimitiveGrid, |         Bitmap, Brightness, BrightnessGrid, Command, CompressionCode, Origin, | ||||||
|  |         PrimitiveGrid, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fn round_trip(original: Command) { |     fn round_trip(original: Command) { | ||||||
|  | @ -592,8 +588,8 @@ mod tests { | ||||||
|                 compression, |                 compression, | ||||||
|             )); |             )); | ||||||
|             round_trip(Command::BitmapLinearWin( |             round_trip(Command::BitmapLinearWin( | ||||||
|                 Origin::new(0, 0), |                 Origin::ZERO, | ||||||
|                 PixelGrid::max_sized(), |                 Bitmap::max_sized(), | ||||||
|                 compression, |                 compression, | ||||||
|             )); |             )); | ||||||
|         } |         } | ||||||
|  | @ -718,7 +714,7 @@ mod tests { | ||||||
|         for compression in all_compressions().to_owned() { |         for compression in all_compressions().to_owned() { | ||||||
|             let p: Packet = Command::BitmapLinearWin( |             let p: Packet = Command::BitmapLinearWin( | ||||||
|                 Origin::new(16, 8), |                 Origin::new(16, 8), | ||||||
|                 PixelGrid::new(8, 8), |                 Bitmap::new(8, 8), | ||||||
|                 compression, |                 compression, | ||||||
|             ) |             ) | ||||||
|             .into(); |             .into(); | ||||||
|  | @ -907,4 +903,28 @@ mod tests { | ||||||
|             Origin::new(1, 0) + Origin::new(3, 2) |             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
 | /// # Examples
 | ||||||
| ///
 | ///
 | ||||||
| /// ```rust
 | /// ```rust
 | ||||||
| /// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid};
 | /// # use servicepoint::{Command, CompressionCode, Origin, Bitmap};
 | ||||||
| /// // create command without payload compression
 | /// // create command without payload compression
 | ||||||
| /// # let pixels = PixelGrid::max_sized();
 | /// # let pixels = Bitmap::max_sized();
 | ||||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed);
 | /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Uncompressed);
 | ||||||
| ///
 | ///
 | ||||||
| /// // create command with payload compressed with lzma and appropriate header flags
 | /// // create command with payload compressed with lzma and appropriate header flags
 | ||||||
| /// # let pixels = PixelGrid::max_sized();
 | /// # let pixels = Bitmap::max_sized();
 | ||||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Lzma);
 | /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Lzma);
 | ||||||
| /// ```
 | /// ```
 | ||||||
| #[repr(u16)] | #[repr(u16)] | ||||||
| #[derive(Debug, Clone, Copy, PartialEq)] | #[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 crate::{Grid, PrimitiveGrid}; | ||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| 
 | 
 | ||||||
|  | @ -10,9 +13,16 @@ pub type Cp437Grid = PrimitiveGrid<u8>; | ||||||
| /// A grid containing UTF-8 characters.
 | /// A grid containing UTF-8 characters.
 | ||||||
| pub type CharGrid = PrimitiveGrid<char>; | pub type CharGrid = PrimitiveGrid<char>; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | /// Errors that can occur when loading CP-437.
 | ||||||
|  | #[derive(Debug, PartialEq)] | ||||||
| pub enum Cp437LoadError { | 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 { | impl Cp437Grid { | ||||||
|  | @ -36,7 +46,7 @@ impl Cp437Grid { | ||||||
| 
 | 
 | ||||||
|             for (index, char) in value.chars().enumerate() { |             for (index, char) in value.chars().enumerate() { | ||||||
|                 if !char.is_ascii() { |                 if !char.is_ascii() { | ||||||
|                     return Err(InvalidChar { index, char }); |                     return Err(Cp437LoadError::InvalidChar { index, char }); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 let is_lf = char == '\n'; |                 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
 |     /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// Mostly follows CP437, except for:
 |     /// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
 | ||||||
|     ///  * 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.
 |  | ||||||
|     ///
 |     ///
 | ||||||
|     /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
 |     /// 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] |     #[rustfmt::skip] | ||||||
|     const CP437_TO_UTF8: [char; 256] = [ |     pub const CP437_TO_UTF8: [char; 256] = [ | ||||||
|         /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '\r', '♫', '☼', |         /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', | ||||||
|         /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '', '', '∟', '↔',  '▲', '▼', |         /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔',  '▲', '▼', | ||||||
|         /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', |         /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', | ||||||
|         /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', |         /* 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', |         /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||||||
|  | @ -116,7 +120,7 @@ mod feature_cp437 { | ||||||
|         /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', |         /* 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(|| { |         once_cell::sync::Lazy::new(|| { | ||||||
|             let pairs = CP437_TO_UTF8 |             let pairs = CP437_TO_UTF8 | ||||||
|                 .iter() |                 .iter() | ||||||
|  | @ -125,48 +129,34 @@ mod feature_cp437 { | ||||||
|             HashMap::from_iter(pairs) |             HashMap::from_iter(pairs) | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     const MISSING_CHAR_CP437: u8 = 0x3F; |     const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
 | ||||||
| 
 | 
 | ||||||
|     impl From<&Cp437Grid> for CharGrid { |     impl From<&Cp437Grid> for CharGrid { | ||||||
|         fn from(value: &Cp437Grid) -> Self { |         fn from(value: &Cp437Grid) -> Self { | ||||||
|             let mut grid = Self::new(value.width(), value.height()); |             value.map(cp437_to_char) | ||||||
| 
 |  | ||||||
|             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 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     impl From<&CharGrid> for Cp437Grid { |     impl From<&CharGrid> for Cp437Grid { | ||||||
|         fn from(value: &CharGrid) -> Self { |         fn from(value: &CharGrid) -> Self { | ||||||
|             let mut grid = Self::new(value.width(), value.height()); |             value.map(char_to_cp437) | ||||||
| 
 |  | ||||||
|             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 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     impl From<&str> for CharGrid { |     impl From<&str> for CharGrid { | ||||||
|         fn from(value: &str) -> Self { |         fn from(value: &str) -> Self { | ||||||
|             let value = value.replace("\r\n", "\n"); |             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 = |             let width = | ||||||
|                 lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); |                 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()); |             let mut grid = Self::new(width, lines.len()); | ||||||
|             for (y, line) in lines.iter().enumerate() { |             for (y, line) in lines.iter().enumerate() { | ||||||
|                 for (x, char) in line.chars().enumerate() { |                 for (x, char) in line.chars().enumerate() { | ||||||
|  | @ -177,6 +167,44 @@ mod feature_cp437 { | ||||||
|             grid |             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)] | #[cfg(test)] | ||||||
|  | @ -204,12 +232,23 @@ mod tests { | ||||||
|         // line break will be added
 |         // line break will be added
 | ||||||
|         assert_eq!(actual, expected); |         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(test)] | ||||||
| #[cfg(feature = "cp437")] | #[cfg(feature = "cp437")] | ||||||
| mod tests_feature_cp437 { | mod tests_feature_cp437 { | ||||||
|     use crate::{CharGrid, Cp437Grid}; |     use super::*; | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn round_trip_cp437() { |     fn round_trip_cp437() { | ||||||
|  | @ -218,4 +257,48 @@ mod tests_feature_cp437 { | ||||||
|         let actual = CharGrid::from(&cp437); |         let actual = CharGrid::from(&cp437); | ||||||
|         assert_eq!(actual, utf8); |         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.
 |     /// When the specified position is out of bounds for this grid.
 | ||||||
|     fn assert_in_bounds(&self, x: usize, y: usize) { |     fn assert_in_bounds(&self, x: usize, y: usize) { | ||||||
|         assert!( |         let width = self.width(); | ||||||
|             x < self.width(), |         assert!(x < width, "cannot access index [{x}, {y}] because x is outside of bounds [0..{width})"); | ||||||
|             "cannot access index [{x}, {y}] because x is outside of bounds 0..{}", |         let height = self.height(); | ||||||
|             self.width() - 1 |         assert!(y < height, "cannot access index [{x}, {y}] because x is outside of bounds [0..{height})"); | ||||||
|         ); |  | ||||||
|         assert!( |  | ||||||
|             y < self.height(), |  | ||||||
|             "cannot access index [{x}, {y}] because y is outside of bounds 0..{}", |  | ||||||
|             self.height() - 1 |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| //! # Examples
 | //! # Examples
 | ||||||
| //!
 | //!
 | ||||||
| //! ```rust
 | //! ```rust
 | ||||||
| //! use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | //! use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||||
| //!
 | //!
 | ||||||
| //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
 | //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
 | ||||||
| //!     .expect("connection failed");
 | //!     .expect("connection failed");
 | ||||||
|  | @ -18,15 +18,15 @@ | ||||||
| //! ```
 | //! ```
 | ||||||
| //!
 | //!
 | ||||||
| //! ```rust
 | //! ```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");
 | //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
 | ||||||
| //!  // turn on all pixels in a grid
 | //!  // turn on all pixels in a grid
 | ||||||
| //!  let mut pixels = PixelGrid::max_sized();
 | //!  let mut pixels = Bitmap::max_sized();
 | ||||||
| //!  pixels.fill(true);
 | //!  pixels.fill(true);
 | ||||||
| //!
 | //!
 | ||||||
| //!  // create command to send pixels
 | //!  // create command to send pixels
 | ||||||
| //!  let command = Command::BitmapLinearWin(
 | //!  let command = Command::BitmapLinearWin(
 | ||||||
| //!     servicepoint::Origin::new(0, 0),
 | //!     servicepoint::Origin::ZERO,
 | ||||||
| //!     pixels,
 | //!     pixels,
 | ||||||
| //!     CompressionCode::Uncompressed
 | //!     CompressionCode::Uncompressed
 | ||||||
| //!  );
 | //!  );
 | ||||||
|  | @ -40,6 +40,7 @@ use std::time::Duration; | ||||||
| pub use bitvec; | pub use bitvec; | ||||||
| use bitvec::prelude::{BitVec, Msb0}; | use bitvec::prelude::{BitVec, Msb0}; | ||||||
| 
 | 
 | ||||||
|  | pub use crate::bitmap::Bitmap; | ||||||
| pub use crate::brightness::{Brightness, BrightnessGrid}; | pub use crate::brightness::{Brightness, BrightnessGrid}; | ||||||
| pub use crate::command::{Command, Offset}; | pub use crate::command::{Command, Offset}; | ||||||
| pub use crate::compression_code::CompressionCode; | 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::data_ref::DataRef; | ||||||
| pub use crate::grid::Grid; | pub use crate::grid::Grid; | ||||||
| pub use crate::origin::{Origin, Pixels, Tiles}; | pub use crate::origin::{Origin, Pixels, Tiles}; | ||||||
| pub use crate::pixel_grid::PixelGrid; |  | ||||||
| pub use crate::primitive_grid::PrimitiveGrid; | pub use crate::primitive_grid::PrimitiveGrid; | ||||||
| 
 | 
 | ||||||
| type SpBitVec = BitVec<u8, Msb0>; | type SpBitVec = BitVec<u8, Msb0>; | ||||||
| 
 | 
 | ||||||
|  | mod bitmap; | ||||||
| mod brightness; | mod brightness; | ||||||
| mod command; | mod command; | ||||||
| mod command_code; | mod command_code; | ||||||
| mod compression; | mod compression; | ||||||
| mod compression_code; | mod compression_code; | ||||||
| mod connection; | mod connection; | ||||||
| mod cp437; | pub mod cp437; | ||||||
| mod data_ref; | mod data_ref; | ||||||
| mod grid; | mod grid; | ||||||
| mod origin; | mod origin; | ||||||
| pub mod packet; | pub mod packet; | ||||||
| mod pixel_grid; |  | ||||||
| mod primitive_grid; | mod primitive_grid; | ||||||
| 
 | 
 | ||||||
| /// size of a single tile in one dimension
 | /// size of a single tile in one dimension
 | ||||||
|  | @ -95,8 +95,8 @@ pub const TILE_HEIGHT: usize = 20; | ||||||
| /// # Examples
 | /// # Examples
 | ||||||
| ///
 | ///
 | ||||||
| /// ```rust
 | /// ```rust
 | ||||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||||
| /// ```
 | /// ```
 | ||||||
| pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||||
| 
 | 
 | ||||||
|  | @ -105,8 +105,8 @@ pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||||
| /// # Examples
 | /// # Examples
 | ||||||
| ///
 | ///
 | ||||||
| /// ```rust
 | /// ```rust
 | ||||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||||
| /// ```
 | /// ```
 | ||||||
| pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | ||||||
| 
 | 
 | ||||||
|  | @ -119,10 +119,10 @@ pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; | ||||||
| ///
 | ///
 | ||||||
| /// ```rust
 | /// ```rust
 | ||||||
| /// # use std::time::Instant;
 | /// # 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")
 | /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||||
| /// #     .expect("connection failed");
 | /// #     .expect("connection failed");
 | ||||||
| /// # let pixels = PixelGrid::max_sized();
 | /// # let pixels = Bitmap::max_sized();
 | ||||||
| /// loop {
 | /// loop {
 | ||||||
| ///    let start = Instant::now();
 | ///    let start = Instant::now();
 | ||||||
| ///
 | ///
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ pub struct Origin<Unit: DisplayUnit> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Unit: DisplayUnit> Origin<Unit> { | impl<Unit: DisplayUnit> Origin<Unit> { | ||||||
|     /// Top-left. Equivalent to `Origin::new(0, 0)`.
 |     /// Top-left. Equivalent to `Origin::ZERO`.
 | ||||||
|     pub const ZERO: Self = Self { |     pub const ZERO: Self = Self { | ||||||
|         x: 0, |         x: 0, | ||||||
|         y: 0, |         y: 0, | ||||||
|  |  | ||||||
|  | @ -27,8 +27,8 @@ use std::mem::size_of; | ||||||
| 
 | 
 | ||||||
| use crate::compression::into_compressed; | use crate::compression::into_compressed; | ||||||
| use crate::{ | use crate::{ | ||||||
|     command_code::CommandCode, Command, CompressionCode, Grid, Offset, Origin, |     command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset, | ||||||
|     PixelGrid, Pixels, Tiles, TILE_SIZE, |     Origin, Pixels, Tiles, TILE_SIZE, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A raw header.
 | /// A raw header.
 | ||||||
|  | @ -214,7 +214,7 @@ impl From<Command> for Packet { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Packet { | impl Packet { | ||||||
|     /// Helper method for `BitMapLinear*`-Commands into [Packet]
 |     /// Helper method for `BitmapLinear*`-Commands into [Packet]
 | ||||||
|     #[allow(clippy::cast_possible_truncation)] |     #[allow(clippy::cast_possible_truncation)] | ||||||
|     fn bitmap_linear_into_packet( |     fn bitmap_linear_into_packet( | ||||||
|         command: CommandCode, |         command: CommandCode, | ||||||
|  | @ -239,7 +239,7 @@ impl Packet { | ||||||
|     #[allow(clippy::cast_possible_truncation)] |     #[allow(clippy::cast_possible_truncation)] | ||||||
|     fn bitmap_win_into_packet( |     fn bitmap_win_into_packet( | ||||||
|         origin: Origin<Pixels>, |         origin: Origin<Pixels>, | ||||||
|         pixels: PixelGrid, |         pixels: Bitmap, | ||||||
|         compression: CompressionCode, |         compression: CompressionCode, | ||||||
|     ) -> Packet { |     ) -> Packet { | ||||||
|         debug_assert_eq!(origin.x % 8, 0); |         debug_assert_eq!(origin.x % 8, 0); | ||||||
|  |  | ||||||
|  | @ -110,6 +110,34 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | ||||||
|             None |             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> { | impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] | ||||||
| cbindgen = "0.27.0" | cbindgen = "0.27.0" | ||||||
| 
 | 
 | ||||||
| [dependencies.servicepoint] | [dependencies.servicepoint] | ||||||
| version = "0.9.1" | version = "0.10.0" | ||||||
| path = "../servicepoint" | path = "../servicepoint" | ||||||
| features = ["all_compressions"] | features = ["all_compressions"] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,8 +21,8 @@ int main(void) { | ||||||
|     if (connection == NULL) |     if (connection == NULL) | ||||||
|         return 1; |         return 1; | ||||||
| 
 | 
 | ||||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); |     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||||
|     sp_pixel_grid_fill(pixels, true); |     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, Uncompressed); | ||||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); |     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ line_endings = "LF" | ||||||
| 
 | 
 | ||||||
| ############################# Codegen Options ################################## | ############################# Codegen Options ################################## | ||||||
| 
 | 
 | ||||||
| style = "both" | style = "type" | ||||||
| usize_is_size_t = true | usize_is_size_t = true | ||||||
| 
 | 
 | ||||||
| # this is needed because otherwise the order in the C# bindings is different on different machines | # this is needed because otherwise the order in the C# bindings is different on different machines | ||||||
|  | @ -31,3 +31,6 @@ all_features = true | ||||||
| [export] | [export] | ||||||
| include = [] | include = [] | ||||||
| exclude = [] | 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) |     if (connection == NULL) | ||||||
|         return 1; |         return 1; | ||||||
| 
 | 
 | ||||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); |     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||||
|     sp_pixel_grid_fill(pixels, true); |     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))); |     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||||
| 
 | 
 | ||||||
|     sp_command_free(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 crate::SPByteSlice; | ||||||
| use servicepoint::bitvec::prelude::{BitVec, Msb0}; | use servicepoint::bitvec::prelude::{BitVec, Msb0}; | ||||||
| 
 | 
 | ||||||
|  | @ -9,9 +10,9 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0}; | ||||||
| ///
 | ///
 | ||||||
| /// # Examples
 | /// # Examples
 | ||||||
| /// ```C
 | /// ```C
 | ||||||
| /// SPBitVec vec = sp_bit_vec_new(8);
 | /// SPBitVec vec = sp_bitvec_new(8);
 | ||||||
| /// sp_bit_vec_set(vec, 5, true);
 | /// sp_bitvec_set(vec, 5, true);
 | ||||||
| /// sp_bit_vec_free(vec);
 | /// sp_bitvec_free(vec);
 | ||||||
| /// ```
 | /// ```
 | ||||||
| pub struct SPBitVec(BitVec<u8, Msb0>); | 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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `size`: size in bits.
 | /// - `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
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When `size` is not divisible by 8.
 | /// - when `size` is not divisible by 8.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec { | pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> { | ||||||
|     Box::into_raw(Box::new(SPBitVec(BitVec::repeat(false, size)))) |     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
 | /// # 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`
 | /// - `data` points to a valid memory location of at least `data_length`
 | ||||||
| ///   bytes in size.
 | ///   bytes in size.
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_bit_vec_load( | pub unsafe extern "C" fn sp_bitvec_load( | ||||||
|     data: *const u8, |     data: *const u8, | ||||||
|     data_length: usize, |     data_length: usize, | ||||||
| ) -> *mut SPBitVec { | ) -> NonNull<SPBitVec> { | ||||||
|  |     assert!(!data.is_null()); | ||||||
|     let data = std::slice::from_raw_parts(data, data_length); |     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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not written to concurrently
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_bit_vec_clone( | pub unsafe extern "C" fn sp_bitvec_clone( | ||||||
|     bit_vec: *const SPBitVec, |     bit_vec: *const SPBitVec, | ||||||
| ) -> *mut SPBitVec { | ) -> NonNull<SPBitVec> { | ||||||
|     Box::into_raw(Box::new((*bit_vec).clone())) |     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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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` 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] | #[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); |     _ = Box::from_raw(bit_vec); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the value of a bit from the `SPBitVec`.
 | /// Gets the value of a bit from the [SPBitVec].
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -117,23 +140,25 @@ pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) { | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When accessing `index` out of bounds.
 | /// - when `bit_vec` is NULL
 | ||||||
|  | /// - when accessing `index` out of bounds
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not written to concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_bit_vec_get( | pub unsafe extern "C" fn sp_bitvec_get( | ||||||
|     bit_vec: *const SPBitVec, |     bit_vec: *const SPBitVec, | ||||||
|     index: usize, |     index: usize, | ||||||
| ) -> bool { | ) -> bool { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     *(*bit_vec).0.get(index).unwrap() |     *(*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -141,58 +166,68 @@ pub unsafe extern "C" fn sp_bit_vec_get( | ||||||
| /// - `index`: the bit index to edit
 | /// - `index`: the bit index to edit
 | ||||||
| /// - `value`: the value to set the bit to
 | /// - `value`: the value to set the bit to
 | ||||||
| ///
 | ///
 | ||||||
| /// returns: old value of the bit
 |  | ||||||
| ///
 |  | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When accessing `index` out of bounds.
 | /// - when `bit_vec` is NULL
 | ||||||
|  | /// - when accessing `index` out of bounds
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_bit_vec_set( | pub unsafe extern "C" fn sp_bitvec_set( | ||||||
|     bit_vec: *mut SPBitVec, |     bit_vec: *mut SPBitVec, | ||||||
|     index: usize, |     index: usize, | ||||||
|     value: bool, |     value: bool, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     (*bit_vec).0.set(index, value) |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec`: instance to write to
 | /// - `bit_vec`: instance to write to
 | ||||||
| /// - `value`: the value to set all bits to
 | /// - `value`: the value to set all bits to
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `bit_vec` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[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) |     (*bit_vec).0.fill(value) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the length of the `SPBitVec` in bits.
 | /// Gets the length of the [SPBitVec] in bits.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec`: instance to write to
 | /// - `bit_vec`: instance to write to
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `bit_vec` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | /// - `bit_vec` points to a valid [SPBitVec]
 | ||||||
| #[no_mangle] | #[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() |     (*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
 | /// - `bit_vec`: instance to write to
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `bit_vec` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | /// - `bit_vec` points to a valid [SPBitVec]
 | ||||||
| #[no_mangle] | #[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() |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec`: instance to write to
 | /// - `bit_vec`: instance to write to
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `bit_vec` is NULL
 | ||||||
|  | ///
 | ||||||
| /// ## Safety
 | /// ## Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | /// - `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 after the passed [SPBitVec] has been freed
 | ||||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBitVec` or directly
 | /// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly
 | ||||||
| #[no_mangle] | #[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, |     bit_vec: *mut SPBitVec, | ||||||
| ) -> SPByteSlice { | ) -> SPByteSlice { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     let data = (*bit_vec).0.as_raw_mut_slice(); |     let data = (*bit_vec).0.as_raw_mut_slice(); | ||||||
|     SPByteSlice { |     SPByteSlice { | ||||||
|         start: data.as_mut_ptr_range().start, |         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||||
|         length: data.len(), |         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_`
 | //! prefix `sp_brightness_grid_`
 | ||||||
| 
 | 
 | ||||||
| use crate::SPByteSlice; | use crate::SPByteSlice; | ||||||
| use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; | use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; | ||||||
|  | use std::convert::Into; | ||||||
| use std::intrinsics::transmute; | 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.
 | /// A grid containing brightness values.
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -21,17 +30,12 @@ use std::intrinsics::transmute; | ||||||
| /// SPCommand command = sp_command_char_brightness(grid);
 | /// SPCommand command = sp_command_char_brightness(grid);
 | ||||||
| /// sp_connection_free(connection);
 | /// sp_connection_free(connection);
 | ||||||
| /// ```
 | /// ```
 | ||||||
|  | #[derive(Clone)] | ||||||
| pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); | pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); | ||||||
| 
 | 
 | ||||||
| impl Clone for SPBrightnessGrid { | /// Creates a new [SPBrightnessGrid] with the specified dimensions.
 | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         SPBrightnessGrid(self.0.clone()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -43,17 +47,21 @@ impl Clone for SPBrightnessGrid { | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_new( | pub unsafe extern "C" fn sp_brightness_grid_new( | ||||||
|     width: usize, |     width: usize, | ||||||
|     height: usize, |     height: usize, | ||||||
| ) -> *mut SPBrightnessGrid { | ) -> NonNull<SPBrightnessGrid> { | ||||||
|     Box::into_raw(Box::new(SPBrightnessGrid( |     let result = Box::new(SPBrightnessGrid( | ||||||
|         servicepoint::BrightnessGrid::new(width, height), |         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
 | /// # 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -69,52 +77,67 @@ pub unsafe extern "C" fn sp_brightness_grid_load( | ||||||
|     height: usize, |     height: usize, | ||||||
|     data: *const u8, |     data: *const u8, | ||||||
|     data_length: usize, |     data_length: usize, | ||||||
| ) -> *mut SPBrightnessGrid { | ) -> NonNull<SPBrightnessGrid> { | ||||||
|  |     assert!(!data.is_null()); | ||||||
|     let data = std::slice::from_raw_parts(data, data_length); |     let data = std::slice::from_raw_parts(data, data_length); | ||||||
|     let grid = PrimitiveGrid::load(width, height, data); |     let grid = PrimitiveGrid::load(width, height, data); | ||||||
|     let grid = servicepoint::BrightnessGrid::try_from(grid) |     let grid = servicepoint::BrightnessGrid::try_from(grid) | ||||||
|         .expect("invalid brightness value"); |         .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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `brightness_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// returns: new [SPBrightnessGrid] instance. Will never return NULL.
 | ||||||
|  | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `brightness_grid` is not written to concurrently
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_clone( | pub unsafe extern "C" fn sp_brightness_grid_clone( | ||||||
|     brightness_grid: *const SPBrightnessGrid, |     brightness_grid: *const SPBrightnessGrid, | ||||||
| ) -> *mut SPBrightnessGrid { | ) -> NonNull<SPBrightnessGrid> { | ||||||
|     Box::into_raw(Box::new((*brightness_grid).clone())) |     assert!(!brightness_grid.is_null()); | ||||||
|  |     let result = Box::new((*brightness_grid).clone()); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Deallocates a `SPBrightnessGrid`.
 | /// Deallocates a [SPBrightnessGrid].
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `brightness_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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` 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_free( | pub unsafe extern "C" fn sp_brightness_grid_free( | ||||||
|     brightness_grid: *mut SPBrightnessGrid, |     brightness_grid: *mut SPBrightnessGrid, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     _ = Box::from_raw(brightness_grid); |     _ = Box::from_raw(brightness_grid); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -125,15 +148,18 @@ pub unsafe extern "C" fn sp_brightness_grid_free( | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `brightness_grid`: instance to read from
 | ||||||
| /// - `x` and `y`: position of the cell to read
 | /// - `x` and `y`: position of the cell to read
 | ||||||
| ///
 | ///
 | ||||||
|  | /// returns: value at position
 | ||||||
|  | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When accessing `x` or `y` out of bounds.
 | /// - when `brightness_grid` is NULL
 | ||||||
|  | /// - When accessing `x` or `y` out of bounds.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `brightness_grid` is not written to concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_get( | pub unsafe extern "C" fn sp_brightness_grid_get( | ||||||
|  | @ -141,10 +167,11 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | ||||||
|     x: usize, |     x: usize, | ||||||
|     y: usize, |     y: usize, | ||||||
| ) -> u8 { | ) -> u8 { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     (*brightness_grid).0.get(x, y).into() |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -156,6 +183,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get( | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
| /// - When accessing `x` or `y` out of bounds.
 | /// - When accessing `x` or `y` out of bounds.
 | ||||||
| /// - When providing an invalid brightness value
 | /// - 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:
 | /// 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
 | /// - `brightness_grid` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_set( | pub unsafe extern "C" fn sp_brightness_grid_set( | ||||||
|  | @ -172,12 +200,13 @@ pub unsafe extern "C" fn sp_brightness_grid_set( | ||||||
|     y: usize, |     y: usize, | ||||||
|     value: u8, |     value: u8, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     let brightness = |     let brightness = | ||||||
|         Brightness::try_from(value).expect("invalid brightness value"); |         Brightness::try_from(value).expect("invalid brightness value"); | ||||||
|     (*brightness_grid).0.set(x, y, brightness); |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -186,83 +215,106 @@ pub unsafe extern "C" fn sp_brightness_grid_set( | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
| /// - When providing an invalid brightness value
 | /// - When providing an invalid brightness value
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `brightness_grid` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_fill( | pub unsafe extern "C" fn sp_brightness_grid_fill( | ||||||
|     brightness_grid: *mut SPBrightnessGrid, |     brightness_grid: *mut SPBrightnessGrid, | ||||||
|     value: u8, |     value: u8, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     let brightness = |     let brightness = | ||||||
|         Brightness::try_from(value).expect("invalid brightness value"); |         Brightness::try_from(value).expect("invalid brightness value"); | ||||||
|     (*brightness_grid).0.fill(brightness); |     (*brightness_grid).0.fill(brightness); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the width of the `SPBrightnessGrid` instance.
 | /// Gets the width of the [SPBrightnessGrid] instance.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `brightness_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// returns: width
 | ||||||
|  | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_width( | pub unsafe extern "C" fn sp_brightness_grid_width( | ||||||
|     brightness_grid: *const SPBrightnessGrid, |     brightness_grid: *const SPBrightnessGrid, | ||||||
| ) -> usize { | ) -> usize { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     (*brightness_grid).0.width() |     (*brightness_grid).0.width() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the height of the `SPBrightnessGrid` instance.
 | /// Gets the height of the [SPBrightnessGrid] instance.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `brightness_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// returns: height
 | ||||||
|  | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `brightness_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | /// - `brightness_grid` points to a valid [SPBrightnessGrid]
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_height( | pub unsafe extern "C" fn sp_brightness_grid_height( | ||||||
|     brightness_grid: *const SPBrightnessGrid, |     brightness_grid: *const SPBrightnessGrid, | ||||||
| ) -> usize { | ) -> usize { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     (*brightness_grid).0.height() |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid`: instance to read from
 | /// - `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:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | /// - `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 after the passed [SPBrightnessGrid] has been freed
 | ||||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBrightnessGrid` or directly
 | /// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | ||||||
|     brightness_grid: *mut SPBrightnessGrid, |     brightness_grid: *mut SPBrightnessGrid, | ||||||
| ) -> SPByteSlice { | ) -> SPByteSlice { | ||||||
|  |     assert!(!brightness_grid.is_null()); | ||||||
|     assert_eq!(core::mem::size_of::<Brightness>(), 1); |     assert_eq!(core::mem::size_of::<Brightness>(), 1); | ||||||
| 
 |  | ||||||
|     let data = (*brightness_grid).0.data_ref_mut(); |     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); |     let data: &mut [u8] = transmute(data); | ||||||
|     SPByteSlice { |     SPByteSlice { | ||||||
|         start: data.as_mut_ptr_range().start, |         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||||
|         length: data.len(), |         length: data.len(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| //! FFI slice helper
 | //! FFI slice helper
 | ||||||
| 
 | 
 | ||||||
|  | use std::ptr::NonNull; | ||||||
|  | 
 | ||||||
| #[repr(C)] | #[repr(C)] | ||||||
| /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
 | /// 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.
 | ///   will try to free the memory of a potentially separate allocator.
 | ||||||
| pub struct SPByteSlice { | pub struct SPByteSlice { | ||||||
|     /// The start address of the memory
 |     /// The start address of the memory
 | ||||||
|     pub start: *mut u8, |     pub start: NonNull<u8>, | ||||||
|     /// The amount of memory in bytes
 |     /// The amount of memory in bytes
 | ||||||
|     pub length: usize, |     pub length: usize, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,21 +1,21 @@ | ||||||
| //! C functions for interacting with `SPCommand`s
 | //! C functions for interacting with [SPCommand]s
 | ||||||
| //!
 | //!
 | ||||||
| //! prefix `sp_command_`
 | //! prefix `sp_command_`
 | ||||||
| 
 | 
 | ||||||
| use std::ptr::null_mut; | use std::ptr::{null_mut, NonNull}; | ||||||
| 
 | 
 | ||||||
| use servicepoint::{Brightness, Origin}; | use servicepoint::{Brightness, Origin}; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     SPBitVec, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, SPPacket, |     SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, | ||||||
|     SPPixelGrid, |     SPPacket, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A low-level display command.
 | /// A low-level display command.
 | ||||||
| ///
 | ///
 | ||||||
| /// This struct and associated functions implement the UDP protocol for the display.
 | /// 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
 | /// # 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.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `SPPacket` points to a valid instance of `SPPacket`
 | /// - [SPPacket] points to a valid instance of [SPPacket]
 | ||||||
| /// - `SPPacket` is not used concurrently or after this call
 | /// - [SPPacket] is not used concurrently or after this call
 | ||||||
| /// - the result is checked for NULL
 | /// - 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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_try_from_packet( | 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_clone( | pub unsafe extern "C" fn sp_command_clone( | ||||||
|     command: *const SPCommand, |     command: *const SPCommand, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|     Box::into_raw(Box::new((*command).clone())) |     assert!(!command.is_null()); | ||||||
|  |     let result = Box::new((*command).clone()); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Set all pixels to the off state.
 | /// Set all pixels to the off state.
 | ||||||
| ///
 | ///
 | ||||||
| /// Does not affect brightness.
 | /// 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
 | /// # Examples
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -90,28 +102,30 @@ pub unsafe extern "C" fn sp_command_clone( | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_clear() -> *mut SPCommand { | pub unsafe extern "C" fn sp_command_clear() -> NonNull<SPCommand> { | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Clear))) |     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.
 | /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||||
| ///
 | ///
 | ||||||
| /// Please do not send this in your normal program flow.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand { | pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<SPCommand> { | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::HardReset))) |     let result = Box::new(SPCommand(servicepoint::Command::HardReset)); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// A yet-to-be-tested command.
 | /// 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 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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { | pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> { | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::FadeOut))) |     let result = Box::new(SPCommand(servicepoint::Command::FadeOut)); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Set the brightness of all tiles to the same value.
 | /// 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
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -141,44 +156,50 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_brightness( | pub unsafe extern "C" fn sp_command_brightness( | ||||||
|     brightness: u8, |     brightness: u8, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|     let brightness = |     let brightness = | ||||||
|         Brightness::try_from(brightness).expect("invalid brightness"); |         Brightness::try_from(brightness).expect("invalid brightness"); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Brightness( |     let result = Box::new(SPCommand( | ||||||
|         brightness, |         servicepoint::Command::Brightness(brightness), | ||||||
|     )))) |     )); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Set the brightness of individual tiles in a rectangular area of the display.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_char_brightness( | pub unsafe extern "C" fn sp_command_char_brightness( | ||||||
|     x: usize, |     x: usize, | ||||||
|     y: usize, |     y: usize, | ||||||
|     grid: *mut SPBrightnessGrid, |     grid: *mut SPBrightnessGrid, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!grid.is_null()); | ||||||
|     let byte_grid = *Box::from_raw(grid); |     let byte_grid = *Box::from_raw(grid); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::CharBrightness( |     let result = Box::new(SPCommand( | ||||||
|         Origin::new(x, y), |         servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0), | ||||||
|         byte_grid.0, |     )); | ||||||
|     )))) |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Set pixel data starting at the pixel offset on screen.
 | /// 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
 | /// 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.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not used concurrently or after this call
 | ||||||
| /// - `compression` matches one of the allowed enum values
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_bitmap_linear( | pub unsafe extern "C" fn sp_command_bitmap_linear( | ||||||
|     offset: usize, |     offset: usize, | ||||||
|     bit_vec: *mut SPBitVec, |     bit_vec: *mut SPBitVec, | ||||||
|     compression: SPCompressionCode, |     compression: SPCompressionCode, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     let bit_vec = *Box::from_raw(bit_vec); |     let bit_vec = *Box::from_raw(bit_vec); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinear( |     let result = Box::new(SPCommand( | ||||||
|         offset, |         servicepoint::Command::BitmapLinear( | ||||||
|         bit_vec.into(), |             offset, | ||||||
|         compression.try_into().expect("invalid compression code"), |             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.
 | /// 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
 | /// 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.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not used concurrently or after this call
 | ||||||
| /// - `compression` matches one of the allowed enum values
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_and( | pub unsafe extern "C" fn sp_command_bitmap_linear_and( | ||||||
|     offset: usize, |     offset: usize, | ||||||
|     bit_vec: *mut SPBitVec, |     bit_vec: *mut SPBitVec, | ||||||
|     compression: SPCompressionCode, |     compression: SPCompressionCode, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     let bit_vec = *Box::from_raw(bit_vec); |     let bit_vec = *Box::from_raw(bit_vec); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( |     let result = Box::new(SPCommand( | ||||||
|         offset, |         servicepoint::Command::BitmapLinearAnd( | ||||||
|         bit_vec.into(), |             offset, | ||||||
|         compression.try_into().expect("invalid compression code"), |             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.
 | /// 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
 | /// 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.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not used concurrently or after this call
 | ||||||
| /// - `compression` matches one of the allowed enum values
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_or( | pub unsafe extern "C" fn sp_command_bitmap_linear_or( | ||||||
|     offset: usize, |     offset: usize, | ||||||
|     bit_vec: *mut SPBitVec, |     bit_vec: *mut SPBitVec, | ||||||
|     compression: SPCompressionCode, |     compression: SPCompressionCode, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     let bit_vec = *Box::from_raw(bit_vec); |     let bit_vec = *Box::from_raw(bit_vec); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( |     let result = Box::new(SPCommand( | ||||||
|         offset, |         servicepoint::Command::BitmapLinearOr( | ||||||
|         bit_vec.into(), |             offset, | ||||||
|         compression.try_into().expect("invalid compression code"), |             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.
 | /// 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
 | /// 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.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `bit_vec` is not used concurrently or after this call
 | ||||||
| /// - `compression` matches one of the allowed enum values
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | ||||||
|     offset: usize, |     offset: usize, | ||||||
|     bit_vec: *mut SPBitVec, |     bit_vec: *mut SPBitVec, | ||||||
|     compression: SPCompressionCode, |     compression: SPCompressionCode, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!bit_vec.is_null()); | ||||||
|     let bit_vec = *Box::from_raw(bit_vec); |     let bit_vec = *Box::from_raw(bit_vec); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( |     let result = Box::new(SPCommand( | ||||||
|         offset, |         servicepoint::Command::BitmapLinearXor( | ||||||
|         bit_vec.into(), |             offset, | ||||||
|         compression.try_into().expect("invalid compression code"), |             bit_vec.into(), | ||||||
|     )))) |             compression.try_into().expect("invalid compression code"), | ||||||
|  |         ), | ||||||
|  |     )); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Show text on the screen.
 | /// Show text on the screen.
 | ||||||
| ///
 | ///
 | ||||||
| /// <div class="warning">
 | /// The passed [SPCp437Grid] gets consumed.
 | ||||||
| ///     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.///
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_cp437_data( | pub unsafe extern "C" fn sp_command_cp437_data( | ||||||
|     x: usize, |     x: usize, | ||||||
|     y: usize, |     y: usize, | ||||||
|     grid: *mut SPCp437Grid, |     grid: *mut SPCp437Grid, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|  |     assert!(!grid.is_null()); | ||||||
|     let grid = *Box::from_raw(grid); |     let grid = *Box::from_raw(grid); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Cp437Data( |     let result = Box::new(SPCommand( | ||||||
|         Origin::new(x, y), |         servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0), | ||||||
|         grid.0, |     )); | ||||||
|     )))) |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Sets a window of pixels to the specified values.
 | /// 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `pixel_grid` points to a valid instance of `SPPixelGrid`
 | /// - `bitmap` points to a valid instance of [SPBitmap]
 | ||||||
| /// - `pixel_grid` is not used concurrently or after this call
 | /// - `bitmap` is not used concurrently or after this call
 | ||||||
| /// - `compression` matches one of the allowed enum values
 | /// - `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`.
 | ///   by explicitly calling `sp_command_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_win( | pub unsafe extern "C" fn sp_command_bitmap_linear_win( | ||||||
|     x: usize, |     x: usize, | ||||||
|     y: usize, |     y: usize, | ||||||
|     pixel_grid: *mut SPPixelGrid, |     bitmap: *mut SPBitmap, | ||||||
|     compression_code: SPCompressionCode, |     compression_code: SPCompressionCode, | ||||||
| ) -> *mut SPCommand { | ) -> NonNull<SPCommand> { | ||||||
|     let byte_grid = (*Box::from_raw(pixel_grid)).0; |     assert!(!bitmap.is_null()); | ||||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( |     let byte_grid = (*Box::from_raw(bitmap)).0; | ||||||
|         Origin::new(x, y), |     let result = Box::new(SPCommand( | ||||||
|         byte_grid, |         servicepoint::Command::BitmapLinearWin( | ||||||
|         compression_code |             Origin::new(x, y), | ||||||
|             .try_into() |             byte_grid, | ||||||
|             .expect("invalid compression code"), |             compression_code | ||||||
|     )))) |                 .try_into() | ||||||
|  |                 .expect("invalid compression code"), | ||||||
|  |         ), | ||||||
|  |     )); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Deallocates a `SPCommand`.
 | /// Deallocates a [SPCommand].
 | ||||||
| ///
 | ///
 | ||||||
| /// # Examples
 | /// # Examples
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -390,14 +456,19 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( | ||||||
| /// sp_command_free(c);
 | /// sp_command_free(c);
 | ||||||
| /// ```
 | /// ```
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `command` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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` 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { | pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { | ||||||
|  |     assert!(!command.is_null()); | ||||||
|     _ = Box::from_raw(command); |     _ = 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_`
 | //! prefix `sp_connection_`
 | ||||||
| 
 | 
 | ||||||
|  | @ -18,13 +18,13 @@ use crate::{SPCommand, SPPacket}; | ||||||
| /// ```
 | /// ```
 | ||||||
| pub struct SPConnection(pub(crate) servicepoint::Connection); | 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
 | /// returns: NULL if connection fails, or connected instance
 | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// Bad string encoding
 | /// - when `host` is null or an invalid host
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -36,6 +36,7 @@ pub struct SPConnection(pub(crate) servicepoint::Connection); | ||||||
| pub unsafe extern "C" fn sp_connection_open( | pub unsafe extern "C" fn sp_connection_open( | ||||||
|     host: *const c_char, |     host: *const c_char, | ||||||
| ) -> *mut SPConnection { | ) -> *mut SPConnection { | ||||||
|  |     assert!(!host.is_null()); | ||||||
|     let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); |     let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); | ||||||
|     let connection = match servicepoint::Connection::open(host) { |     let connection = match servicepoint::Connection::open(host) { | ||||||
|         Err(_) => return null_mut(), |         Err(_) => return null_mut(), | ||||||
|  | @ -45,59 +46,78 @@ pub unsafe extern "C" fn sp_connection_open( | ||||||
|     Box::into_raw(Box::new(SPConnection(connection))) |     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.
 | /// The passed `packet` gets consumed.
 | ||||||
| ///
 | ///
 | ||||||
| /// returns: true in case of success
 | /// returns: true in case of success
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `connection` is NULL
 | ||||||
|  | /// - when `packet` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `connection` points to a valid instance of `SPConnection`
 | /// - `connection` points to a valid instance of [SPConnection]
 | ||||||
| /// - `packet` points to a valid instance of `SPPacket`
 | /// - `packet` points to a valid instance of [SPPacket]
 | ||||||
| /// - `packet` is not used concurrently or after this call
 | /// - `packet` is not used concurrently or after this call
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_connection_send_packet( | pub unsafe extern "C" fn sp_connection_send_packet( | ||||||
|     connection: *const SPConnection, |     connection: *const SPConnection, | ||||||
|     packet: *mut SPPacket, |     packet: *mut SPPacket, | ||||||
| ) -> bool { | ) -> bool { | ||||||
|  |     assert!(!connection.is_null()); | ||||||
|  |     assert!(!packet.is_null()); | ||||||
|     let packet = Box::from_raw(packet); |     let packet = Box::from_raw(packet); | ||||||
|     (*connection).0.send((*packet).0).is_ok() |     (*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.
 | /// The passed `command` gets consumed.
 | ||||||
| ///
 | ///
 | ||||||
| /// returns: true in case of success
 | /// returns: true in case of success
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `connection` is NULL
 | ||||||
|  | /// - when `command` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `connection` points to a valid instance of `SPConnection`
 | /// - `connection` points to a valid instance of [SPConnection]
 | ||||||
| /// - `command` points to a valid instance of `SPPacket`
 | /// - `command` points to a valid instance of [SPPacket]
 | ||||||
| /// - `command` is not used concurrently or after this call
 | /// - `command` is not used concurrently or after this call
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_connection_send_command( | pub unsafe extern "C" fn sp_connection_send_command( | ||||||
|     connection: *const SPConnection, |     connection: *const SPConnection, | ||||||
|     command: *mut SPCommand, |     command: *mut SPCommand, | ||||||
| ) -> bool { | ) -> bool { | ||||||
|  |     assert!(!connection.is_null()); | ||||||
|  |     assert!(!command.is_null()); | ||||||
|     let command = (*Box::from_raw(command)).0; |     let command = (*Box::from_raw(command)).0; | ||||||
|     (*connection).0.send(command).is_ok() |     (*connection).0.send(command).is_ok() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Closes and deallocates a `SPConnection`.
 | /// Closes and deallocates a [SPConnection].
 | ||||||
|  | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `connection` is NULL
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `connection` is not used concurrently or after this call
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { | pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { | ||||||
|  |     assert!(!connection.is_null()); | ||||||
|     _ = Box::from_raw(connection); |     _ = 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_`
 | //! prefix `sp_cp437_grid_`
 | ||||||
| 
 | 
 | ||||||
|  | use std::ptr::NonNull; | ||||||
| use crate::SPByteSlice; | use crate::SPByteSlice; | ||||||
| use servicepoint::{DataRef, Grid}; | 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -39,19 +40,21 @@ impl Clone for SPCp437Grid { | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_new( | pub unsafe extern "C" fn sp_cp437_grid_new( | ||||||
|     width: usize, |     width: usize, | ||||||
|     height: usize, |     height: usize, | ||||||
| ) -> *mut SPCp437Grid { | ) -> NonNull<SPCp437Grid> { | ||||||
|     Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new( |     let result = Box::new(SPCp437Grid( | ||||||
|         width, height, |         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.
 | /// Will never return NULL.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # 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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -67,43 +70,56 @@ pub unsafe extern "C" fn sp_cp437_grid_load( | ||||||
|     height: usize, |     height: usize, | ||||||
|     data: *const u8, |     data: *const u8, | ||||||
|     data_length: usize, |     data_length: usize, | ||||||
| ) -> *mut SPCp437Grid { | ) -> NonNull<SPCp437Grid> { | ||||||
|  |     assert!(data.is_null()); | ||||||
|     let data = std::slice::from_raw_parts(data, data_length); |     let data = std::slice::from_raw_parts(data, data_length); | ||||||
|     Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( |     let result = Box::new(SPCp437Grid( | ||||||
|         width, height, data, |         servicepoint::Cp437Grid::load(width, height, data), | ||||||
|     )))) |     )); | ||||||
|  |     NonNull::from(Box::leak(result)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Clones a `SPCp437Grid`.
 | /// Clones a [SPCp437Grid].
 | ||||||
| ///
 | ///
 | ||||||
| /// Will never return NULL.
 | /// Will never return NULL.
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `cp437_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `cp437_grid` is not written to concurrently
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_clone( | pub unsafe extern "C" fn sp_cp437_grid_clone( | ||||||
|     cp437_grid: *const SPCp437Grid, |     cp437_grid: *const SPCp437Grid, | ||||||
| ) -> *mut SPCp437Grid { | ) -> NonNull<SPCp437Grid> { | ||||||
|     Box::into_raw(Box::new((*cp437_grid).clone())) |     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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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` 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] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     _ = Box::from_raw(cp437_grid); |     _ = Box::from_raw(cp437_grid); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -116,13 +132,14 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When accessing `x` or `y` out of bounds.
 | /// - when `cp437_grid` is NULL
 | ||||||
|  | /// - when accessing `x` or `y` out of bounds
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `cp437_grid` is not written to concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_get( | pub unsafe extern "C" fn sp_cp437_grid_get( | ||||||
|  | @ -130,10 +147,11 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | ||||||
|     x: usize, |     x: usize, | ||||||
|     y: usize, |     y: usize, | ||||||
| ) -> u8 { | ) -> u8 { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     (*cp437_grid).0.get(x, y) |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -145,13 +163,14 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | ||||||
| ///
 | ///
 | ||||||
| /// # Panics
 | /// # Panics
 | ||||||
| ///
 | ///
 | ||||||
| /// When accessing `x` or `y` out of bounds.
 | /// - when `cp437_grid` is NULL
 | ||||||
|  | /// - when accessing `x` or `y` out of bounds
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `cp437_grid` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_set( | pub unsafe extern "C" fn sp_cp437_grid_set( | ||||||
|  | @ -160,84 +179,104 @@ pub unsafe extern "C" fn sp_cp437_grid_set( | ||||||
|     y: usize, |     y: usize, | ||||||
|     value: u8, |     value: u8, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     (*cp437_grid).0.set(x, y, value); |     (*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
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid`: instance to write to
 | /// - `cp437_grid`: instance to write to
 | ||||||
| /// - `value`: the value to set all cells to
 | /// - `value`: the value to set all cells to
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `cp437_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `cp437_grid` is not written to or read from concurrently
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_fill( | pub unsafe extern "C" fn sp_cp437_grid_fill( | ||||||
|     cp437_grid: *mut SPCp437Grid, |     cp437_grid: *mut SPCp437Grid, | ||||||
|     value: u8, |     value: u8, | ||||||
| ) { | ) { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     (*cp437_grid).0.fill(value); |     (*cp437_grid).0.fill(value); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the width of the `SPCp437Grid` instance.
 | /// Gets the width of the [SPCp437Grid] instance.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid`: instance to read from
 | /// - `cp437_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `cp437_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_width( | pub unsafe extern "C" fn sp_cp437_grid_width( | ||||||
|     cp437_grid: *const SPCp437Grid, |     cp437_grid: *const SPCp437Grid, | ||||||
| ) -> usize { | ) -> usize { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     (*cp437_grid).0.width() |     (*cp437_grid).0.width() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets the height of the `SPCp437Grid` instance.
 | /// Gets the height of the [SPCp437Grid] instance.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid`: instance to read from
 | /// - `cp437_grid`: instance to read from
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `cp437_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | /// - `cp437_grid` points to a valid [SPCp437Grid]
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_height( | pub unsafe extern "C" fn sp_cp437_grid_height( | ||||||
|     cp437_grid: *const SPCp437Grid, |     cp437_grid: *const SPCp437Grid, | ||||||
| ) -> usize { | ) -> usize { | ||||||
|  |     assert!(!cp437_grid.is_null()); | ||||||
|     (*cp437_grid).0.height() |     (*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.
 | /// Will never return NULL.
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `cp437_grid` is NULL
 | ||||||
|  | ///
 | ||||||
| /// ## Safety
 | /// ## Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | /// - `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 after the passed [SPCp437Grid] has been freed
 | ||||||
| /// - the returned memory range is never accessed concurrently, either via the `SPCp437Grid` or directly
 | /// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | ||||||
|     cp437_grid: *mut SPCp437Grid, |     cp437_grid: *mut SPCp437Grid, | ||||||
| ) -> SPByteSlice { | ) -> SPByteSlice { | ||||||
|     let data = (*cp437_grid).0.data_ref_mut(); |     let data = (*cp437_grid).0.data_ref_mut(); | ||||||
|     SPByteSlice { |     SPByteSlice { | ||||||
|         start: data.as_mut_ptr_range().start, |         start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), | ||||||
|         length: data.len(), |         length: data.len(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,8 +13,8 @@ | ||||||
| //!     if (connection == NULL)
 | //!     if (connection == NULL)
 | ||||||
| //!         return 1;
 | //!         return 1;
 | ||||||
| //!
 | //!
 | ||||||
| //!     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | //!     SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||||
| //!     sp_pixel_grid_fill(pixels, true);
 | //!     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, Uncompressed);
 | ||||||
| //!     while (sp_connection_send_command(connection, sp_command_clone(command)));
 | //!     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::brightness_grid::*; | ||||||
| pub use crate::byte_slice::*; | pub use crate::byte_slice::*; | ||||||
| pub use crate::command::*; | pub use crate::command::*; | ||||||
|  | @ -33,9 +34,9 @@ pub use crate::connection::*; | ||||||
| pub use crate::constants::*; | pub use crate::constants::*; | ||||||
| pub use crate::cp437_grid::*; | pub use crate::cp437_grid::*; | ||||||
| pub use crate::packet::*; | pub use crate::packet::*; | ||||||
| pub use crate::pixel_grid::*; |  | ||||||
| 
 | 
 | ||||||
| mod bit_vec; | mod bitvec; | ||||||
|  | mod bitmap; | ||||||
| mod brightness_grid; | mod brightness_grid; | ||||||
| mod byte_slice; | mod byte_slice; | ||||||
| mod command; | mod command; | ||||||
|  | @ -43,4 +44,3 @@ mod connection; | ||||||
| mod constants; | mod constants; | ||||||
| mod cp437_grid; | mod cp437_grid; | ||||||
| mod packet; | 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_`
 | //! prefix `sp_packet_`
 | ||||||
| 
 | 
 | ||||||
| use std::ptr::null_mut; | use std::ptr::{null_mut, NonNull}; | ||||||
| 
 | 
 | ||||||
| use crate::SPCommand; | use crate::SPCommand; | ||||||
| 
 | 
 | ||||||
| /// The raw packet
 | /// The raw packet
 | ||||||
| pub struct SPPacket(pub(crate) servicepoint::packet::Packet); | pub struct SPPacket(pub(crate) servicepoint::packet::Packet); | ||||||
| 
 | 
 | ||||||
| /// Turns a `SPCommand` into a `SPPacket`.
 | /// Turns a [SPCommand] into a [SPPacket].
 | ||||||
| /// The `SPCommand` gets consumed.
 | /// The [SPCommand] gets consumed.
 | ||||||
| ///
 | ///
 | ||||||
| /// Will never return NULL.
 | /// Will never return NULL.
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `command` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `SPCommand` points to a valid instance of `SPCommand`
 | /// - [SPCommand] points to a valid instance of [SPCommand]
 | ||||||
| /// - `SPCommand` is not used concurrently or after this call
 | /// - [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
 | /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||||
| ///   by explicitly calling `sp_packet_free`.
 | ///   by explicitly calling `sp_packet_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_packet_from_command( | pub unsafe extern "C" fn sp_packet_from_command( | ||||||
|     command: *mut SPCommand, |     command: *mut SPCommand, | ||||||
| ) -> *mut SPPacket { | ) -> NonNull<SPPacket> { | ||||||
|  |     assert!(!command.is_null()); | ||||||
|     let command = *Box::from_raw(command); |     let command = *Box::from_raw(command); | ||||||
|     let packet = SPPacket(command.0.into()); |     let result = Box::new(SPPacket(command.0.into())); | ||||||
|     Box::into_raw(Box::new(packet)) |     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
 | /// returns: NULL in case of an error, pointer to the allocated packet otherwise
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `data` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// The caller has to make sure that:
 | ||||||
| ///
 | ///
 | ||||||
| /// - `data` points to a valid memory region of at least `length` bytes
 | /// - `data` points to a valid memory region of at least `length` bytes
 | ||||||
| /// - `data` is not written to concurrently
 | /// - `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`.
 | ///   by explicitly calling `sp_packet_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_packet_try_load( | pub unsafe extern "C" fn sp_packet_try_load( | ||||||
|     data: *const u8, |     data: *const u8, | ||||||
|     length: usize, |     length: usize, | ||||||
| ) -> *mut SPPacket { | ) -> *mut SPPacket { | ||||||
|  |     assert!(!data.is_null()); | ||||||
|     let data = std::slice::from_raw_parts(data, length); |     let data = std::slice::from_raw_parts(data, length); | ||||||
|     match servicepoint::packet::Packet::try_from(data) { |     match servicepoint::packet::Packet::try_from(data) { | ||||||
|         Err(_) => null_mut(), |         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.
 | /// Will never return NULL.
 | ||||||
| ///
 | ///
 | ||||||
|  | /// # Panics
 | ||||||
|  | ///
 | ||||||
|  | /// - when `packet` is NULL
 | ||||||
|  | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `packet` is not written to concurrently
 | ||||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||||
| ///   by explicitly calling `sp_packet_free`.
 | ///   by explicitly calling `sp_packet_free`.
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_packet_clone( | pub unsafe extern "C" fn sp_packet_clone( | ||||||
|     packet: *const SPPacket, |     packet: *const SPPacket, | ||||||
| ) -> *mut SPPacket { | ) -> NonNull<SPPacket> { | ||||||
|     Box::into_raw(Box::new(SPPacket((*packet).0.clone()))) |     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
 | /// # Safety
 | ||||||
| ///
 | ///
 | ||||||
| /// The caller has to make sure that:
 | /// 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
 | /// - `packet` is not used concurrently or after this call
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { | pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { | ||||||
|  |     assert!(!packet.is_null()); | ||||||
|     _ = Box::from_raw(packet) |     _ = 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" | csbindgen = "1.9.3" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| servicepoint_binding_c = { version = "0.9.1", path = "../servicepoint_binding_c" } | servicepoint_binding_c = { version = "0.10.0", path = "../servicepoint_binding_c" } | ||||||
| servicepoint = { version = "0.9.1", path = "../servicepoint" } | servicepoint = { version = "0.10.0", path = "../servicepoint" } | ||||||
| 
 | 
 | ||||||
| [lints] | [lints] | ||||||
| workspace = true | workspace = true | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ using ServicePoint; | ||||||
| 
 | 
 | ||||||
| // using statement calls Dispose() on scope exit, which frees unmanaged instances | // using statement calls Dispose() on scope exit, which frees unmanaged instances | ||||||
| using var connection = Connection.Open("127.0.0.1:2342"); | 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) | while (true) | ||||||
| { | { | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -8,7 +8,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | ||||||
|     { |     { | ||||||
|         unsafe |         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) |             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 |         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 |             unsafe | ||||||
|             { |             { | ||||||
|                 return NativeMethods.sp_bit_vec_get(Instance, (nuint)index); |                 return NativeMethods.sp_bitvec_get(Instance, (nuint)index); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         set |         set | ||||||
|         { |         { | ||||||
|             unsafe |             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 |         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 |             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 |             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); |                 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; | 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 |         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 |         unsafe | ||||||
|         { |         { | ||||||
|             fixed (byte* bytesPtr = bytes) |             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)); |                     (nuint)bytes.Length)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public PixelGrid Clone() |     public Bitmap Clone() | ||||||
|     { |     { | ||||||
|         unsafe |         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 |             unsafe | ||||||
|             { |             { | ||||||
|                 return NativeMethods.sp_pixel_grid_get(Instance, (nuint)x, (nuint)y); |                 return NativeMethods.sp_bitmap_get(Instance, (nuint)x, (nuint)y); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         set |         set | ||||||
|         { |         { | ||||||
|             unsafe |             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 |         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 |             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 |             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 |             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); |                 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 |         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; | namespace ServicePoint; | ||||||
| 
 | 
 | ||||||
| public static class Constants | public static class Constants | ||||||
| { | { | ||||||
|     /// size of a single tile in one dimension |     /// 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 |     /// 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 |     /// tile count in the y-direction | ||||||
|     public const int TileHeight = 20; |     public const nuint TileHeight = NativeMethods.SP_TILE_SIZE; | ||||||
| 
 | 
 | ||||||
|     /// screen width in pixels |     /// screen width in pixels | ||||||
|     public const int PixelWidth = TileWidth * TileSize; |     public const nuint PixelWidth = TileWidth * TileSize; | ||||||
| 
 | 
 | ||||||
|     /// screen height in pixels |     /// screen height in pixels | ||||||
|     public const int PixelHeight = TileHeight * TileSize; |     public const nuint PixelHeight = TileHeight * TileSize; | ||||||
| 
 | 
 | ||||||
|     /// pixel count on whole screen |     /// pixel count on whole screen | ||||||
|     public const int PixelCount = PixelWidth * PixelHeight; |     public const nuint PixelCount = PixelWidth * PixelHeight; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
| 
 | 
 | ||||||
|     <PropertyGroup> |     <PropertyGroup> | ||||||
|         <PackageId>ServicePoint</PackageId> |         <PackageId>ServicePoint</PackageId> | ||||||
|         <Version>0.9.1</Version> |         <Version>0.10.0</Version> | ||||||
|         <Authors>Repository Authors</Authors> |         <Authors>Repository Authors</Authors> | ||||||
|         <Company>None</Company> |         <Company>None</Company> | ||||||
|         <Product>ServicePoint</Product> |         <Product>ServicePoint</Product> | ||||||
|  |  | ||||||
|  | @ -8,8 +8,12 @@ fn main() { | ||||||
| 
 | 
 | ||||||
|     let mut builder = csbindgen::Builder::default(); |     let mut builder = csbindgen::Builder::default(); | ||||||
| 
 | 
 | ||||||
|     for source in fs::read_dir("../servicepoint_binding_c/src").unwrap() { |     let mut paths = fs::read_dir("../servicepoint_binding_c/src").unwrap() | ||||||
|         let path = source.unwrap().path(); |         .map(|x| x.unwrap().path()) | ||||||
|  |         .collect::<Vec<_>>(); | ||||||
|  |     paths.sort(); | ||||||
|  | 
 | ||||||
|  |     for path in paths { | ||||||
|         println!("cargo:rerun-if-changed={}", path.display()); |         println!("cargo:rerun-if-changed={}", path.display()); | ||||||
|         builder = builder.input_extern_file(path); |         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.Clear().IntoPacket()); | ||||||
| connection.Send(Command.Brightness(128).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++) | for (var offset = 0; offset < int.MaxValue; offset++) | ||||||
| { | { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter