Merge branch 'fix-brightness'
This commit is contained in:
		
						commit
						a484e6a976
					
				
					 35 changed files with 1594 additions and 553 deletions
				
			
		
							
								
								
									
										14
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -16,9 +16,17 @@ jobs: | |||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - name: Build crates | ||||
| 
 | ||||
|     - name: build default features | ||||
|       run: cargo build --all --verbose | ||||
|     - name: Build | ||||
|     - name: build default features -- examples | ||||
|       run: cargo build --examples --verbose | ||||
|     - name: Run tests | ||||
|     - name: test default features | ||||
|       run: cargo test --all --verbose | ||||
| 
 | ||||
|     - name: build all features | ||||
|       run: cargo build --all-features --verbose | ||||
|     - name: build all features -- examples | ||||
|       run: cargo build --all-features --examples --verbose | ||||
|     - name: test all features | ||||
|       run: cargo test --all --all-features --verbose | ||||
|  |  | |||
|  | @ -1,14 +1,12 @@ | |||
| [workspace] | ||||
| resolver = "2" | ||||
| members = [ | ||||
|     "crates/servicepoint", | ||||
|     "crates/servicepoint_binding_c", | ||||
|     "crates/servicepoint_binding_cs", | ||||
|     "crates/*", | ||||
|     "crates/servicepoint_binding_c/examples/lang_c" | ||||
| ] | ||||
| 
 | ||||
| [workspace.package] | ||||
| version = "0.6.0" | ||||
| version = "0.7.0" | ||||
| 
 | ||||
| [workspace.lints.rust] | ||||
| missing-docs = "warn" | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ flate2 = { version = "1.0", optional = true } | |||
| bzip2 = { version = "0.4", optional = true } | ||||
| zstd = { version = "0.13", optional = true } | ||||
| rust-lzma = { version = "0.6.0", optional = true } | ||||
| rand = { version = "0.8", optional = true } | ||||
| 
 | ||||
| [features] | ||||
| default = ["compression_lzma"] | ||||
|  | @ -27,6 +28,11 @@ compression_bzip2 = ["dep:bzip2"] | |||
| compression_lzma = ["dep:rust-lzma"] | ||||
| compression_zstd = ["dep:zstd"] | ||||
| all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] | ||||
| rand = ["dep:rand"] | ||||
| 
 | ||||
| [[example]] | ||||
| name = "random_brightness" | ||||
| required-features = ["rand"] | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| # for examples | ||||
|  | @ -34,4 +40,4 @@ clap = { version = "4.5", features = ["derive"] } | |||
| rand = "0.8" | ||||
| 
 | ||||
| [lints] | ||||
| workspace = true | ||||
| workspace = true | ||||
|  | @ -46,7 +46,7 @@ In the likely case you only need one of them, you can include that one specifica | |||
| 
 | ||||
| ```toml | ||||
| [dependencies] | ||||
| servicepoint = { version = "0.6.0", default-features = false, features = ["compression-bz"] } | ||||
| servicepoint = { version = "0.7.0", default-features = false, features = ["compression-bz"] } | ||||
| ``` | ||||
| 
 | ||||
| ## Everything else | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::{ByteGrid, Command, Connection, Grid, Origin}; | ||||
| use servicepoint::{Command, Connection, Cp437Grid, Grid, Origin}; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|  | @ -22,14 +22,18 @@ fn main() { | |||
|         cli.text.push("Hello, CCCB!".to_string()); | ||||
|     } | ||||
| 
 | ||||
