diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 54583e5..86f2771 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index dc4713e..47241fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/crates/servicepoint/Cargo.toml b/crates/servicepoint/Cargo.toml index ac2698f..b68b538 100644 --- a/crates/servicepoint/Cargo.toml +++ b/crates/servicepoint/Cargo.toml @@ -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 \ No newline at end of file diff --git a/crates/servicepoint/README.md b/crates/servicepoint/README.md index 8756253..1aa717f 100644 --- a/crates/servicepoint/README.md +++ b/crates/servicepoint/README.md @@ -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 diff --git a/crates/servicepoint/examples/announce.rs b/crates/servicepoint/examples/announce.rs index b165135..75b9ba6 100644 --- a/crates/servicepoint/examples/announce.rs +++ b/crates/servicepoint/examples/announce.rs @@ -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"); } diff --git a/crates/servicepoint/examples/brightness_tester.rs b/crates/servicepoint/examples/brightness_tester.rs index b1b1ac3..5f2bd90 100644 --- a/crates/servicepoint/examples/brightness_tester.rs +++ b/crates/servicepoint/examples/brightness_tester.rs @@ -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"); } diff --git a/crates/servicepoint/examples/game_of_life.rs b/crates/servicepoint/examples/game_of_life.rs index 9fff62e..29507ce 100644 --- a/crates/servicepoint/examples/game_of_life.rs +++ b/crates/servicepoint/examples/game_of_life.rs @@ -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); } diff --git a/crates/servicepoint/examples/moving_line.rs b/crates/servicepoint/examples/moving_line.rs index f0353be..5682be4 100644 --- a/crates/servicepoint/examples/moving_line.rs +++ b/crates/servicepoint/examples/moving_line.rs @@ -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); } } diff --git a/crates/servicepoint/examples/random_brightness.rs b/crates/servicepoint/examples/random_brightness.rs index d9ea583..01487a2 100644 --- a/crates/servicepoint/examples/random_brightness.rs +++ b/crates/servicepoint/examples/random_brightness.rs @@ -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 { diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs index fdf84e3..03e8215 100644 --- a/crates/servicepoint/examples/wiping_clear.rs +++ b/crates/servicepoint/examples/wiping_clear.rs @@ -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); } } diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs new file mode 100644 index 0000000..5401a9f --- /dev/null +++ b/crates/servicepoint/src/brightness.rs @@ -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; + +impl From for u8 { + fn from(brightness: Brightness) -> Self { + brightness.0 + } +} + +impl TryFrom for Brightness { + type Error = u8; + + fn try_from(value: u8) -> Result { + 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 for Vec { + fn from(value: PrimitiveGrid) -> Self { + value + .iter() + .map(|brightness| (*brightness).into()) + .collect() + } +} + +impl From for PrimitiveGrid { + fn from(value: PrimitiveGrid) -> Self { + let u8s = value + .iter() + .map(|brightness| (*brightness).into()) + .collect::>(); + PrimitiveGrid::load(value.width(), value.height(), &u8s) + } +} + +impl TryFrom> for BrightnessGrid { + type Error = u8; + + fn try_from(value: PrimitiveGrid) -> Result { + let brightnesses = value + .iter() + .map(|b| Brightness::try_from(*b)) + .collect::, _>>()?; + Ok(BrightnessGrid::load( + value.width(), + value.height(), + &brightnesses, + )) + } +} + +#[cfg(feature = "rand")] +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Brightness { + Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0)) + } +} diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs index b488426..930b5b2 100644 --- a/crates/servicepoint/src/command.rs +++ b/crates/servicepoint/src/command.rs @@ -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 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; -/// 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. + /// + ///
+ /// 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. + ///
+ /// + /// # 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, Cp437Grid), + + /// Sets a window of pixels to the specified values + BitmapLinearWin(Origin, 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, 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 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); + ///
Untested
+ /// + /// 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 for Command { @@ -203,7 +190,7 @@ impl TryFrom for Command { /// Try to interpret the `Packet` as one containing a `Command` fn try_from(packet: Packet) -> Result { - 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 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, - ) -> 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 { + fn packet_into_command_only( + packet: Packet, + command: Command, + ) -> Result { 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 { + 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 { + 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 { + 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::::new(4, 2), + Origin::new(1, 0) + Origin::new(3, 2) + ); } } diff --git a/crates/servicepoint/src/compression_code.rs b/crates/servicepoint/src/compression_code.rs index 44bed3c..0440c4e 100644 --- a/crates/servicepoint/src/compression_code.rs +++ b/crates/servicepoint/src/compression_code.rs @@ -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 { diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs index 356f97a..d18d76b 100644 --- a/crates/servicepoint/src/connection.rs +++ b/crates/servicepoint/src/connection.rs @@ -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, diff --git a/crates/servicepoint/src/data_ref.rs b/crates/servicepoint/src/data_ref.rs index d2665d6..b8ff624 100644 --- a/crates/servicepoint/src/data_ref.rs +++ b/crates/servicepoint/src/data_ref.rs @@ -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 { /// 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]; } diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index dcb2755..d71e1fa 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -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; -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 diff --git a/crates/servicepoint/src/origin.rs b/crates/servicepoint/src/origin.rs new file mode 100644 index 0000000..88758a5 --- /dev/null +++ b/crates/servicepoint/src/origin.rs @@ -0,0 +1,48 @@ +use std::marker::PhantomData; + +/// An origin marks the top left position of a window sent to the display. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Origin { + /// position in the width direction + pub x: usize, + /// position in the height direction + pub y: usize, + phantom_data: PhantomData, +} + +impl Origin { + /// Create a new `Origin` instance for the provided position. + pub fn new(x: usize, y: usize) -> Self { + Self { + x, + y, + phantom_data: PhantomData, + } + } +} + +impl std::ops::Add> for Origin { + type Output = Origin; + + fn add(self, rhs: Origin) -> Self::Output { + Origin { + x: self.x + rhs.x, + y: self.y + rhs.y, + phantom_data: PhantomData, + } + } +} + +pub trait DisplayUnit {} + +/// Marks something to be measured in number of pixels. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Pixels(); + +/// Marks something to be measured in number of iles. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Tiles(); + +impl DisplayUnit for Pixels {} + +impl DisplayUnit for Tiles {} diff --git a/crates/servicepoint/src/packet.rs b/crates/servicepoint/src/packet.rs index 5ad80f1..80ce052 100644 --- a/crates/servicepoint/src/packet.rs +++ b/crates/servicepoint/src/packet.rs @@ -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 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, + ) -> 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: 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}; diff --git a/crates/servicepoint/src/pixel_grid.rs b/crates/servicepoint/src/pixel_grid.rs index f8c34b4..82e45d1 100644 --- a/crates/servicepoint/src/pixel_grid.rs +++ b/crates/servicepoint/src/pixel_grid.rs @@ -158,7 +158,7 @@ impl Grid for PixelGrid { } } -impl DataRef for PixelGrid { +impl DataRef for PixelGrid { fn data_ref_mut(&mut self) -> &mut [u8] { self.bit_vec.as_raw_mut_slice() } diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/primitive_grid.rs similarity index 70% rename from crates/servicepoint/src/byte_grid.rs rename to crates/servicepoint/src/primitive_grid.rs index 74b9e3c..578379e 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/primitive_grid.rs @@ -2,40 +2,43 @@ use std::slice::{Iter, IterMut}; use crate::{DataRef, Grid}; +pub trait PrimitiveGridType: Sized + Default + Copy + Clone {} +impl PrimitiveGridType for T {} + /// A 2D grid of bytes #[derive(Debug, Clone, PartialEq)] -pub struct ByteGrid { +pub struct PrimitiveGrid { width: usize, height: usize, - data: Vec, + data: Vec, } -impl ByteGrid { - /// Creates a new `ByteGrid` with the specified dimensions. +impl PrimitiveGrid { + /// 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::::new(2,2); /// for y in 0..grid.height() { /// for x in 0..grid.width() { /// grid.get(x, y); /// } /// } /// ``` - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter { 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 { 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 { + pub fn iter_mut(&mut self) -> IterMut { 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 for ByteGrid { - /// Sets the value of the cell at the specified position in the `ByteGrid. +impl Grid for PrimitiveGrid { + /// Sets the value of the cell at the specified position in the `PrimitiveGrid. /// /// # Arguments /// @@ -120,7 +123,7 @@ impl Grid 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 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 for ByteGrid { } } -impl DataRef for ByteGrid { +impl DataRef for PrimitiveGrid { /// 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 for Vec { +impl From> for Vec { /// Turn into the underlying `Vec` containing the rows of bytes. - fn from(value: ByteGrid) -> Self { + fn from(value: PrimitiveGrid) -> Self { value.data } } -pub struct IterRows<'t> { - byte_grid: &'t ByteGrid, +pub struct IterRows<'t, T: PrimitiveGridType> { + byte_grid: &'t PrimitiveGrid, 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 { 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::::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 = 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); diff --git a/crates/servicepoint_binding_c/Cargo.toml b/crates/servicepoint_binding_c/Cargo.toml index d9b124a..16c2f81 100644 --- a/crates/servicepoint_binding_c/Cargo.toml +++ b/crates/servicepoint_binding_c/Cargo.toml @@ -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"] diff --git a/crates/servicepoint_binding_c/cbindgen.toml b/crates/servicepoint_binding_c/cbindgen.toml index 1c63d58..c65f551 100644 --- a/crates/servicepoint_binding_c/cbindgen.toml +++ b/crates/servicepoint_binding_c/cbindgen.toml @@ -33,4 +33,4 @@ include = ["servicepoint"] extra_bindings = ["servicepoint"] [parse.expand] -#all_features = true +all_features = true diff --git a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h index 075f61c..270bb5c 100644 --- a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h +++ b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h @@ -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`. * diff --git a/crates/servicepoint_binding_c/src/brightness_grid.rs b/crates/servicepoint_binding_c/src/brightness_grid.rs new file mode 100644 index 0000000..6fa2c5a --- /dev/null +++ b/crates/servicepoint_binding_c/src/brightness_grid.rs @@ -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::(), 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(), + } +} diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index 400a6ee..823e3d6 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -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, ))) diff --git a/crates/servicepoint_binding_c/src/byte_grid.rs b/crates/servicepoint_binding_c/src/cp437_grid.rs similarity index 54% rename from crates/servicepoint_binding_c/src/byte_grid.rs rename to crates/servicepoint_binding_c/src/cp437_grid.rs index 9b4425f..f14c71d 100644 --- a/crates/servicepoint_binding_c/src/byte_grid.rs +++ b/crates/servicepoint_binding_c/src/cp437_grid.rs @@ -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(), diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs index 7aaa92e..0939b66 100644 --- a/crates/servicepoint_binding_c/src/lib.rs +++ b/crates/servicepoint_binding_c/src/lib.rs @@ -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; diff --git a/crates/servicepoint_binding_cs/Cargo.toml b/crates/servicepoint_binding_cs/Cargo.toml index 5db8776..e93b557 100644 --- a/crates/servicepoint_binding_cs/Cargo.toml +++ b/crates/servicepoint_binding_cs/Cargo.toml @@ -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 diff --git a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs index 5e958ad..009de2d 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/BindGen/ServicePoint.g.cs @@ -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); - /// 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`. - [DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern ByteGrid* sp_byte_grid_new(nuint width, nuint height); + /// 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`. + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern CBrightnessGrid* sp_brightness_grid_new(nuint width, nuint height); - /// 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`. - [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); + /// 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`. + [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); - /// 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`. - [DllImport(__DllName, EntryPoint = "sp_byte_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern ByteGrid* sp_byte_grid_clone(ByteGrid* @this); + /// 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`. + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern CBrightnessGrid* sp_brightness_grid_clone(CBrightnessGrid* @this); - /// 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` - [DllImport(__DllName, EntryPoint = "sp_byte_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern void sp_byte_grid_dealloc(ByteGrid* @this); + /// 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` + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void sp_brightness_grid_dealloc(CBrightnessGrid* @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 `ByteGrid` - `this` is not written to concurrently - [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); + /// 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 + [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); - /// 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 - [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); + /// 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 + [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); - /// 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 - [DllImport(__DllName, EntryPoint = "sp_byte_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern void sp_byte_grid_fill(ByteGrid* @this, byte value); + /// 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 + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void sp_brightness_grid_fill(CBrightnessGrid* @this, byte value); - /// 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` - [DllImport(__DllName, EntryPoint = "sp_byte_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern nuint sp_byte_grid_width(ByteGrid* @this); + /// 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` + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint sp_brightness_grid_width(CBrightnessGrid* @this); - /// 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` - [DllImport(__DllName, EntryPoint = "sp_byte_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern nuint sp_byte_grid_height(ByteGrid* @this); + /// 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` + [DllImport(__DllName, EntryPoint = "sp_brightness_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint sp_brightness_grid_height(CBrightnessGrid* @this); - /// 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 - [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); + /// 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 + [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); + + /// 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`. + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern CCp437Grid* sp_cp437_grid_new(nuint width, nuint height); + + /// 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`. + [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); + + /// 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`. + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern CCp437Grid* sp_cp437_grid_clone(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` + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void sp_cp437_grid_dealloc(CCp437Grid* @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 `Cp437Grid` - `this` is not written to concurrently + [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); + + /// 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 + [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); + + /// 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 + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void sp_cp437_grid_fill(CCp437Grid* @this, byte value); + + /// 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` + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint sp_cp437_grid_width(CCp437Grid* @this); + + /// 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` + [DllImport(__DllName, EntryPoint = "sp_cp437_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern nuint sp_cp437_grid_height(CCp437Grid* @this); + + /// 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 + [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); /// 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`. [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(); - /// Allocates a new `Command::Brightness` instance. # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. + /// Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the same value. # Panics - When the provided brightness value is out of range (0-11). # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`. [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern Command* sp_command_brightness(byte brightness); /// 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`. [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); /// 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`. [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] @@ -147,7 +187,7 @@ namespace ServicePoint.BindGen /// 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`. [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); /// 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`. [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 diff --git a/crates/servicepoint_binding_cs/ServicePoint/BrightnessGrid.cs b/crates/servicepoint_binding_cs/ServicePoint/BrightnessGrid.cs new file mode 100644 index 0000000..36af6e0 --- /dev/null +++ b/crates/servicepoint_binding_cs/ServicePoint/BrightnessGrid.cs @@ -0,0 +1,103 @@ +using ServicePoint.BindGen; + +namespace ServicePoint; + +public sealed class BrightnessGrid : SpNativeInstance +{ + 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 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 Data + { + get + { + unsafe + { + var slice = NativeMethods.sp_brightness_grid_unsafe_data_ref(Instance); + return new Span(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); + } +} diff --git a/crates/servicepoint_binding_cs/ServicePoint/Command.cs b/crates/servicepoint_binding_cs/ServicePoint/Command.cs index b9d0d3c..a0349c8 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/Command.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/Command.cs @@ -61,7 +61,7 @@ public sealed class Command : SpNativeInstance } } - 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 } } - public static Command Cp437Data(int x, int y, ByteGrid byteGrid) + public static Command Cp437Data(int x, int y, Cp437Grid byteGrid) { unsafe { diff --git a/crates/servicepoint_binding_cs/ServicePoint/ByteGrid.cs b/crates/servicepoint_binding_cs/ServicePoint/Cp437Grid.cs similarity index 62% rename from crates/servicepoint_binding_cs/ServicePoint/ByteGrid.cs rename to crates/servicepoint_binding_cs/ServicePoint/Cp437Grid.cs index e6c4fdc..01a83fb 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/ByteGrid.cs +++ b/crates/servicepoint_binding_cs/ServicePoint/Cp437Grid.cs @@ -3,33 +3,33 @@ using ServicePoint.BindGen; namespace ServicePoint; -public sealed class ByteGrid : SpNativeInstance +public sealed class Cp437Grid : SpNativeInstance { - 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 bytes) + public static Cp437Grid Load(int width, int height, Span 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 { 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 { unsafe { - NativeMethods.sp_byte_grid_fill(Instance, value); + NativeMethods.sp_cp437_grid_fill(Instance, value); } } @@ -95,7 +95,7 @@ public sealed class ByteGrid : SpNativeInstance { 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 { 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 { unsafe { - var slice = NativeMethods.sp_byte_grid_unsafe_data_ref(Instance); + var slice = NativeMethods.sp_cp437_grid_unsafe_data_ref(Instance); return new Span(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); } } diff --git a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj index 4285c10..6053dc5 100644 --- a/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj +++ b/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj @@ -7,12 +7,11 @@ true true - true ServicePoint - 0.6.0 + 0.7.0 Repository Authors None ServicePoint @@ -35,7 +34,7 @@ - + diff --git a/crates/servicepoint_binding_cs/build.rs b/crates/servicepoint_binding_cs/build.rs index 49747b2..ec2744e 100644 --- a/crates/servicepoint_binding_cs/build.rs +++ b/crates/servicepoint_binding_cs/build.rs @@ -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") diff --git a/shell.nix b/shell.nix index 0555690..4b4eeca 100644 --- a/shell.nix +++ b/shell.nix @@ -1,11 +1,16 @@ {pkgs ? import {}}: 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}"; }