|     let connection = Connection::open(&cli.destination).unwrap(); | ||||
|     let connection = Connection::open(&cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     if cli.clear { | ||||
|         connection.send(Command::Clear).unwrap(); | ||||
|         connection | ||||
|             .send(Command::Clear) | ||||
|             .expect("sending clear failed"); | ||||
|     } | ||||
| 
 | ||||
|     let max_width = cli.text.iter().map(|t| t.len()).max().unwrap(); | ||||
| 
 | ||||
|     let mut chars = ByteGrid::new(max_width, cli.text.len()); | ||||
|     let mut chars = Cp437Grid::new(max_width, cli.text.len()); | ||||
|     for y in 0..cli.text.len() { | ||||
|         let row = &cli.text[y]; | ||||
| 
 | ||||
|  | @ -40,6 +44,6 @@ fn main() { | |||
|     } | ||||
| 
 | ||||
|     connection | ||||
|         .send(Command::Cp437Data(Origin(0, 0), chars)) | ||||
|         .unwrap(); | ||||
|         .send(Command::Cp437Data(Origin::new(0, 0), chars)) | ||||
|         .expect("sending text failed"); | ||||
| } | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::Command::BitmapLinearWin; | ||||
| use servicepoint::*; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
|  | @ -13,25 +12,26 @@ struct Cli { | |||
| 
 | ||||
| fn main() { | ||||
|     let cli = Cli::parse(); | ||||
|     let connection = Connection::open(cli.destination).unwrap(); | ||||
|     let connection = Connection::open(cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut pixels = PixelGrid::max_sized(); | ||||
|     pixels.fill(true); | ||||
| 
 | ||||
|     connection | ||||
|         .send(BitmapLinearWin( | ||||
|             Origin(0, 0), | ||||
|             pixels, | ||||
|             CompressionCode::Uncompressed, | ||||
|         )) | ||||
|         .expect("send failed"); | ||||
|     let command = Command::BitmapLinearWin( | ||||
|         Origin::new(0, 0), | ||||
|         pixels, | ||||
|         CompressionCode::Uncompressed, | ||||
|     ); | ||||
|     connection.send(command).expect("send failed"); | ||||
| 
 | ||||
|     let mut brightnesses = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT); | ||||
|     let max_brightness = usize::from(u8::from(Brightness::MAX)); | ||||
|     let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT); | ||||
|     for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { | ||||
|         *byte = (index % u8::MAX as usize) as u8; | ||||
|         *byte = Brightness::try_from((index % max_brightness) as u8).unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     connection | ||||
|         .send(Command::CharBrightness(Origin(0, 0), brightnesses)) | ||||
|         .send(Command::CharBrightness(Origin::new(0, 0), brightnesses)) | ||||
|         .expect("send failed"); | ||||
| } | ||||
|  |  | |||
|  | @ -18,17 +18,17 @@ struct Cli { | |||
| fn main() { | ||||
|     let cli = Cli::parse(); | ||||
| 
 | ||||
|     let connection = Connection::open(&cli.destination).unwrap(); | ||||
|     let connection = Connection::open(&cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
|     let mut field = make_random_field(cli.probability); | ||||
| 
 | ||||
|     loop { | ||||
|         connection | ||||
|             .send(Command::BitmapLinearWin( | ||||
|                 Origin(0, 0), | ||||
|                 field.clone(), | ||||
|                 CompressionCode::Lzma, | ||||
|             )) | ||||
|             .expect("could not send"); | ||||
|         let command = Command::BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             field.clone(), | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|         connection.send(command).expect("could not send"); | ||||
|         thread::sleep(FRAME_PACING); | ||||
|         field = iteration(field); | ||||
|     } | ||||
|  |  | |||
|  | @ -13,7 +13,8 @@ struct Cli { | |||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     let connection = Connection::open(Cli::parse().destination).unwrap(); | ||||
|     let connection = Connection::open(Cli::parse().destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut pixels = PixelGrid::max_sized(); | ||||
|     for x_offset in 0..usize::MAX { | ||||
|  | @ -22,13 +23,13 @@ fn main() { | |||
|         for y in 0..PIXEL_HEIGHT { | ||||
|             pixels.set((y + x_offset) % PIXEL_WIDTH, y, true); | ||||
|         } | ||||
|         connection | ||||
|             .send(Command::BitmapLinearWin( | ||||
|                 Origin(0, 0), | ||||
|                 pixels.clone(), | ||||
|                 CompressionCode::Lzma, | ||||
|             )) | ||||
|             .unwrap(); | ||||
| 
 | ||||
|         let command = Command::BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             pixels.clone(), | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|         connection.send(command).expect("send failed"); | ||||
|         thread::sleep(FRAME_PACING); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -22,7 +22,8 @@ struct Cli { | |||
| fn main() { | ||||
|     let cli = Cli::parse(); | ||||
| 
 | ||||
|     let connection = Connection::open(cli.destination).unwrap(); | ||||
|     let connection = Connection::open(cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
|     let wait_duration = Duration::from_millis(cli.wait_ms); | ||||
| 
 | ||||
|     // put all pixels in on state
 | ||||
|  | @ -30,8 +31,11 @@ fn main() { | |||
|         let mut filled_grid = PixelGrid::max_sized(); | ||||
|         filled_grid.fill(true); | ||||
| 
 | ||||
|         let command = | ||||
|             BitmapLinearWin(Origin(0, 0), filled_grid, CompressionCode::Lzma); | ||||
|         let command = BitmapLinearWin( | ||||
|             Origin::new(0, 0), | ||||
|             filled_grid, | ||||
|             CompressionCode::Lzma, | ||||
|         ); | ||||
|         connection.send(command).expect("send failed"); | ||||
|     } | ||||
| 
 | ||||
|  | @ -48,8 +52,8 @@ fn main() { | |||
|         let w = rng.gen_range(min_size..=TILE_WIDTH - x); | ||||
|         let h = rng.gen_range(min_size..=TILE_HEIGHT - y); | ||||
| 
 | ||||
|         let origin = Origin(x, y); | ||||
|         let mut luma = ByteGrid::new(w, h); | ||||
|         let origin = Origin::new(x, y); | ||||
|         let mut luma = BrightnessGrid::new(w, h); | ||||
| 
 | ||||
|         for y in 0..h { | ||||
|             for x in 0..w { | ||||
|  |  | |||
|  | @ -22,7 +22,8 @@ fn main() { | |||
|         Duration::from_millis(cli.time / PIXEL_WIDTH as u64), | ||||
|     ); | ||||
| 
 | ||||
|     let connection = Connection::open(cli.destination).unwrap(); | ||||
|     let connection = Connection::open(cli.destination) | ||||
|         .expect("could not connect to display"); | ||||
| 
 | ||||
|     let mut enabled_pixels = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); | ||||
|     enabled_pixels.fill(true); | ||||
|  | @ -38,7 +39,7 @@ fn main() { | |||
| 
 | ||||
|         connection | ||||
|             .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma)) | ||||
|             .unwrap(); | ||||
|             .expect("could not send command to display"); | ||||
|         thread::sleep(sleep_duration); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										111
									
								
								crates/servicepoint/src/brightness.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								crates/servicepoint/src/brightness.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,111 @@ | |||
| use crate::{Grid, PrimitiveGrid}; | ||||
| 
 | ||||
| #[cfg(feature = "rand")] | ||||
| use rand::{ | ||||
|     distributions::{Distribution, Standard}, | ||||
|     Rng, | ||||
| }; | ||||
| 
 | ||||
| /// A display brightness value, checked for correct value range
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```
 | ||||
| /// # use servicepoint::{Brightness, Command, Connection};
 | ||||
| /// let b = Brightness::MAX;
 | ||||
| /// let val: u8 = b.into();
 | ||||
| ///
 | ||||
| /// let b = Brightness::try_from(7).unwrap();
 | ||||
| /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
| /// let result = connection.send(Command::Brightness(b));
 | ||||
| /// ```
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq)] | ||||
| pub struct Brightness(u8); | ||||
| 
 | ||||
| /// A grid containing brightness values.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
 | ||||
| /// let mut grid = BrightnessGrid::new(2,2);
 | ||||
| /// grid.set(0, 0, Brightness::MIN);
 | ||||
| /// grid.set(1, 1, Brightness::MIN);
 | ||||
| ///
 | ||||
| /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
| /// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
 | ||||
| /// ```
 | ||||
| pub type BrightnessGrid = PrimitiveGrid<Brightness>; | ||||
| 
 | ||||
| impl From<Brightness> for u8 { | ||||
|     fn from(brightness: Brightness) -> Self { | ||||
|         brightness.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<u8> for Brightness { | ||||
|     type Error = u8; | ||||
| 
 | ||||
|     fn try_from(value: u8) -> Result<Self, Self::Error> { | ||||
|         if value > Self::MAX.0 { | ||||
|             Err(value) | ||||
|         } else { | ||||
|             Ok(Brightness(value)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Brightness { | ||||
|     /// highest possible brightness value, 11
 | ||||
|     pub const MAX: Brightness = Brightness(11); | ||||
|     /// lowest possible brightness value, 0
 | ||||
|     pub const MIN: Brightness = Brightness(0); | ||||
| } | ||||
| 
 | ||||
| impl Default for Brightness { | ||||
|     fn default() -> Self { | ||||
|         Self::MAX | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BrightnessGrid> for Vec<u8> { | ||||
|     fn from(value: PrimitiveGrid<Brightness>) -> Self { | ||||
|         value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BrightnessGrid> for PrimitiveGrid<u8> { | ||||
|     fn from(value: PrimitiveGrid<Brightness>) -> Self { | ||||
|         let u8s = value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect::<Vec<u8>>(); | ||||
|         PrimitiveGrid::load(value.width(), value.height(), &u8s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<PrimitiveGrid<u8>> for BrightnessGrid { | ||||
|     type Error = u8; | ||||
| 
 | ||||
|     fn try_from(value: PrimitiveGrid<u8>) -> Result<Self, Self::Error> { | ||||
|         let brightnesses = value | ||||
|             .iter() | ||||
|             .map(|b| Brightness::try_from(*b)) | ||||
|             .collect::<Result<Vec<Brightness>, _>>()?; | ||||
|         Ok(BrightnessGrid::load( | ||||
|             value.width(), | ||||
|             value.height(), | ||||
|             &brightnesses, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "rand")] | ||||
| impl Distribution<Brightness> for Standard { | ||||
|     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Brightness { | ||||
|         Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0)) | ||||
|     } | ||||
| } | ||||
|  | @ -1,183 +1,168 @@ | |||
| use bitvec::prelude::BitVec; | ||||
| 
 | ||||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::{into_compressed, into_decompressed}; | ||||
| use crate::{ | ||||
|     ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, SpBitVec, | ||||
|     TILE_SIZE, | ||||
|     command_code::CommandCode, compression::into_decompressed, Brightness, | ||||
|     BrightnessGrid, CompressionCode, Header, Origin, Packet, PixelGrid, Pixels, | ||||
|     PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
| /// An origin marks the top left position of a window sent to the display.
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
| pub struct Origin(pub usize, pub usize); | ||||
| 
 | ||||
| impl std::ops::Add<Origin> for Origin { | ||||
|     type Output = Origin; | ||||
| 
 | ||||
|     fn add(self, rhs: Origin) -> Self::Output { | ||||
|         let Origin(x1, y1) = self; | ||||
|         let Origin(x2, y2) = rhs; | ||||
|         Origin(x1 + x2, y1 + y2) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Type alias for documenting the meaning of the u16 in enum values
 | ||||
| pub type Offset = usize; | ||||
| 
 | ||||
| /// Type alias for documenting the meaning of the u16 in enum values
 | ||||
| pub type Brightness = u8; | ||||
| /// A grid containing codepage 437 characters.
 | ||||
| ///
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| pub type Cp437Grid = PrimitiveGrid<u8>; | ||||
| 
 | ||||
| /// A command to send to the display.
 | ||||
| /// A low-level display command.
 | ||||
| ///
 | ||||
| /// This struct and associated functions implement the UDP protocol for the display.
 | ||||
| ///
 | ||||
| /// To send a `Command`, use a `Connection`.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Brightness, Command, Connection, Packet};
 | ||||
| ///
 | ||||
| /// // create command
 | ||||
| /// let command = Command::Brightness(Brightness::MAX);
 | ||||
| ///
 | ||||
| /// // turn command into Packet
 | ||||
| /// let packet: Packet = command.clone().into();
 | ||||
| ///
 | ||||
| /// // read command from packet
 | ||||
| /// let round_tripped = Command::try_from(packet).unwrap();
 | ||||
| ///
 | ||||
| /// // round tripping produces exact copy
 | ||||
| /// assert_eq!(command, round_tripped);
 | ||||
| ///
 | ||||
| /// // send command
 | ||||
| /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
| /// connection.send(command).unwrap();
 | ||||
| /// ```
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum Command { | ||||
|     /// Set all pixels to the off state
 | ||||
|     /// Set all pixels to the off state. Does not affect brightness.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// connection.send(Command::Clear).unwrap();
 | ||||
|     /// ```
 | ||||
|     Clear, | ||||
|     /// Kills the udp daemon, usually results in a reboot of the display.
 | ||||
|     HardReset, | ||||
|     /// Slowly decrease brightness until off? Untested.
 | ||||
|     FadeOut, | ||||
|     /// Set the brightness of tiles
 | ||||
|     CharBrightness(Origin, ByteGrid), | ||||
|     /// Set the brightness of all tiles
 | ||||
| 
 | ||||
|     /// Show text on the screen.
 | ||||
|     ///
 | ||||
|     /// <div class="warning">
 | ||||
|     ///     The library does not currently convert between UTF-8 and CP-437.
 | ||||
|     ///     Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
 | ||||
|     /// </div>
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection, Cp437Grid, Origin};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'].map(move |c| c as u8);
 | ||||
|     /// let grid = Cp437Grid::load(5, 2, &chars);
 | ||||
|     /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
 | ||||
|     /// ```
 | ||||
|     Cp437Data(Origin<Tiles>, Cp437Grid), | ||||
| 
 | ||||
|     /// Sets a window of pixels to the specified values
 | ||||
|     BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode), | ||||
| 
 | ||||
|     /// Set the brightness of all tiles to the same value.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Brightness, Command, Connection};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// let command = Command::Brightness(Brightness::MAX);
 | ||||
|     /// connection.send(command).unwrap();
 | ||||
|     /// ```
 | ||||
|     Brightness(Brightness), | ||||
|     #[deprecated] | ||||
|     /// Legacy command code, gets ignored by the real display.
 | ||||
|     BitmapLegacy, | ||||
|     /// Set pixel data starting at the offset.
 | ||||
| 
 | ||||
|     /// Set the brightness of individual tiles in a rectangular area of the display.
 | ||||
|     CharBrightness(Origin<Tiles>, BrightnessGrid), | ||||
| 
 | ||||
|     /// Set pixel data starting at the pixel offset on screen.
 | ||||
|     ///
 | ||||
|     /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
|     /// once the starting row is full, overwriting will continue on column 0.
 | ||||
|     ///
 | ||||
|     /// The contained `BitVec` is always uncompressed.
 | ||||
|     BitmapLinear(Offset, SpBitVec, CompressionCode), | ||||
| 
 | ||||
|     /// Set pixel data according to an and-mask starting at the offset.
 | ||||
|     ///
 | ||||
|     /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
|     /// once the starting row is full, overwriting will continue on column 0.
 | ||||
|     ///
 | ||||
|     /// The contained `BitVec` is always uncompressed.
 | ||||
|     BitmapLinearAnd(Offset, SpBitVec, CompressionCode), | ||||
| 
 | ||||
|     /// Set pixel data according to an or-mask starting at the offset.
 | ||||
|     ///
 | ||||
|     /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
|     /// once the starting row is full, overwriting will continue on column 0.
 | ||||
|     ///
 | ||||
|     /// The contained `BitVec` is always uncompressed.
 | ||||
|     BitmapLinearOr(Offset, SpBitVec, CompressionCode), | ||||
| 
 | ||||
|     /// Set pixel data according to a xor-mask starting at the offset.
 | ||||
|     ///
 | ||||
|     /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
|     /// once the starting row is full, overwriting will continue on column 0.
 | ||||
|     ///
 | ||||
|     /// The contained `BitVec` is always uncompressed.
 | ||||
|     BitmapLinearXor(Offset, SpBitVec, CompressionCode), | ||||
|     /// Show text on the screen. Note that the byte data has to be CP437 encoded.
 | ||||
|     Cp437Data(Origin, ByteGrid), | ||||
|     /// Sets a window of pixels to the specified values
 | ||||
|     BitmapLinearWin(Origin, PixelGrid, CompressionCode), | ||||
| } | ||||
| 
 | ||||
| impl From<Command> for Packet { | ||||
|     /// Move the `Command` into a `Packet` instance for sending.
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn from(value: Command) -> Self { | ||||
|         match value { | ||||
|             Command::Clear => Command::command_code_only(CommandCode::Clear), | ||||
|             Command::FadeOut => { | ||||
|                 Command::command_code_only(CommandCode::FadeOut) | ||||
|             } | ||||
|             Command::HardReset => { | ||||
|                 Command::command_code_only(CommandCode::HardReset) | ||||
|             } | ||||
|             #[allow(deprecated)] | ||||
|             Command::BitmapLegacy => { | ||||
|                 Command::command_code_only(CommandCode::BitmapLegacy) | ||||
|             } | ||||
|             Command::CharBrightness(Origin(x, y), grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::CharBrightness.into(), | ||||
|                     x as u16, | ||||
|                     y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|             Command::Brightness(brightness) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Brightness.into(), | ||||
|                     0x00000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                 ), | ||||
|                 vec![brightness], | ||||
|             ), | ||||
|             Command::BitmapLinearWin(origin, pixels, compression) => { | ||||
|                 bitmap_win_into_packet(origin, pixels, compression) | ||||
|             } | ||||
|             Command::BitmapLinear(offset, bits, compression) => { | ||||
|                 Command::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinear, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearAnd(offset, bits, compression) => { | ||||
|                 Command::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearAnd, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearOr(offset, bits, compression) => { | ||||
|                 Command::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearOr, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearXor(offset, bits, compression) => { | ||||
|                 Command::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearXor, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::Cp437Data(Origin(x, y), grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Cp437Data.into(), | ||||
|                     x as u16, | ||||
|                     y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|     /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||
|     ///
 | ||||
|     /// Please do not send this in your normal program flow.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// connection.send(Command::HardReset).unwrap();
 | ||||
|     /// ```
 | ||||
|     HardReset, | ||||
| 
 | ||||
| #[allow(clippy::cast_possible_truncation)] | ||||
| fn bitmap_win_into_packet( | ||||
|     origin: Origin, | ||||
|     pixels: PixelGrid, | ||||
|     compression: CompressionCode, | ||||
| ) -> Packet { | ||||
|     let Origin(pixel_x, pixel_y) = origin; | ||||
|     debug_assert_eq!(pixel_x % 8, 0); | ||||
|     debug_assert_eq!(pixels.width() % 8, 0); | ||||
|     /// <div class="warning">Untested</div>
 | ||||
|     ///
 | ||||
|     /// Slowly decrease brightness until off or something like that?
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// connection.send(Command::FadeOut).unwrap();
 | ||||
|     /// ```
 | ||||
|     FadeOut, | ||||
| 
 | ||||
|     let tile_x = (pixel_x / TILE_SIZE) as u16; | ||||
|     let tile_w = (pixels.width() / TILE_SIZE) as u16; | ||||
|     let pixel_h = pixels.height() as u16; | ||||
|     let payload = into_compressed(compression, pixels.into()); | ||||
|     let command = match compression { | ||||
|         CompressionCode::Uncompressed => { | ||||
|             CommandCode::BitmapLinearWinUncompressed | ||||
|         } | ||||
|         #[cfg(feature = "compression_zlib")] | ||||
|         CompressionCode::Zlib => CommandCode::BitmapLinearWinZlib, | ||||
|         #[cfg(feature = "compression_bzip2")] | ||||
|         CompressionCode::Bzip2 => CommandCode::BitmapLinearWinBzip2, | ||||
|         #[cfg(feature = "compression_lzma")] | ||||
|         CompressionCode::Lzma => CommandCode::BitmapLinearWinLzma, | ||||
|         #[cfg(feature = "compression_zstd")] | ||||
|         CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, | ||||
|     }; | ||||
| 
 | ||||
|     Packet( | ||||
|         Header(command.into(), tile_x, pixel_y as u16, tile_w, pixel_h), | ||||
|         payload, | ||||
|     ) | ||||
|     /// Legacy command code, gets ignored by the real display.
 | ||||
|     ///
 | ||||
|     /// Might be useful as a noop package.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection};
 | ||||
|     /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
|     /// // this sends a packet that does nothing
 | ||||
|     /// # #[allow(deprecated)]
 | ||||
|     /// connection.send(Command::BitmapLegacy).unwrap();
 | ||||
|     /// ```
 | ||||
|     #[deprecated] | ||||
|     BitmapLegacy, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
|  | @ -196,6 +181,8 @@ pub enum TryFromPacketError { | |||
|     InvalidCompressionCode(u16), | ||||
|     /// Decompression of the payload failed. This can be caused by corrupted packets.
 | ||||
|     DecompressionFailed, | ||||
|     /// The given brightness value is out of bounds
 | ||||
|     InvalidBrightness(u8), | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Packet> for Command { | ||||
|  | @ -203,7 +190,7 @@ impl TryFrom<Packet> for Command { | |||
| 
 | ||||
|     /// Try to interpret the `Packet` as one containing a `Command`
 | ||||
|     fn try_from(packet: Packet) -> Result<Self, Self::Error> { | ||||
|         let Packet(Header(command_u16, a, b, c, d), _) = packet; | ||||
|         let Packet(Header(command_u16, a, _, _, _), _) = packet; | ||||
|         let command_code = match CommandCode::try_from(command_u16) { | ||||
|             Err(()) => { | ||||
|                 return Err(TryFromPacketError::InvalidCommand(command_u16)); | ||||
|  | @ -212,47 +199,19 @@ impl TryFrom<Packet> for Command { | |||
|         }; | ||||
| 
 | ||||
|         match command_code { | ||||
|             CommandCode::Clear => match Self::check_command_only(packet) { | ||||
|                 Some(err) => Err(err), | ||||
|                 None => Ok(Command::Clear), | ||||
|             }, | ||||
|             CommandCode::Brightness => { | ||||
|                 let Packet(header, payload) = packet; | ||||
|                 if payload.len() != 1 { | ||||
|                     return Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|                         1, | ||||
|                         payload.len(), | ||||
|                     )); | ||||
|                 } | ||||
| 
 | ||||
|                 let Header(_, a, b, c, d) = header; | ||||
|                 if a != 0 || b != 0 || c != 0 || d != 0 { | ||||
|                     Err(TryFromPacketError::ExtraneousHeaderValues) | ||||
|                 } else { | ||||
|                     Ok(Command::Brightness(payload[0])) | ||||
|                 } | ||||
|             CommandCode::Clear => { | ||||
|                 Self::packet_into_command_only(packet, Command::Clear) | ||||
|             } | ||||
|             CommandCode::HardReset => match Self::check_command_only(packet) { | ||||
|                 Some(err) => Err(err), | ||||
|                 None => Ok(Command::HardReset), | ||||
|             }, | ||||
|             CommandCode::FadeOut => match Self::check_command_only(packet) { | ||||
|                 Some(err) => Err(err), | ||||
|                 None => Ok(Command::FadeOut), | ||||
|             }, | ||||
|             CommandCode::Cp437Data => { | ||||
|                 let Packet(_, payload) = packet; | ||||
|                 Ok(Command::Cp437Data( | ||||
|                     Origin(a as usize, b as usize), | ||||
|                     ByteGrid::load(c as usize, d as usize, &payload), | ||||
|                 )) | ||||
|             CommandCode::Brightness => Self::packet_into_brightness(&packet), | ||||
|             CommandCode::HardReset => { | ||||
|                 Self::packet_into_command_only(packet, Command::HardReset) | ||||
|             } | ||||
|             CommandCode::FadeOut => { | ||||
|                 Self::packet_into_command_only(packet, Command::FadeOut) | ||||
|             } | ||||
|             CommandCode::Cp437Data => Self::packet_into_cp437(&packet), | ||||
|             CommandCode::CharBrightness => { | ||||
|                 let Packet(_, payload) = packet; | ||||
|                 Ok(Command::CharBrightness( | ||||
|                     Origin(a as usize, b as usize), | ||||
|                     ByteGrid::load(c as usize, d as usize, &payload), | ||||
|                 )) | ||||
|                 Self::packet_into_char_brightness(&packet) | ||||
|             } | ||||
|             #[allow(deprecated)] | ||||
|             CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy), | ||||
|  | @ -316,7 +275,7 @@ impl Command { | |||
|         }; | ||||
| 
 | ||||
|         Ok(Command::BitmapLinearWin( | ||||
|             Origin(tiles_x as usize * TILE_SIZE, pixels_y as usize), | ||||
|             Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), | ||||
|             PixelGrid::load( | ||||
|                 tile_w as usize * TILE_SIZE, | ||||
|                 pixel_h as usize, | ||||
|  | @ -326,42 +285,18 @@ impl Command { | |||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for `BitMapLinear*`-Commands into `Packet`
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn bitmap_linear_into_packet( | ||||
|         command: CommandCode, | ||||
|         offset: Offset, | ||||
|         compression: CompressionCode, | ||||
|         payload: Vec<u8>, | ||||
|     ) -> Packet { | ||||
|         let length = payload.len() as u16; | ||||
|         let payload = into_compressed(compression, payload); | ||||
|         Packet( | ||||
|             Header( | ||||
|                 command.into(), | ||||
|                 offset as u16, | ||||
|                 length, | ||||
|                 compression.into(), | ||||
|                 0, | ||||
|             ), | ||||
|             payload, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for creating empty packets only containing the command code
 | ||||
|     fn command_code_only(code: CommandCode) -> Packet { | ||||
|         Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![]) | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for checking that a packet is empty and only contains a command code
 | ||||
|     fn check_command_only(packet: Packet) -> Option<TryFromPacketError> { | ||||
|     fn packet_into_command_only( | ||||
|         packet: Packet, | ||||
|         command: Command, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         if !payload.is_empty() { | ||||
|             Some(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) | ||||
|         } else if a != 0 || b != 0 || c != 0 || d != 0 { | ||||
|             Some(TryFromPacketError::ExtraneousHeaderValues) | ||||
|             Err(TryFromPacketError::ExtraneousHeaderValues) | ||||
|         } else { | ||||
|             None | ||||
|             Ok(command) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -391,16 +326,63 @@ impl Command { | |||
|         } | ||||
|         Ok((BitVec::from_vec(payload), sub)) | ||||
|     } | ||||
| 
 | ||||
|     fn packet_into_char_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, x, y, width, height), payload) = packet; | ||||
| 
 | ||||
|         let grid = | ||||
|             PrimitiveGrid::load(*width as usize, *height as usize, payload); | ||||
|         let grid = match BrightnessGrid::try_from(grid) { | ||||
|             Ok(grid) => grid, | ||||
|             Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)), | ||||
|         }; | ||||
| 
 | ||||
|         Ok(Command::CharBrightness( | ||||
|             Origin::new(*x as usize, *y as usize), | ||||
|             grid, | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn packet_into_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         if payload.len() != 1 { | ||||
|             return Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|                 1, | ||||
|                 payload.len(), | ||||
|             )); | ||||
|         } | ||||
| 
 | ||||
|         if *a != 0 || *b != 0 || *c != 0 || *d != 0 { | ||||
|             return Err(TryFromPacketError::ExtraneousHeaderValues); | ||||
|         } | ||||
| 
 | ||||
|         match Brightness::try_from(payload[0]) { | ||||
|             Ok(b) => Ok(Command::Brightness(b)), | ||||
|             Err(_) => Err(TryFromPacketError::InvalidBrightness(payload[0])), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn packet_into_cp437( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         Ok(Command::Cp437Data( | ||||
|             Origin::new(*a as usize, *b as usize), | ||||
|             Cp437Grid::load(*c as usize, *d as usize, payload), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use bitvec::prelude::BitVec; | ||||
| 
 | ||||
|     use crate::command::TryFromPacketError; | ||||
|     use crate::command_code::CommandCode; | ||||
|     use crate::{ | ||||
|         ByteGrid, Command, CompressionCode, Header, Origin, Packet, PixelGrid, | ||||
|         bitvec::prelude::BitVec, command::TryFromPacketError, | ||||
|         command_code::CommandCode, origin::Pixels, Brightness, Command, | ||||
|         CompressionCode, Header, Origin, Packet, PixelGrid, PrimitiveGrid, | ||||
|     }; | ||||
| 
 | ||||
|     fn round_trip(original: Command) { | ||||
|  | @ -443,7 +425,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_brightness() { | ||||
|         round_trip(Command::Brightness(6)); | ||||
|         round_trip(Command::Brightness(Brightness::try_from(6).unwrap())); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|  | @ -454,12 +436,18 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_char_brightness() { | ||||
|         round_trip(Command::CharBrightness(Origin(5, 2), ByteGrid::new(7, 5))); | ||||
|         round_trip(Command::CharBrightness( | ||||
|             Origin::new(5, 2), | ||||
|             PrimitiveGrid::new(7, 5), | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_cp437_data() { | ||||
|         round_trip(Command::Cp437Data(Origin(5, 2), ByteGrid::new(7, 5))); | ||||
|         round_trip(Command::Cp437Data( | ||||
|             Origin::new(5, 2), | ||||
|             PrimitiveGrid::new(7, 5), | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|  | @ -486,7 +474,7 @@ mod tests { | |||
|                 compression, | ||||
|             )); | ||||
|             round_trip(Command::BitmapLinearWin( | ||||
|                 Origin(0, 0), | ||||
|                 Origin::new(0, 0), | ||||
|                 PixelGrid::max_sized(), | ||||
|                 compression, | ||||
|             )); | ||||
|  | @ -572,7 +560,7 @@ mod tests { | |||
|     fn error_decompression_failed_win() { | ||||
|         for compression in all_compressions().to_owned() { | ||||
|             let p: Packet = Command::BitmapLinearWin( | ||||
|                 Origin(16, 8), | ||||
|                 Origin::new(16, 8), | ||||
|                 PixelGrid::new(8, 8), | ||||
|                 compression, | ||||
|             ) | ||||
|  | @ -696,6 +684,9 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn origin_add() { | ||||
|         assert_eq!(Origin(4, 2), Origin(1, 0) + Origin(3, 2)); | ||||
|         assert_eq!( | ||||
|             Origin::<Pixels>::new(4, 2), | ||||
|             Origin::new(1, 0) + Origin::new(3, 2) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,17 @@ | |||
| /// Specifies the kind of compression to use. Availability depends on features.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid};
 | ||||
| /// // create command without payload compression
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed);
 | ||||
| ///
 | ||||
| /// // create command with payload compressed with lzma and appropriate header flags
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Lzma);
 | ||||
| /// ```
 | ||||
| #[repr(u16)] | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
| pub enum CompressionCode { | ||||
|  |  | |||
|  | @ -6,6 +6,15 @@ use log::{debug, info}; | |||
| use crate::Packet; | ||||
| 
 | ||||
| /// A connection to the display.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::Command;
 | ||||
| /// let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
| ///     .expect("connection failed");
 | ||||
| ///  connection.send(Command::Clear)
 | ||||
| ///     .expect("send failed");
 | ||||
| /// ```
 | ||||
| pub struct Connection { | ||||
|     socket: UdpSocket, | ||||
| } | ||||
|  | @ -46,21 +55,12 @@ impl Connection { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
|     /// let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
|     ///     .expect("connection failed");
 | ||||
|     ///
 | ||||
|     ///  // turn off all pixels
 | ||||
|     /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
|     /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
|     /// #     .expect("connection failed");
 | ||||
|     ///  // turn off all pixels on display
 | ||||
|     ///  connection.send(Command::Clear)
 | ||||
|     ///     .expect("send failed");
 | ||||
|     ///
 | ||||
|     ///  // turn on all pixels
 | ||||
|     ///  let mut pixels = PixelGrid::max_sized();
 | ||||
|     ///  pixels.fill(true);
 | ||||
|     ///
 | ||||
|     ///  // send pixels to display
 | ||||
|     ///  connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed))
 | ||||
|     ///     .expect("send failed");
 | ||||
|     /// ```
 | ||||
|     pub fn send( | ||||
|         &self, | ||||
|  |  | |||
|  | @ -2,10 +2,13 @@ | |||
| ///
 | ||||
| /// The expectation is that you can create an equal instance with this data given the additional
 | ||||
| /// metadata needed.
 | ||||
| pub trait DataRef { | ||||
| pub trait DataRef<T> { | ||||
|     /// Get the underlying bytes writable.
 | ||||
|     fn data_ref_mut(&mut self) -> &mut [u8]; | ||||
|     ///
 | ||||
|     /// Note that depending on the struct this is implemented on, writing invalid values here might
 | ||||
|     /// lead to panics later in the lifetime of the program or on the receiving side.
 | ||||
|     fn data_ref_mut(&mut self) -> &mut [T]; | ||||
| 
 | ||||
|     /// Get the underlying bytes read-only.
 | ||||
|     fn data_ref(&self) -> &[u8]; | ||||
|     fn data_ref(&self) -> &[T]; | ||||
| } | ||||
|  |  | |||
|  | @ -1,22 +1,55 @@ | |||
| //! Abstractions for the UDP protocol of the CCCB servicepoint display.
 | ||||
| //!
 | ||||
| //! # Examples
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
| //!
 | ||||
| //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
 | ||||
| //!     .expect("connection failed");
 | ||||
| //!
 | ||||
| //!  // turn off all pixels on display
 | ||||
| //!  connection.send(Command::Clear)
 | ||||
| //!     .expect("send failed");
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
| //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
 | ||||
| //!  // turn on all pixels in a grid
 | ||||
| //!  let mut pixels = PixelGrid::max_sized();
 | ||||
| //!  pixels.fill(true);
 | ||||
| //!
 | ||||
| //!  // create command to send pixels
 | ||||
| //!  let command = Command::BitmapLinearWin(
 | ||||
| //!     servicepoint::Origin::new(0, 0),
 | ||||
| //!     pixels,
 | ||||
| //!     CompressionCode::Uncompressed
 | ||||
| //!  );
 | ||||
| //!
 | ||||
| //!  // send command to display
 | ||||
| //!  connection.send(command).expect("send failed");
 | ||||
| //! ```
 | ||||
| 
 | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| pub use bitvec; | ||||
| use bitvec::prelude::{BitVec, Msb0}; | ||||
| 
 | ||||
| pub use crate::byte_grid::ByteGrid; | ||||
| pub use crate::command::{Brightness, Command, Offset, Origin}; | ||||
| pub use crate::brightness::{Brightness, BrightnessGrid}; | ||||
| pub use crate::command::{Command, Cp437Grid, Offset}; | ||||
| pub use crate::compression_code::CompressionCode; | ||||
| pub use crate::connection::Connection; | ||||
| pub use crate::data_ref::DataRef; | ||||
| pub use crate::grid::Grid; | ||||
| pub use crate::origin::{Origin, Pixels, Tiles}; | ||||
| pub use crate::packet::{Header, Packet, Payload}; | ||||
| pub use crate::pixel_grid::PixelGrid; | ||||
| pub use crate::primitive_grid::PrimitiveGrid; | ||||
| 
 | ||||
| type SpBitVec = BitVec<u8, Msb0>; | ||||
| 
 | ||||
| mod byte_grid; | ||||
| mod brightness; | ||||
| mod command; | ||||
| mod command_code; | ||||
| mod compression; | ||||
|  | @ -24,28 +57,84 @@ mod compression_code; | |||
| mod connection; | ||||
| mod data_ref; | ||||
| mod grid; | ||||
| mod origin; | ||||
| mod packet; | ||||
| mod pixel_grid; | ||||
| mod primitive_grid; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
| pub const TILE_SIZE: usize = 8; | ||||
| 
 | ||||
| /// tile count in the x-direction
 | ||||
| /// Display tile count in the x-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_WIDTH: usize = 56; | ||||
| 
 | ||||
| /// tile count in the y-direction
 | ||||
| /// Display tile count in the y-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_HEIGHT: usize = 20; | ||||
| 
 | ||||
| /// screen width in pixels
 | ||||
| /// Display width in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | ||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||
| 
 | ||||
| /// screen height in pixels
 | ||||
| /// Display height in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
 | ||||
| /// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | ||||
| 
 | ||||
| /// pixel count on whole screen
 | ||||
| pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use std::time::Instant;
 | ||||
| /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, PixelGrid};
 | ||||
| /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
| /// #     .expect("connection failed");
 | ||||
| /// # let pixels = PixelGrid::max_sized();
 | ||||
| /// loop {
 | ||||
| ///    let start = Instant::now();
 | ||||
| ///
 | ||||
| ///    // Change pixels here
 | ||||
| ///
 | ||||
| ///    connection.send(Command::BitmapLinearWin(
 | ||||
| ///            Origin::new(0,0),
 | ||||
| ///            pixels,
 | ||||
| ///            CompressionCode::Lzma
 | ||||
| ///        ))
 | ||||
| ///        .expect("send failed");
 | ||||
| ///
 | ||||
| ///    // warning: will crash if resulting duration is negative, e.g. when resuming from standby
 | ||||
| ///    std::thread::sleep(FRAME_PACING - start.elapsed());
 | ||||
| ///    # break; // prevent doctest from hanging
 | ||||
| /// }
 | ||||
| /// ```
 | ||||
| pub const FRAME_PACING: Duration = Duration::from_millis(30); | ||||
| 
 | ||||
| // include README.md in doctest
 | ||||
|  |  | |||
							
								
								
									
										48
									
								
								crates/servicepoint/src/origin.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								crates/servicepoint/src/origin.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| use std::marker::PhantomData; | ||||
| 
 | ||||
| /// An origin marks the top left position of a window sent to the display.
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq)] | ||||
| pub struct Origin<Unit: DisplayUnit> { | ||||
|     /// position in the width direction
 | ||||
|     pub x: usize, | ||||
|     /// position in the height direction
 | ||||
|     pub y: usize, | ||||
|     phantom_data: PhantomData<Unit>, | ||||
| } | ||||
| 
 | ||||
| impl<Unit: DisplayUnit> Origin<Unit> { | ||||
|     /// Create a new `Origin` instance for the provided position.
 | ||||
|     pub fn new(x: usize, y: usize) -> Self { | ||||
|         Self { | ||||
|             x, | ||||
|             y, | ||||
|             phantom_data: PhantomData, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: DisplayUnit> std::ops::Add<Origin<T>> for Origin<T> { | ||||
|     type Output = Origin<T>; | ||||
| 
 | ||||
|     fn add(self, rhs: Origin<T>) -> Self::Output { | ||||
|         Origin { | ||||
|             x: self.x + rhs.x, | ||||
|             y: self.y + rhs.y, | ||||
|             phantom_data: PhantomData, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait DisplayUnit {} | ||||
| 
 | ||||
| /// Marks something to be measured in number of pixels.
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq)] | ||||
| pub struct Pixels(); | ||||
| 
 | ||||
| /// Marks something to be measured in number of iles.
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq)] | ||||
| pub struct Tiles(); | ||||
| 
 | ||||
| impl DisplayUnit for Pixels {} | ||||
| 
 | ||||
| impl DisplayUnit for Tiles {} | ||||
|  | @ -1,5 +1,12 @@ | |||
| use std::mem::size_of; | ||||
| 
 | ||||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::into_compressed; | ||||
| use crate::{ | ||||
|     Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, | ||||
|     TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
| /// A raw header. Should probably not be used directly.
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16); | ||||
|  | @ -58,6 +65,151 @@ impl TryFrom<&[u8]> for Packet { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Command> for Packet { | ||||
|     /// Move the `Command` into a `Packet` instance for sending.
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn from(value: Command) -> Self { | ||||
|         match value { | ||||
|             Command::Clear => Self::command_code_only(CommandCode::Clear), | ||||
|             Command::FadeOut => Self::command_code_only(CommandCode::FadeOut), | ||||
|             Command::HardReset => { | ||||
|                 Self::command_code_only(CommandCode::HardReset) | ||||
|             } | ||||
|             #[allow(deprecated)] | ||||
|             Command::BitmapLegacy => { | ||||
|                 Self::command_code_only(CommandCode::BitmapLegacy) | ||||
|             } | ||||
|             Command::CharBrightness(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::CharBrightness.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|             Command::Brightness(brightness) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Brightness.into(), | ||||
|                     0x00000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                 ), | ||||
|                 vec![brightness.into()], | ||||
|             ), | ||||
|             Command::BitmapLinearWin(origin, pixels, compression) => { | ||||
|                 Self::bitmap_win_into_packet(origin, pixels, compression) | ||||
|             } | ||||
|             Command::BitmapLinear(offset, bits, compression) => { | ||||
|                 Self::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinear, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearAnd(offset, bits, compression) => { | ||||
|                 Self::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearAnd, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearOr(offset, bits, compression) => { | ||||
|                 Self::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearOr, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::BitmapLinearXor(offset, bits, compression) => { | ||||
|                 Self::bitmap_linear_into_packet( | ||||
|                     CommandCode::BitmapLinearXor, | ||||
|                     offset, | ||||
|                     compression, | ||||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::Cp437Data(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Cp437Data.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Packet { | ||||
|     /// Helper method for `BitMapLinear*`-Commands into `Packet`
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn bitmap_linear_into_packet( | ||||
|         command: CommandCode, | ||||
|         offset: Offset, | ||||
|         compression: CompressionCode, | ||||
|         payload: Vec<u8>, | ||||
|     ) -> Packet { | ||||
|         let length = payload.len() as u16; | ||||
|         let payload = into_compressed(compression, payload); | ||||
|         Packet( | ||||
|             Header( | ||||
|                 command.into(), | ||||
|                 offset as u16, | ||||
|                 length, | ||||
|                 compression.into(), | ||||
|                 0, | ||||
|             ), | ||||
|             payload, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|     fn bitmap_win_into_packet( | ||||
|         origin: Origin<Pixels>, | ||||
|         pixels: PixelGrid, | ||||
|         compression: CompressionCode, | ||||
|     ) -> Packet { | ||||
|         debug_assert_eq!(origin.x % 8, 0); | ||||
|         debug_assert_eq!(pixels.width() % 8, 0); | ||||
| 
 | ||||
|         let tile_x = (origin.x / TILE_SIZE) as u16; | ||||
|         let tile_w = (pixels.width() / TILE_SIZE) as u16; | ||||
|         let pixel_h = pixels.height() as u16; | ||||
|         let payload = into_compressed(compression, pixels.into()); | ||||
|         let command = match compression { | ||||
|             CompressionCode::Uncompressed => { | ||||
|                 CommandCode::BitmapLinearWinUncompressed | ||||
|             } | ||||
|             #[cfg(feature = "compression_zlib")] | ||||
|             CompressionCode::Zlib => CommandCode::BitmapLinearWinZlib, | ||||
|             #[cfg(feature = "compression_bzip2")] | ||||
|             CompressionCode::Bzip2 => CommandCode::BitmapLinearWinBzip2, | ||||
|             #[cfg(feature = "compression_lzma")] | ||||
|             CompressionCode::Lzma => CommandCode::BitmapLinearWinLzma, | ||||
|             #[cfg(feature = "compression_zstd")] | ||||
|             CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, | ||||
|         }; | ||||
| 
 | ||||
|         Packet( | ||||
|             Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h), | ||||
|             payload, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for creating empty packets only containing the command code
 | ||||
|     fn command_code_only(code: CommandCode) -> Packet { | ||||
|         Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![]) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::{Header, Packet}; | ||||
|  |  | |||
|  | @ -158,7 +158,7 @@ impl Grid<bool> for PixelGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl DataRef for PixelGrid { | ||||
| impl DataRef<u8> for PixelGrid { | ||||
|     fn data_ref_mut(&mut self) -> &mut [u8] { | ||||
|         self.bit_vec.as_raw_mut_slice() | ||||
|     } | ||||
|  |  | |||
|  | @ -2,40 +2,43 @@ use std::slice::{Iter, IterMut}; | |||
| 
 | ||||
| use crate::{DataRef, Grid}; | ||||
| 
 | ||||
| pub trait PrimitiveGridType: Sized + Default + Copy + Clone {} | ||||
| impl<T: Sized + Default + Copy + Clone> PrimitiveGridType for T {} | ||||
| 
 | ||||
| /// A 2D grid of bytes
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct ByteGrid { | ||||
| pub struct PrimitiveGrid<T: PrimitiveGridType> { | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: Vec<u8>, | ||||
|     data: Vec<T>, | ||||
| } | ||||
| 
 | ||||
| impl ByteGrid { | ||||
|     /// Creates a new `ByteGrid` with the specified dimensions.
 | ||||
| impl<T: PrimitiveGridType> PrimitiveGrid<T> { | ||||
|     /// Creates a new `PrimitiveGrid` with the specified dimensions.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// - width: size in x-direction
 | ||||
|     /// - height: size in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: `ByteGrid` initialized to 0.
 | ||||
|     /// returns: `PrimitiveGrid` initialized to default value.
 | ||||
|     pub fn new(width: usize, height: usize) -> Self { | ||||
|         Self { | ||||
|             data: vec![0; width * height], | ||||
|             data: vec![Default::default(); width * height], | ||||
|             width, | ||||
|             height, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Loads a `ByteGrid` with the specified dimensions from the provided data.
 | ||||
|     /// Loads a `PrimitiveGrid` with the specified dimensions from the provided data.
 | ||||
|     ///
 | ||||
|     /// returns: `ByteGrid` that contains a copy of the provided data
 | ||||
|     /// returns: `PrimitiveGrid` that contains a copy of the provided data
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// - when the dimensions and data size do not match exactly.
 | ||||
|     #[must_use] | ||||
|     pub fn load(width: usize, height: usize, data: &[u8]) -> Self { | ||||
|     pub fn load(width: usize, height: usize, data: &[T]) -> Self { | ||||
|         assert_eq!(width * height, data.len()); | ||||
|         Self { | ||||
|             data: Vec::from(data), | ||||
|  | @ -44,24 +47,24 @@ impl ByteGrid { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all cells in `ByteGrid`.
 | ||||
|     /// Iterate over all cells in `PrimitiveGrid`.
 | ||||
|     ///
 | ||||
|     /// Order is equivalent to the following loop:
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::{ByteGrid, Grid};
 | ||||
|     /// # let grid = ByteGrid::new(2,2);
 | ||||
|     /// # use servicepoint::{PrimitiveGrid, Grid};
 | ||||
|     /// # let grid = PrimitiveGrid::<u8>::new(2,2);
 | ||||
|     /// for y in 0..grid.height() {
 | ||||
|     ///     for x in 0..grid.width() {
 | ||||
|     ///         grid.get(x, y);
 | ||||
|     ///     }
 | ||||
|     /// }
 | ||||
|     /// ```
 | ||||
|     pub fn iter(&self) -> Iter<u8> { | ||||
|     pub fn iter(&self) -> Iter<T> { | ||||
|         self.data.iter() | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all rows in `ByteGrid` top to bottom.
 | ||||
|     pub fn iter_rows(&self) -> IterRows { | ||||
|     /// Iterate over all rows in `PrimitiveGrid` top to bottom.
 | ||||
|     pub fn iter_rows(&self) -> IterRows<T> { | ||||
|         IterRows { | ||||
|             byte_grid: self, | ||||
|             row: 0, | ||||
|  | @ -71,7 +74,7 @@ impl ByteGrid { | |||
|     /// Returns an iterator that allows modifying each value.
 | ||||
|     ///
 | ||||
|     /// The iterator yields all cells from top left to bottom right.
 | ||||
|     pub fn iter_mut(&mut self) -> IterMut<u8> { | ||||
|     pub fn iter_mut(&mut self) -> IterMut<T> { | ||||
|         self.data.iter_mut() | ||||
|     } | ||||
| 
 | ||||
|  | @ -84,7 +87,7 @@ impl ByteGrid { | |||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// When accessing `x` or `y` out of bounds.
 | ||||
|     pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 { | ||||
|     pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut T { | ||||
|         self.assert_in_bounds(x, y); | ||||
|         &mut self.data[x + y * self.width] | ||||
|     } | ||||
|  | @ -100,7 +103,7 @@ impl ByteGrid { | |||
|         &mut self, | ||||
|         x: isize, | ||||
|         y: isize, | ||||
|     ) -> Option<&mut u8> { | ||||
|     ) -> Option<&mut T> { | ||||
|         if self.is_in_bounds(x, y) { | ||||
|             Some(&mut self.data[x as usize + y as usize * self.width]) | ||||
|         } else { | ||||
|  | @ -109,8 +112,8 @@ impl ByteGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Grid<u8> for ByteGrid { | ||||
|     /// Sets the value of the cell at the specified position in the `ByteGrid.
 | ||||
| impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | ||||
|     /// Sets the value of the cell at the specified position in the `PrimitiveGrid.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|  | @ -120,7 +123,7 @@ impl Grid<u8> for ByteGrid { | |||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// When accessing `x` or `y` out of bounds.
 | ||||
|     fn set(&mut self, x: usize, y: usize, value: u8) { | ||||
|     fn set(&mut self, x: usize, y: usize, value: T) { | ||||
|         self.assert_in_bounds(x, y); | ||||
|         self.data[x + y * self.width] = value; | ||||
|     } | ||||
|  | @ -134,12 +137,12 @@ impl Grid<u8> for ByteGrid { | |||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// When accessing `x` or `y` out of bounds.
 | ||||
|     fn get(&self, x: usize, y: usize) -> u8 { | ||||
|     fn get(&self, x: usize, y: usize) -> T { | ||||
|         self.assert_in_bounds(x, y); | ||||
|         self.data[x + y * self.width] | ||||
|     } | ||||
| 
 | ||||
|     fn fill(&mut self, value: u8) { | ||||
|     fn fill(&mut self, value: T) { | ||||
|         self.data.fill(value); | ||||
|     } | ||||
| 
 | ||||
|  | @ -152,32 +155,32 @@ impl Grid<u8> for ByteGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl DataRef for ByteGrid { | ||||
| impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> { | ||||
|     /// Get the underlying byte rows mutable
 | ||||
|     fn data_ref_mut(&mut self) -> &mut [u8] { | ||||
|     fn data_ref_mut(&mut self) -> &mut [T] { | ||||
|         self.data.as_mut_slice() | ||||
|     } | ||||
| 
 | ||||
|     /// Get the underlying byte rows read only
 | ||||
|     fn data_ref(&self) -> &[u8] { | ||||
|     fn data_ref(&self) -> &[T] { | ||||
|         self.data.as_slice() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<ByteGrid> for Vec<u8> { | ||||
| impl<T: PrimitiveGridType> From<PrimitiveGrid<T>> for Vec<T> { | ||||
|     /// Turn into the underlying `Vec<u8>` containing the rows of bytes.
 | ||||
|     fn from(value: ByteGrid) -> Self { | ||||
|     fn from(value: PrimitiveGrid<T>) -> Self { | ||||
|         value.data | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IterRows<'t> { | ||||
|     byte_grid: &'t ByteGrid, | ||||
| pub struct IterRows<'t, T: PrimitiveGridType> { | ||||
|     byte_grid: &'t PrimitiveGrid<T>, | ||||
|     row: usize, | ||||
| } | ||||
| 
 | ||||
| impl<'t> Iterator for IterRows<'t> { | ||||
|     type Item = Iter<'t, u8>; | ||||
| impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { | ||||
|     type Item = Iter<'t, T>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.row >= self.byte_grid.height { | ||||
|  | @ -194,11 +197,11 @@ impl<'t> Iterator for IterRows<'t> { | |||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::{ByteGrid, DataRef, Grid}; | ||||
|     use crate::{DataRef, Grid, PrimitiveGrid}; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn fill() { | ||||
|         let mut grid = ByteGrid::new(2, 2); | ||||
|         let mut grid = PrimitiveGrid::<usize>::new(2, 2); | ||||
|         assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]); | ||||
| 
 | ||||
|         grid.fill(42); | ||||
|  | @ -207,7 +210,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn get_set() { | ||||
|         let mut grid = ByteGrid::new(2, 2); | ||||
|         let mut grid = PrimitiveGrid::new(2, 2); | ||||
|         assert_eq!(grid.get(0, 0), 0); | ||||
|         assert_eq!(grid.get(1, 1), 0); | ||||
| 
 | ||||
|  | @ -222,7 +225,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn load() { | ||||
|         let mut grid = ByteGrid::new(2, 3); | ||||
|         let mut grid = PrimitiveGrid::new(2, 3); | ||||
|         for x in 0..grid.width { | ||||
|             for y in 0..grid.height { | ||||
|                 grid.set(x, y, (x + y) as u8); | ||||
|  | @ -233,13 +236,13 @@ mod tests { | |||
| 
 | ||||
|         let data: Vec<u8> = grid.into(); | ||||
| 
 | ||||
|         let grid = ByteGrid::load(2, 3, &data); | ||||
|         let grid = PrimitiveGrid::load(2, 3, &data); | ||||
|         assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn mut_data_ref() { | ||||
|         let mut vec = ByteGrid::new(2, 2); | ||||
|         let mut vec = PrimitiveGrid::new(2, 2); | ||||
| 
 | ||||
|         let data_ref = vec.data_ref_mut(); | ||||
|         data_ref.copy_from_slice(&[1, 2, 3, 4]); | ||||
|  | @ -250,7 +253,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter() { | ||||
|         let mut vec = ByteGrid::new(2, 2); | ||||
|         let mut vec = PrimitiveGrid::new(2, 2); | ||||
|         vec.set(1, 1, 5); | ||||
| 
 | ||||
|         let mut iter = vec.iter(); | ||||
|  | @ -262,7 +265,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter_mut() { | ||||
|         let mut vec = ByteGrid::new(2, 3); | ||||
|         let mut vec = PrimitiveGrid::new(2, 3); | ||||
|         for (index, cell) in vec.iter_mut().enumerate() { | ||||
|             *cell = index as u8; | ||||
|         } | ||||
|  | @ -272,7 +275,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter_rows() { | ||||
|         let vec = ByteGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); | ||||
|         let vec = PrimitiveGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); | ||||
|         for (y, row) in vec.iter_rows().enumerate() { | ||||
|             for (x, val) in row.enumerate() { | ||||
|                 assert_eq!(*val, (x + y) as u8); | ||||
|  | @ -283,20 +286,20 @@ mod tests { | |||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_x() { | ||||
|         let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         vec.set(2, 1, 5); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_y() { | ||||
|         let vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         vec.get(1, 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn ref_mut() { | ||||
|         let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
| 
 | ||||
|         let top_left = vec.get_ref_mut(0, 0); | ||||
|         *top_left += 5; | ||||
|  | @ -307,7 +310,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn optional() { | ||||
|         let mut grid = ByteGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut grid = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         grid.set_optional(0, 0, 5); | ||||
|         grid.set_optional(-1, 0, 8); | ||||
|         grid.set_optional(0, 8, 42); | ||||
|  | @ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] | |||
| cbindgen = "0.26.0" | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.6.0" | ||||
| version = "0.7.0" | ||||
| path = "../servicepoint" | ||||
| features = ["all_compressions"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,4 +33,4 @@ include = ["servicepoint"] | |||
| extra_bindings = ["servicepoint"] | ||||
| 
 | ||||
| [parse.expand] | ||||
| #all_features = true | ||||
| all_features = true | ||||
|  |  | |||
|  | @ -70,14 +70,18 @@ typedef uint16_t sp_CompressionCode; | |||
| #endif // __cplusplus
 | ||||
| 
 | ||||
| /**
 | ||||
|  * A fixed-size vector of bits | ||||
|  * A display brightness value, checked for correct value range | ||||
|  */ | ||||
| typedef struct sp_BitVec sp_BitVec; | ||||
| typedef struct sp_Brightness sp_Brightness; | ||||
| 
 | ||||
| /**
 | ||||
|  * A 2D grid of bytes | ||||
|  * Opaque struct needed for correct code generation. | ||||
|  */ | ||||
| typedef struct sp_ByteGrid sp_ByteGrid; | ||||
| typedef struct sp_CBitVec sp_CBitVec; | ||||
| 
 | ||||
| typedef struct sp_CBrightnessGrid sp_CBrightnessGrid; | ||||
| 
 | ||||
| typedef struct sp_CCp437Grid sp_CCp437Grid; | ||||
| 
 | ||||
| /**
 | ||||
|  * A command to send to the display. | ||||
|  | @ -99,6 +103,16 @@ typedef struct sp_Packet sp_Packet; | |||
|  */ | ||||
| typedef struct sp_PixelGrid sp_PixelGrid; | ||||
| 
 | ||||
| /**
 | ||||
|  * A 2D grid of bytes | ||||
|  */ | ||||
| typedef struct sp_PrimitiveGrid_Brightness sp_PrimitiveGrid_Brightness; | ||||
| 
 | ||||
| /**
 | ||||
|  * A 2D grid of bytes | ||||
|  */ | ||||
| typedef struct sp_PrimitiveGrid_u8 sp_PrimitiveGrid_u8; | ||||
| 
 | ||||
| /**
 | ||||
|  * Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. | ||||
|  * | ||||
|  | @ -127,9 +141,20 @@ typedef struct sp_CByteSlice { | |||
| typedef size_t sp_Offset; | ||||
| 
 | ||||
| /**
 | ||||
|  * Type alias for documenting the meaning of the u16 in enum values | ||||
|  * A grid containing brightness values. | ||||
|  */ | ||||
| typedef uint8_t sp_Brightness; | ||||
| typedef struct sp_PrimitiveGrid_Brightness sp_BrightnessGrid; | ||||
| 
 | ||||
| /**
 | ||||
|  * A grid containing codepage 437 characters. | ||||
|  * | ||||
|  * The encoding is currently not enforced. | ||||
|  */ | ||||
| typedef struct sp_PrimitiveGrid_u8 sp_Cp437Grid; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
|  | @ -147,7 +172,7 @@ extern "C" { | |||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_bit_vec_dealloc`. | ||||
|  */ | ||||
| struct sp_BitVec *sp_bit_vec_clone(const struct sp_BitVec *this_); | ||||
| struct sp_CBitVec *sp_bit_vec_clone(const struct sp_CBitVec *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a `BitVec`. | ||||
|  | @ -160,7 +185,7 @@ struct sp_BitVec *sp_bit_vec_clone(const struct sp_BitVec *this_); | |||
|  * - `this` is not used concurrently or after this call | ||||
|  * - `this` was not passed to another consuming function, e.g. to create a `Command` | ||||
|  */ | ||||
| void sp_bit_vec_dealloc(struct sp_BitVec *this_); | ||||
| void sp_bit_vec_dealloc(struct sp_CBitVec *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of all bits in the `BitVec`. | ||||
|  | @ -176,7 +201,7 @@ void sp_bit_vec_dealloc(struct sp_BitVec *this_); | |||
|  * - `this` points to a valid `BitVec` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_bit_vec_fill(struct sp_BitVec *this_, bool value); | ||||
| void sp_bit_vec_fill(struct sp_CBitVec *this_, bool value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the value of a bit from the `BitVec`. | ||||
|  | @ -199,7 +224,7 @@ void sp_bit_vec_fill(struct sp_BitVec *this_, bool value); | |||
|  * - `this` points to a valid `BitVec` | ||||
|  * - `this` is not written to concurrently | ||||
|  */ | ||||
| bool sp_bit_vec_get(const struct sp_BitVec *this_, size_t index); | ||||
| bool sp_bit_vec_get(const struct sp_CBitVec *this_, size_t index); | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns true if length is 0. | ||||
|  | @ -210,7 +235,7 @@ bool sp_bit_vec_get(const struct sp_BitVec *this_, size_t index); | |||
|  * | ||||
|  * - `this` points to a valid `BitVec` | ||||
|  */ | ||||
| bool sp_bit_vec_is_empty(const struct sp_BitVec *this_); | ||||
| bool sp_bit_vec_is_empty(const struct sp_CBitVec *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the length of the `BitVec` in bits. | ||||
|  | @ -221,7 +246,7 @@ bool sp_bit_vec_is_empty(const struct sp_BitVec *this_); | |||
|  * | ||||
|  * - `this` points to a valid `BitVec` | ||||
|  */ | ||||
| size_t sp_bit_vec_len(const struct sp_BitVec *this_); | ||||
| size_t sp_bit_vec_len(const struct sp_CBitVec *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Interpret the data as a series of bits and load then into a new `BitVec` instance. | ||||
|  | @ -235,8 +260,8 @@ size_t sp_bit_vec_len(const struct sp_BitVec *this_); | |||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_bit_vec_dealloc`. | ||||
|  */ | ||||
| struct sp_BitVec *sp_bit_vec_load(const uint8_t *data, | ||||
|                                   size_t data_length); | ||||
| struct sp_CBitVec *sp_bit_vec_load(const uint8_t *data, | ||||
|                                    size_t data_length); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new `BitVec` instance. | ||||
|  | @ -258,7 +283,7 @@ struct sp_BitVec *sp_bit_vec_load(const uint8_t *data, | |||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_bit_vec_dealloc`. | ||||
|  */ | ||||
| struct sp_BitVec *sp_bit_vec_new(size_t size); | ||||
| struct sp_CBitVec *sp_bit_vec_new(size_t size); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of a bit in the `BitVec`. | ||||
|  | @ -282,7 +307,7 @@ struct sp_BitVec *sp_bit_vec_new(size_t size); | |||
|  * - `this` points to a valid `BitVec` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| bool sp_bit_vec_set(struct sp_BitVec *this_, size_t index, bool value); | ||||
| void sp_bit_vec_set(struct sp_CBitVec *this_, size_t index, bool value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets an unsafe reference to the data of the `BitVec` instance. | ||||
|  | @ -295,37 +320,37 @@ bool sp_bit_vec_set(struct sp_BitVec *this_, size_t index, bool value); | |||
|  * - the returned memory range is never accessed after the passed `BitVec` has been freed | ||||
|  * - the returned memory range is never accessed concurrently, either via the `BitVec` or directly | ||||
|  */ | ||||
| struct sp_CByteSlice sp_bit_vec_unsafe_data_ref(struct sp_BitVec *this_); | ||||
| struct sp_CByteSlice sp_bit_vec_unsafe_data_ref(struct sp_CBitVec *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Clones a `ByteGrid`. | ||||
|  * Clones a `BrightnessGrid`. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  * - `this` is not written to concurrently | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_byte_grid_dealloc`. | ||||
|  *   by explicitly calling `sp_brightness_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_ByteGrid *sp_byte_grid_clone(const struct sp_ByteGrid *this_); | ||||
| struct sp_CBrightnessGrid *sp_brightness_grid_clone(const struct sp_CBrightnessGrid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a `ByteGrid`. | ||||
|  * Deallocates a `BrightnessGrid`. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  * - `this` is not used concurrently or after this call | ||||
|  * - `this` was not passed to another consuming function, e.g. to create a `Command` | ||||
|  */ | ||||
| void sp_byte_grid_dealloc(struct sp_ByteGrid *this_); | ||||
| void sp_brightness_grid_dealloc(struct sp_CBrightnessGrid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of all cells in the `ByteGrid`. | ||||
|  * Sets the value of all cells in the `BrightnessGrid`. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  | @ -336,10 +361,10 @@ void sp_byte_grid_dealloc(struct sp_ByteGrid *this_); | |||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_byte_grid_fill(struct sp_ByteGrid *this_, uint8_t value); | ||||
| void sp_brightness_grid_fill(struct sp_CBrightnessGrid *this_, uint8_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the current value at the specified position. | ||||
|  | @ -357,13 +382,15 @@ void sp_byte_grid_fill(struct sp_ByteGrid *this_, uint8_t value); | |||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  * - `this` is not written to concurrently | ||||
|  */ | ||||
| uint8_t sp_byte_grid_get(const struct sp_ByteGrid *this_, size_t x, size_t y); | ||||
| uint8_t sp_brightness_grid_get(const struct sp_CBrightnessGrid *this_, | ||||
|                                size_t x, | ||||
|                                size_t y); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the height of the `ByteGrid` instance. | ||||
|  * Gets the height of the `BrightnessGrid` instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  | @ -373,12 +400,12 @@ uint8_t sp_byte_grid_get(const struct sp_ByteGrid *this_, size_t x, size_t y); | |||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  */ | ||||
| size_t sp_byte_grid_height(const struct sp_ByteGrid *this_); | ||||
| size_t sp_brightness_grid_height(const struct sp_CBrightnessGrid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a `ByteGrid` with the specified dimensions from the provided data. | ||||
|  * Loads a `BrightnessGrid` with the specified dimensions from the provided data. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  | @ -391,30 +418,30 @@ size_t sp_byte_grid_height(const struct sp_ByteGrid *this_); | |||
|  * - `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_byte_grid_dealloc`. | ||||
|  *   by explicitly calling `sp_brightness_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_ByteGrid *sp_byte_grid_load(size_t width, | ||||
|                                       size_t height, | ||||
|                                       const uint8_t *data, | ||||
|                                       size_t data_length); | ||||
| struct sp_CBrightnessGrid *sp_brightness_grid_load(size_t width, | ||||
|                                                    size_t height, | ||||
|                                                    const uint8_t *data, | ||||
|                                                    size_t data_length); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new `ByteGrid` with the specified dimensions. | ||||
|  * Creates a new `BrightnessGrid` with the specified dimensions. | ||||
|  * | ||||
|  * returns: `ByteGrid` initialized to 0. | ||||
|  * returns: `BrightnessGrid` initialized to 0. | ||||
|  * | ||||
|  * # 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_byte_grid_dealloc`. | ||||
|  *   by explicitly calling `sp_brightness_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_ByteGrid *sp_byte_grid_new(size_t width, | ||||
|                                      size_t height); | ||||
| struct sp_CBrightnessGrid *sp_brightness_grid_new(size_t width, | ||||
|                                                   size_t height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of the specified position in the `ByteGrid`. | ||||
|  * Sets the value of the specified position in the `BrightnessGrid`. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  | @ -435,26 +462,26 @@ struct sp_ByteGrid *sp_byte_grid_new(size_t width, | |||
|  * - `this` points to a valid `BitVec` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_byte_grid_set(struct sp_ByteGrid *this_, | ||||
|                       size_t x, | ||||
|                       size_t y, | ||||
|                       uint8_t value); | ||||
| void sp_brightness_grid_set(struct sp_CBrightnessGrid *this_, | ||||
|                             size_t x, | ||||
|                             size_t y, | ||||
|                             uint8_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets an unsafe reference to the data of the `ByteGrid` instance. | ||||
|  * Gets an unsafe reference to the data of the `BrightnessGrid` instance. | ||||
|  * | ||||
|  * ## Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - the returned memory range is never accessed after the passed `ByteGrid` has been freed | ||||
|  * - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  * - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed | ||||
|  * - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly | ||||
|  */ | ||||
| struct sp_CByteSlice sp_byte_grid_unsafe_data_ref(struct sp_ByteGrid *this_); | ||||
| struct sp_CByteSlice sp_brightness_grid_unsafe_data_ref(struct sp_CBrightnessGrid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the width of the `ByteGrid` instance. | ||||
|  * Gets the width of the `BrightnessGrid` instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  | @ -464,9 +491,9 @@ struct sp_CByteSlice sp_byte_grid_unsafe_data_ref(struct sp_ByteGrid *this_); | |||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `ByteGrid` | ||||
|  * - `this` points to a valid `BrightnessGrid` | ||||
|  */ | ||||
| size_t sp_byte_grid_width(const struct sp_ByteGrid *this_); | ||||
| size_t sp_brightness_grid_width(const struct sp_CBrightnessGrid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocates a new `Command::BitmapLinear` instance. | ||||
|  | @ -483,7 +510,7 @@ size_t sp_byte_grid_width(const struct sp_ByteGrid *this_); | |||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct sp_Command *sp_command_bitmap_linear(sp_Offset offset, | ||||
|                                             struct sp_BitVec *bit_vec, | ||||
|                                             struct sp_CBitVec *bit_vec, | ||||
|                                             sp_CompressionCode compression); | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -501,7 +528,7 @@ struct sp_Command *sp_command_bitmap_linear(sp_Offset offset, | |||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset, | ||||
|                                                 struct sp_BitVec *bit_vec, | ||||
|                                                 struct sp_CBitVec *bit_vec, | ||||
|                                                 sp_CompressionCode compression); | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -519,7 +546,7 @@ struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset, | |||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct sp_Command *sp_command_bitmap_linear_or(sp_Offset offset, | ||||
|                                                struct sp_BitVec *bit_vec, | ||||
|                                                struct sp_CBitVec *bit_vec, | ||||
|                                                sp_CompressionCode compression); | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -556,11 +583,16 @@ struct sp_Command *sp_command_bitmap_linear_win(size_t x, | |||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset, | ||||
|                                                 struct sp_BitVec *bit_vec, | ||||
|                                                 struct sp_CBitVec *bit_vec, | ||||
|                                                 sp_CompressionCode compression); | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocates a new `Command::Brightness` instance. | ||||
|  * Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the | ||||
|  * same value. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - When the provided brightness value is out of range (0-11). | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  | @ -569,7 +601,7 @@ struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset, | |||
|  * - the returned `Command` instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_command_dealloc`. | ||||
|  */ | ||||
| struct sp_Command *sp_command_brightness(sp_Brightness brightness); | ||||
| struct sp_Command *sp_command_brightness(uint8_t brightness); | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocates a new `Command::CharBrightness` instance. | ||||
|  | @ -586,7 +618,7 @@ struct sp_Command *sp_command_brightness(sp_Brightness brightness); | |||
|  */ | ||||
| struct sp_Command *sp_command_char_brightness(size_t x, | ||||
|                                               size_t y, | ||||
|                                               struct sp_ByteGrid *byte_grid); | ||||
|                                               sp_BrightnessGrid *byte_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocates a new `Command::Clear` instance. | ||||
|  | @ -629,7 +661,7 @@ struct sp_Command *sp_command_clone(const struct sp_Command *original); | |||
|  */ | ||||
| struct sp_Command *sp_command_cp437_data(size_t x, | ||||
|                                          size_t y, | ||||
|                                          struct sp_ByteGrid *byte_grid); | ||||
|                                          sp_Cp437Grid *byte_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a `Command`. | ||||
|  | @ -732,6 +764,179 @@ struct sp_Connection *sp_connection_open(const char *host); | |||
| bool sp_connection_send(const struct sp_Connection *connection, | ||||
|                         struct sp_Packet *packet); | ||||
| 
 | ||||
| /**
 | ||||
|  * Clones a `Cp437Grid`. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  * - `this` is not written to concurrently | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_cp437_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_CCp437Grid *sp_cp437_grid_clone(const struct sp_CCp437Grid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a `Cp437Grid`. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  * - `this` is not used concurrently or after this call | ||||
|  * - `this` was not passed to another consuming function, e.g. to create a `Command` | ||||
|  */ | ||||
| void sp_cp437_grid_dealloc(struct sp_CCp437Grid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of all cells in the `Cp437Grid`. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `this`: instance to write to | ||||
|  * * `value`: the value to set all cells to | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_cp437_grid_fill(struct sp_CCp437Grid *this_, uint8_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the current value at the specified position. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `this`: 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: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  * - `this` is not written to concurrently | ||||
|  */ | ||||
| uint8_t sp_cp437_grid_get(const struct sp_CCp437Grid *this_, | ||||
|                           size_t x, | ||||
|                           size_t y); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the height of the `Cp437Grid` instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `this`: instance to read from | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  */ | ||||
| size_t sp_cp437_grid_height(const struct sp_CCp437Grid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a `Cp437Grid` with the specified dimensions from the provided data. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * When the provided `data_length` is not sufficient for the `height` and `width` | ||||
|  * | ||||
|  * # 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_cp437_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_CCp437Grid *sp_cp437_grid_load(size_t width, | ||||
|                                          size_t height, | ||||
|                                          const uint8_t *data, | ||||
|                                          size_t data_length); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new `Cp437Grid` with the specified dimensions. | ||||
|  * | ||||
|  * returns: `Cp437Grid` initialized to 0. | ||||
|  * | ||||
|  * # 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_cp437_grid_dealloc`. | ||||
|  */ | ||||
| struct sp_CCp437Grid *sp_cp437_grid_new(size_t width, | ||||
|                                         size_t height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of the specified position in the `Cp437Grid`. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `this`: 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: | ||||
|  * | ||||
|  * - `this` points to a valid `BitVec` | ||||
|  * - `this` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_cp437_grid_set(struct sp_CCp437Grid *this_, | ||||
|                        size_t x, | ||||
|                        size_t y, | ||||
|                        uint8_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets an unsafe reference to the data of the `Cp437Grid` instance. | ||||
|  * | ||||
|  * ## Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  * - the returned memory range is never accessed after the passed `Cp437Grid` has been freed | ||||
|  * - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly | ||||
|  */ | ||||
| struct sp_CByteSlice sp_cp437_grid_unsafe_data_ref(struct sp_CCp437Grid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the width of the `Cp437Grid` instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * * `this`: instance to read from | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `this` points to a valid `Cp437Grid` | ||||
|  */ | ||||
| size_t sp_cp437_grid_width(const struct sp_CCp437Grid *this_); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a `Packet`. | ||||
|  * | ||||
|  |  | |||
							
								
								
									
										233
									
								
								crates/servicepoint_binding_c/src/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								crates/servicepoint_binding_c/src/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,233 @@ | |||
| //! C functions for interacting with `BrightnessGrid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_brightness_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{Brightness, BrightnessGrid, DataRef, Grid, PrimitiveGrid}; | ||||
| use std::intrinsics::transmute; | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| 
 | ||||
| /// C-wrapper for grid containing brightness values.
 | ||||
| #[derive(Clone)] | ||||
| pub struct CBrightnessGrid(pub(crate) BrightnessGrid); | ||||
| 
 | ||||
| /// Creates a new `BrightnessGrid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `BrightnessGrid` initialized to 0.
 | ||||
| ///
 | ||||
| /// # 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_brightness_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut CBrightnessGrid { | ||||
|     Box::into_raw(Box::new(CBrightnessGrid(BrightnessGrid::new( | ||||
|         width, height, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `BrightnessGrid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// When the provided `data_length` is not sufficient for the `height` and `width`
 | ||||
| ///
 | ||||
| /// # 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_brightness_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut CBrightnessGrid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let grid = PrimitiveGrid::load(width, height, data); | ||||
|     let grid = | ||||
|         BrightnessGrid::try_from(grid).expect("invalid brightness value"); | ||||
|     Box::into_raw(Box::new(CBrightnessGrid(grid))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `BrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_clone( | ||||
|     this: *const CBrightnessGrid, | ||||
| ) -> *mut CBrightnessGrid { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `BrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_dealloc( | ||||
|     this: *mut CBrightnessGrid, | ||||
| ) { | ||||
|     _ = Box::from_raw(this); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: 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:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_get( | ||||
|     this: *const CBrightnessGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     (*this).0.get(x, y).into() | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `BrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: 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:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_set( | ||||
|     this: *mut CBrightnessGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*this).0.set(x, y, brightness); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `BrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_fill( | ||||
|     this: *mut CBrightnessGrid, | ||||
|     value: u8, | ||||
| ) { | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*this).0.fill(brightness); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `BrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_width( | ||||
|     this: *const CBrightnessGrid, | ||||
| ) -> usize { | ||||
|     (*this).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `BrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_height( | ||||
|     this: *const CBrightnessGrid, | ||||
| ) -> usize { | ||||
|     (*this).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `BrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | ||||
|     this: *mut CBrightnessGrid, | ||||
| ) -> CByteSlice { | ||||
|     assert_eq!(std::mem::size_of::<Brightness>(), 1); | ||||
| 
 | ||||
|     let data = (*this).0.data_ref_mut(); | ||||
|     let data: &mut [u8] = transmute(data); | ||||
|     CByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
| } | ||||
|  | @ -5,11 +5,12 @@ | |||
| use std::ptr::null_mut; | ||||
| 
 | ||||
| use servicepoint::{ | ||||
|     Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet, | ||||
|     PixelGrid, | ||||
|     Brightness, Command, CompressionCode, Offset, Origin, Packet, PixelGrid, | ||||
| }; | ||||
| 
 | ||||
| use crate::bit_vec::CBitVec; | ||||
| use crate::brightness_grid::CBrightnessGrid; | ||||
| use crate::cp437_grid::CCp437Grid; | ||||
| 
 | ||||
| /// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.
 | ||||
| ///
 | ||||
|  | @ -91,7 +92,12 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { | |||
|     Box::into_raw(Box::new(Command::FadeOut)) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::Brightness` instance.
 | ||||
| /// Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the
 | ||||
| /// same value.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - When the provided brightness value is out of range (0-11).
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -100,9 +106,9 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { | |||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_brightness( | ||||
|     brightness: Brightness, | ||||
| ) -> *mut Command { | ||||
| pub unsafe extern "C" fn sp_command_brightness(brightness: u8) -> *mut Command { | ||||
|     let brightness = | ||||
|         Brightness::try_from(brightness).expect("invalid brightness"); | ||||
|     Box::into_raw(Box::new(Command::Brightness(brightness))) | ||||
| } | ||||
| 
 | ||||
|  | @ -121,10 +127,13 @@ pub unsafe extern "C" fn sp_command_brightness( | |||
| pub unsafe extern "C" fn sp_command_char_brightness( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     byte_grid: *mut ByteGrid, | ||||
|     byte_grid: *mut CBrightnessGrid, | ||||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(byte_grid); | ||||
|     Box::into_raw(Box::new(Command::CharBrightness(Origin(x, y), byte_grid))) | ||||
|     Box::into_raw(Box::new(Command::CharBrightness( | ||||
|         Origin::new(x, y), | ||||
|         byte_grid.0, | ||||
|     ))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinear` instance.
 | ||||
|  | @ -246,10 +255,10 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | |||
| pub unsafe extern "C" fn sp_command_cp437_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     byte_grid: *mut ByteGrid, | ||||
|     byte_grid: *mut CCp437Grid, | ||||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(byte_grid); | ||||
|     Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid))) | ||||
|     Box::into_raw(Box::new(Command::Cp437Data(Origin::new(x, y), byte_grid.0))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinearWin` instance.
 | ||||
|  | @ -273,7 +282,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( | |||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(pixel_grid); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinearWin( | ||||
|         Origin(x, y), | ||||
|         Origin::new(x, y), | ||||
|         byte_grid, | ||||
|         compression_code, | ||||
|     ))) | ||||
|  |  | |||
|  | @ -1,30 +1,36 @@ | |||
| //! C functions for interacting with `ByteGrid`s
 | ||||
| //! C functions for interacting with `Cp437Grid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_byte_grid_`
 | ||||
| //! prefix `sp_cp437_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{ByteGrid, DataRef, Grid}; | ||||
| use servicepoint::{Cp437Grid, DataRef, Grid}; | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| 
 | ||||
| /// Creates a new `ByteGrid` with the specified dimensions.
 | ||||
| /// A C-wrapper for grid containing codepage 437 characters.
 | ||||
| ///
 | ||||
| /// returns: `ByteGrid` initialized to 0.
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| #[derive(Clone)] | ||||
| pub struct CCp437Grid(pub(crate) Cp437Grid); | ||||
| 
 | ||||
| /// Creates a new `Cp437Grid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `Cp437Grid` initialized to 0.
 | ||||
| ///
 | ||||
| /// # 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_byte_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_new( | ||||
| pub unsafe extern "C" fn sp_cp437_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut ByteGrid { | ||||
|     Box::into_raw(Box::new(ByteGrid::new(width, height))) | ||||
| ) -> *mut CCp437Grid { | ||||
|     Box::into_raw(Box::new(CCp437Grid(Cp437Grid::new(width, height)))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `ByteGrid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a `Cp437Grid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -37,46 +43,46 @@ pub unsafe extern "C" fn sp_byte_grid_new( | |||
| /// - `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_byte_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_load( | ||||
| pub unsafe extern "C" fn sp_cp437_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut ByteGrid { | ||||
| ) -> *mut CCp437Grid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(ByteGrid::load(width, height, data))) | ||||
|     Box::into_raw(Box::new(CCp437Grid(Cp437Grid::load(width, height, data)))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `ByteGrid`.
 | ||||
| /// Clones a `Cp437Grid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_byte_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_clone( | ||||
|     this: *const ByteGrid, | ||||
| ) -> *mut ByteGrid { | ||||
| pub unsafe extern "C" fn sp_cp437_grid_clone( | ||||
|     this: *const CCp437Grid, | ||||
| ) -> *mut CCp437Grid { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `ByteGrid`.
 | ||||
| /// Deallocates a `Cp437Grid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_dealloc(this: *mut ByteGrid) { | ||||
| pub unsafe extern "C" fn sp_cp437_grid_dealloc(this: *mut CCp437Grid) { | ||||
|     _ = Box::from_raw(this); | ||||
| } | ||||
| 
 | ||||
|  | @ -95,18 +101,18 @@ pub unsafe extern "C" fn sp_byte_grid_dealloc(this: *mut ByteGrid) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_get( | ||||
|     this: *const ByteGrid, | ||||
| pub unsafe extern "C" fn sp_cp437_grid_get( | ||||
|     this: *const CCp437Grid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     (*this).get(x, y) | ||||
|     (*this).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `ByteGrid`.
 | ||||
| /// Sets the value of the specified position in the `Cp437Grid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -127,16 +133,16 @@ pub unsafe extern "C" fn sp_byte_grid_get( | |||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_set( | ||||
|     this: *mut ByteGrid, | ||||
| pub unsafe extern "C" fn sp_cp437_grid_set( | ||||
|     this: *mut CCp437Grid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     (*this).set(x, y, value); | ||||
|     (*this).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `ByteGrid`.
 | ||||
| /// Sets the value of all cells in the `Cp437Grid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -147,14 +153,14 @@ pub unsafe extern "C" fn sp_byte_grid_set( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) { | ||||
|     (*this).fill(value); | ||||
| pub unsafe extern "C" fn sp_cp437_grid_fill(this: *mut CCp437Grid, value: u8) { | ||||
|     (*this).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `ByteGrid` instance.
 | ||||
| /// Gets the width of the `Cp437Grid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -164,13 +170,13 @@ pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize { | ||||
|     (*this).width() | ||||
| pub unsafe extern "C" fn sp_cp437_grid_width(this: *const CCp437Grid) -> usize { | ||||
|     (*this).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `ByteGrid` instance.
 | ||||
| /// Gets the height of the `Cp437Grid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
|  | @ -180,26 +186,28 @@ pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_height(this: *const ByteGrid) -> usize { | ||||
|     (*this).height() | ||||
| pub unsafe extern "C" fn sp_cp437_grid_height( | ||||
|     this: *const CCp437Grid, | ||||
| ) -> usize { | ||||
|     (*this).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `ByteGrid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the `Cp437Grid` instance.
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `ByteGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `ByteGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - the returned memory range is never accessed after the passed `Cp437Grid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_byte_grid_unsafe_data_ref( | ||||
|     this: *mut ByteGrid, | ||||
| pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | ||||
|     this: *mut CCp437Grid, | ||||
| ) -> CByteSlice { | ||||
|     let data = (*this).data_ref_mut(); | ||||
|     let data = (*this).0.data_ref_mut(); | ||||
|     CByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|  | @ -9,7 +9,7 @@ pub use crate::c_slice::CByteSlice; | |||
| 
 | ||||
| pub mod bit_vec; | ||||
| 
 | ||||
| pub mod byte_grid; | ||||
| pub mod brightness_grid; | ||||
| 
 | ||||
| pub mod command; | ||||
| 
 | ||||
|  | @ -19,7 +19,9 @@ pub mod packet; | |||
| 
 | ||||
| pub mod pixel_grid; | ||||
| 
 | ||||
| pub mod c_slice; | ||||
| 
 | ||||
| pub mod cp437_grid; | ||||
| 
 | ||||
| /// The minimum time needed for the display to refresh the screen in ms.
 | ||||
| pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32; | ||||
| 
 | ||||
| pub mod c_slice; | ||||
|  |  | |||
|  | @ -13,8 +13,8 @@ test = false | |||
| csbindgen = "1.8.0" | ||||
| 
 | ||||
| [dependencies] | ||||
| servicepoint_binding_c = { version = "0.6.0", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.6.0", path = "../servicepoint" } | ||||
| servicepoint_binding_c = { version = "0.7.0", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.7.0", path = "../servicepoint" } | ||||
| 
 | ||||
| [lints] | ||||
| workspace = true | ||||
|  |  | |||
|  | @ -61,45 +61,85 @@ namespace ServicePoint.BindGen | |||
|         [DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CByteSlice sp_bit_vec_unsafe_data_ref(CBitVec* @this); | ||||
| 
 | ||||
|         /// <summary>Creates a new `ByteGrid` with the specified dimensions.  returns: `ByteGrid` initialized to 0.  # 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_byte_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern ByteGrid* sp_byte_grid_new(nuint width, nuint height); | ||||
|         /// <summary>Creates a new `BrightnessGrid` with the specified dimensions.  returns: `BrightnessGrid` initialized to 0.  # 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_brightness_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CBrightnessGrid* sp_brightness_grid_new(nuint width, nuint height); | ||||
| 
 | ||||
|         /// <summary>Loads a `ByteGrid` with the specified dimensions from the provided data.  # Panics  When the provided `data_length` is not sufficient for the `height` and `width`  # 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_byte_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern ByteGrid* sp_byte_grid_load(nuint width, nuint height, byte* data, nuint data_length); | ||||
|         /// <summary>Loads a `BrightnessGrid` with the specified dimensions from the provided data.  # Panics  When the provided `data_length` is not sufficient for the `height` and `width`  # 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_brightness_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CBrightnessGrid* sp_brightness_grid_load(nuint width, nuint height, byte* data, nuint data_length); | ||||
| 
 | ||||
|         /// <summary>Clones a `ByteGrid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern ByteGrid* sp_byte_grid_clone(ByteGrid* @this); | ||||
|         /// <summary>Clones a `BrightnessGrid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_brightness_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CBrightnessGrid* sp_brightness_grid_clone(CBrightnessGrid* @this); | ||||
| 
 | ||||
|         /// <summary>Deallocates a `ByteGrid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_byte_grid_dealloc(ByteGrid* @this); | ||||
|         /// <summary>Deallocates a `BrightnessGrid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_brightness_grid_dealloc(CBrightnessGrid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets the current value at the specified position.  # Arguments  * `this`: 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:  - `this` points to a valid `ByteGrid` - `this` is not written to concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern byte sp_byte_grid_get(ByteGrid* @this, nuint x, nuint y); | ||||
|         /// <summary>Gets the current value at the specified position.  # Arguments  * `this`: 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:  - `this` points to a valid `BrightnessGrid` - `this` is not written to concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern byte sp_brightness_grid_get(CBrightnessGrid* @this, nuint x, nuint y); | ||||
| 
 | ||||
|         /// <summary>Sets the value of the specified position in the `ByteGrid`.  # Arguments  * `this`: 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:  - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_byte_grid_set(ByteGrid* @this, nuint x, nuint y, byte value); | ||||
|         /// <summary>Sets the value of the specified position in the `BrightnessGrid`.  # Arguments  * `this`: 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:  - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_brightness_grid_set(CBrightnessGrid* @this, nuint x, nuint y, byte value); | ||||
| 
 | ||||
|         /// <summary>Sets the value of all cells in the `ByteGrid`.  # Arguments  * `this`: instance to write to * `value`: the value to set all cells to  # Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_byte_grid_fill(ByteGrid* @this, byte value); | ||||
|         /// <summary>Sets the value of all cells in the `BrightnessGrid`.  # Arguments  * `this`: instance to write to * `value`: the value to set all cells to  # Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_brightness_grid_fill(CBrightnessGrid* @this, byte value); | ||||
| 
 | ||||
|         /// <summary>Gets the width of the `ByteGrid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_byte_grid_width(ByteGrid* @this); | ||||
|         /// <summary>Gets the width of the `BrightnessGrid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_brightness_grid_width(CBrightnessGrid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets the height of the `ByteGrid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_byte_grid_height(ByteGrid* @this); | ||||
|         /// <summary>Gets the height of the `BrightnessGrid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_brightness_grid_height(CBrightnessGrid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets an unsafe reference to the data of the `ByteGrid` instance.  ## Safety  The caller has to make sure that:  - `this` points to a valid `ByteGrid` - the returned memory range is never accessed after the passed `ByteGrid` has been freed - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_byte_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CByteSlice sp_byte_grid_unsafe_data_ref(ByteGrid* @this); | ||||
|         /// <summary>Gets an unsafe reference to the data of the `BrightnessGrid` instance.  ## Safety  The caller has to make sure that:  - `this` points to a valid `BrightnessGrid` - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_brightness_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CByteSlice sp_brightness_grid_unsafe_data_ref(CBrightnessGrid* @this); | ||||
| 
 | ||||
|         /// <summary>Creates a new `Cp437Grid` with the specified dimensions.  returns: `Cp437Grid` initialized to 0.  # 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_cp437_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CCp437Grid* sp_cp437_grid_new(nuint width, nuint height); | ||||
| 
 | ||||
|         /// <summary>Loads a `Cp437Grid` with the specified dimensions from the provided data.  # Panics  When the provided `data_length` is not sufficient for the `height` and `width`  # 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_cp437_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CCp437Grid* sp_cp437_grid_load(nuint width, nuint height, byte* data, nuint data_length); | ||||
| 
 | ||||
|         /// <summary>Clones a `Cp437Grid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_cp437_grid_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CCp437Grid* sp_cp437_grid_clone(CCp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary>Deallocates a `Cp437Grid`.  # Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_cp437_grid_dealloc(CCp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets the current value at the specified position.  # Arguments  * `this`: 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:  - `this` points to a valid `Cp437Grid` - `this` is not written to concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern byte sp_cp437_grid_get(CCp437Grid* @this, nuint x, nuint y); | ||||
| 
 | ||||
|         /// <summary>Sets the value of the specified position in the `Cp437Grid`.  # Arguments  * `this`: 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:  - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_cp437_grid_set(CCp437Grid* @this, nuint x, nuint y, byte value); | ||||
| 
 | ||||
|         /// <summary>Sets the value of all cells in the `Cp437Grid`.  # Arguments  * `this`: instance to write to * `value`: the value to set all cells to  # Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid` - `this` is not written to or read from concurrently</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern void sp_cp437_grid_fill(CCp437Grid* @this, byte value); | ||||
| 
 | ||||
|         /// <summary>Gets the width of the `Cp437Grid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_cp437_grid_width(CCp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets the height of the `Cp437Grid` instance.  # Arguments  * `this`: instance to read from  # Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid`</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern nuint sp_cp437_grid_height(CCp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary>Gets an unsafe reference to the data of the `Cp437Grid` instance.  ## Safety  The caller has to make sure that:  - `this` points to a valid `Cp437Grid` - the returned memory range is never accessed after the passed `Cp437Grid` has been freed - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_cp437_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern CByteSlice sp_cp437_grid_unsafe_data_ref(CCp437Grid* @this); | ||||
| 
 | ||||
|         /// <summary>Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.  Returns: pointer to new `Command` instance or NULL  # Safety  The caller has to make sure that:  - `packet` points to a valid instance of `Packet` - `packet` is not used concurrently or after this call - the result is checked for NULL - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|  | @ -121,13 +161,13 @@ namespace ServicePoint.BindGen | |||
|         [DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern Command* sp_command_fade_out(); | ||||
| 
 | ||||
|         /// <summary>Allocates a new `Command::Brightness` instance.  # Safety  The caller has to make sure that:  - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         /// <summary>Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the same value.  # Panics  - When the provided brightness value is out of range (0-11).  # Safety  The caller has to make sure that:  - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern Command* sp_command_brightness(byte brightness); | ||||
| 
 | ||||
|         /// <summary>Allocates a new `Command::CharBrightness` instance. The passed `ByteGrid` gets consumed.  # Safety  The caller has to make sure that:  - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_char_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern Command* sp_command_char_brightness(nuint x, nuint y, ByteGrid* byte_grid); | ||||
|         public static extern Command* sp_command_char_brightness(nuint x, nuint y, CBrightnessGrid* byte_grid); | ||||
| 
 | ||||
|         /// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets consumed.  # Safety  The caller has to make sure that:  - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|  | @ -147,7 +187,7 @@ namespace ServicePoint.BindGen | |||
| 
 | ||||
|         /// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets consumed.  # Safety  The caller has to make sure that:  - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|         public static extern Command* sp_command_cp437_data(nuint x, nuint y, ByteGrid* byte_grid); | ||||
|         public static extern Command* sp_command_cp437_data(nuint x, nuint y, CCp437Grid* byte_grid); | ||||
| 
 | ||||
|         /// <summary>Allocates a new `Command::BitmapLinearWin` instance. The passed `PixelGrid` gets consumed.  # Safety  The caller has to make sure that:  - `pixel_grid` points to a valid instance of `PixelGrid` - `pixel_grid` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> | ||||
|         [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_win", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] | ||||
|  | @ -231,6 +271,16 @@ namespace ServicePoint.BindGen | |||
|     { | ||||
|     } | ||||
| 
 | ||||
|     [StructLayout(LayoutKind.Sequential)] | ||||
|     public unsafe partial struct CBrightnessGrid | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     [StructLayout(LayoutKind.Sequential)] | ||||
|     public unsafe partial struct CCp437Grid | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     [StructLayout(LayoutKind.Sequential)] | ||||
|     public unsafe partial struct CByteSlice | ||||
|     { | ||||
|  | @ -238,11 +288,6 @@ namespace ServicePoint.BindGen | |||
|         public nuint length; | ||||
|     } | ||||
| 
 | ||||
|     [StructLayout(LayoutKind.Sequential)] | ||||
|     public unsafe partial struct ByteGrid | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     [StructLayout(LayoutKind.Sequential)] | ||||
|     public unsafe partial struct Connection | ||||
|     { | ||||
|  | @ -262,17 +307,17 @@ namespace ServicePoint.BindGen | |||
|     public enum Command | ||||
|     { | ||||
|         Clear, | ||||
|         HardReset, | ||||
|         FadeOut, | ||||
|         CharBrightness, | ||||
|         Cp437Data, | ||||
|         BitmapLinearWin, | ||||
|         Brightness, | ||||
|         BitmapLegacy, | ||||
|         CharBrightness, | ||||
|         BitmapLinear, | ||||
|         BitmapLinearAnd, | ||||
|         BitmapLinearOr, | ||||
|         BitmapLinearXor, | ||||
|         Cp437Data, | ||||
|         BitmapLinearWin, | ||||
|         HardReset, | ||||
|         FadeOut, | ||||
|         BitmapLegacy, | ||||
|     } | ||||
| 
 | ||||
|     public enum CompressionCode : ushort | ||||
|  |  | |||
							
								
								
									
										103
									
								
								crates/servicepoint_binding_cs/ServicePoint/BrightnessGrid.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								crates/servicepoint_binding_cs/ServicePoint/BrightnessGrid.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | |||
| using ServicePoint.BindGen; | ||||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class BrightnessGrid : SpNativeInstance<BindGen.CBrightnessGrid> | ||||
| { | ||||
|     public static BrightnessGrid New(int width, int height) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new BrightnessGrid(NativeMethods.sp_brightness_grid_new((nuint)width, (nuint)height)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static BrightnessGrid Load(int width, int height, Span<byte> bytes) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             fixed (byte* bytesPtr = bytes) | ||||
|             { | ||||
|                 return new BrightnessGrid(NativeMethods.sp_brightness_grid_load((nuint)width, (nuint)height, bytesPtr, | ||||
|                     (nuint)bytes.Length)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public BrightnessGrid Clone() | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new BrightnessGrid(NativeMethods.sp_brightness_grid_clone(Instance)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public byte this[int x, int y] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return NativeMethods.sp_brightness_grid_get(Instance, (nuint)x, (nuint)y); | ||||
|             } | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 NativeMethods.sp_brightness_grid_set(Instance, (nuint)x, (nuint)y, value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void Fill(byte value) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             NativeMethods.sp_brightness_grid_fill(Instance, value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public int Width | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_brightness_grid_width(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public int Height | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_brightness_grid_height(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public Span<byte> Data | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 var slice = NativeMethods.sp_brightness_grid_unsafe_data_ref(Instance); | ||||
|                 return new Span<byte>(slice.start, (int)slice.length); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe BrightnessGrid(BindGen.CBrightnessGrid* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_brightness_grid_dealloc(Instance); | ||||
|     } | ||||
| } | ||||
|  | @ -61,7 +61,7 @@ public sealed class Command : SpNativeInstance<BindGen.Command> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Command CharBrightness(int x, int y, ByteGrid grid) | ||||
|     public static Command CharBrightness(int x, int y, BrightnessGrid grid) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|  | @ -113,7 +113,7 @@ public sealed class Command : SpNativeInstance<BindGen.Command> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Command Cp437Data(int x, int y, ByteGrid byteGrid) | ||||
|     public static Command Cp437Data(int x, int y, Cp437Grid byteGrid) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|  |  | |||
|  | @ -3,33 +3,33 @@ using ServicePoint.BindGen; | |||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | ||||
| public sealed class Cp437Grid : SpNativeInstance<BindGen.CCp437Grid> | ||||
| { | ||||
|     public static ByteGrid New(int width, int height) | ||||
|     public static Cp437Grid New(int width, int height) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new ByteGrid(NativeMethods.sp_byte_grid_new((nuint)width, (nuint)height)); | ||||
|             return new Cp437Grid(NativeMethods.sp_cp437_grid_new((nuint)width, (nuint)height)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static ByteGrid Load(int width, int height, Span<byte> bytes) | ||||
|     public static Cp437Grid Load(int width, int height, Span<byte> bytes) | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             fixed (byte* bytesPtr = bytes) | ||||
|             { | ||||
|                 return new ByteGrid(NativeMethods.sp_byte_grid_load((nuint)width, (nuint)height, bytesPtr, | ||||
|                 return new Cp437Grid(NativeMethods.sp_cp437_grid_load((nuint)width, (nuint)height, bytesPtr, | ||||
|                     (nuint)bytes.Length)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public ByteGrid Clone() | ||||
|     public Cp437Grid Clone() | ||||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return new ByteGrid(NativeMethods.sp_byte_grid_clone(Instance)); | ||||
|             return new Cp437Grid(NativeMethods.sp_cp437_grid_clone(Instance)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -39,14 +39,14 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return NativeMethods.sp_byte_grid_get(Instance, (nuint)x, (nuint)y); | ||||
|                 return NativeMethods.sp_cp437_grid_get(Instance, (nuint)x, (nuint)y); | ||||
|             } | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 NativeMethods.sp_byte_grid_set(Instance, (nuint)x, (nuint)y, value); | ||||
|                 NativeMethods.sp_cp437_grid_set(Instance, (nuint)x, (nuint)y, value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -85,7 +85,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             NativeMethods.sp_byte_grid_fill(Instance, value); | ||||
|             NativeMethods.sp_cp437_grid_fill(Instance, value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -95,7 +95,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_byte_grid_width(Instance); | ||||
|                 return (int)NativeMethods.sp_cp437_grid_width(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -106,7 +106,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 return (int)NativeMethods.sp_byte_grid_height(Instance); | ||||
|                 return (int)NativeMethods.sp_cp437_grid_height(Instance); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -117,18 +117,18 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> | |||
|         { | ||||
|             unsafe | ||||
|             { | ||||
|                 var slice = NativeMethods.sp_byte_grid_unsafe_data_ref(Instance); | ||||
|                 var slice = NativeMethods.sp_cp437_grid_unsafe_data_ref(Instance); | ||||
|                 return new Span<byte>(slice.start, (int)slice.length); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe ByteGrid(BindGen.ByteGrid* instance) : base(instance) | ||||
|     private unsafe Cp437Grid(BindGen.CCp437Grid* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_byte_grid_dealloc(Instance); | ||||
|         NativeMethods.sp_cp437_grid_dealloc(Instance); | ||||
|     } | ||||
| } | ||||
|  | @ -7,12 +7,11 @@ | |||
|         <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
| 
 | ||||
|         <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck> | ||||
|         <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
|     </PropertyGroup> | ||||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <PackageId>ServicePoint</PackageId> | ||||
|         <Version>0.6.0</Version> | ||||
|         <Version>0.7.0</Version> | ||||
|         <Authors>Repository Authors</Authors> | ||||
|         <Company>None</Company> | ||||
|         <Product>ServicePoint</Product> | ||||
|  | @ -35,7 +34,7 @@ | |||
|         <Exec Command="cargo build"/> | ||||
|         <Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/> | ||||
|     </Target> | ||||
|      | ||||
| 
 | ||||
|     <!-- include native binary in output --> | ||||
|     <ItemGroup Condition="'$(Configuration)'=='Debug'"> | ||||
|         <Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always"> | ||||
|  |  | |||
|  | @ -5,14 +5,14 @@ fn main() { | |||
|     println!("cargo::rerun-if-changed=build.rs"); | ||||
|     csbindgen::Builder::default() | ||||
|         .input_extern_file("../servicepoint_binding_c/src/bit_vec.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/byte_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/brightness_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/cp437_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/command.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/connection.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/pixel_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/lib.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/c_slice.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/packet.rs") | ||||
|         .input_extern_file("../servicepoint/src/byte_grid.rs") | ||||
|         .input_extern_file("../servicepoint/src/command.rs") | ||||
|         .input_extern_file("../servicepoint/src/connection.rs") | ||||
|         .input_extern_file("../servicepoint/src/pixel_grid.rs") | ||||
|  |  | |||
|  | @ -1,11 +1,16 @@ | |||
| {pkgs ? import <nixpkgs> {}}: | ||||
| pkgs.mkShell { | ||||
|   nativeBuildInputs = with pkgs.buildPackages; [ | ||||
|     rustup | ||||
|     rustc cargo gcc rustfmt clippy | ||||
| 
 | ||||
|     pkg-config | ||||
|     xe | ||||
|     lzma | ||||
|     cargo-tarpaulin | ||||
|     gnumake | ||||
| 
 | ||||
|     # dotnet-sdk_8 | ||||
|   ]; | ||||
| 
 | ||||
|   RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter