Merge pull request #13 from cccb/fonts

More CP437, PrimitiveGrid::map, renamings
This commit is contained in:
Vinzenz Schroeter 2024-10-16 20:10:57 +02:00 committed by GitHub
commit 30d74ff07d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 2419 additions and 1580 deletions

10
Cargo.lock generated
View file

@ -147,9 +147,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.29" version = "1.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1" checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -610,7 +610,7 @@ dependencies = [
[[package]] [[package]]
name = "servicepoint" name = "servicepoint"
version = "0.9.1" version = "0.10.0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"bzip2", "bzip2",
@ -626,7 +626,7 @@ dependencies = [
[[package]] [[package]]
name = "servicepoint_binding_c" name = "servicepoint_binding_c"
version = "0.9.1" version = "0.10.0"
dependencies = [ dependencies = [
"cbindgen", "cbindgen",
"servicepoint", "servicepoint",
@ -634,7 +634,7 @@ dependencies = [
[[package]] [[package]]
name = "servicepoint_binding_cs" name = "servicepoint_binding_cs"
version = "0.9.1" version = "0.10.0"
dependencies = [ dependencies = [
"csbindgen", "csbindgen",
"servicepoint", "servicepoint",

View file

@ -6,7 +6,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.9.1" version = "0.10.0"
[workspace.lints.rust] [workspace.lints.rust]
missing-docs = "warn" missing-docs = "warn"

View file

@ -17,7 +17,7 @@ cargo add servicepoint
or or
```toml ```toml
[dependencies] [dependencies]
servicepoint = "0.9.1" servicepoint = "0.10.0"
``` ```
## Examples ## Examples

View file

@ -6,16 +6,26 @@ use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
#[arg(short, long, default_value = "localhost:2342")] #[arg(
short,
long,
default_value = "localhost:2342",
help = "Address of the display"
)]
destination: String, destination: String,
#[arg(short, long, num_args = 1.., value_delimiter = '\n')] #[arg(short, long, num_args = 1.., value_delimiter = '\n',
help = "Text to send - specify multiple times for multiple lines")]
text: Vec<String>, text: Vec<String>,
#[arg(short, long, default_value_t = true)] #[arg(
short,
long,
default_value_t = true,
help = "Clear screen before sending text"
)]
clear: bool, clear: bool,
} }
/// example: `cargo run -- --text "Hallo, /// example: `cargo run -- --text "Hallo" --text "CCCB"`
/// CCCB"`
fn main() { fn main() {
let mut cli = Cli::parse(); let mut cli = Cli::parse();
if cli.text.is_empty() { if cli.text.is_empty() {
@ -31,15 +41,11 @@ fn main() {
.expect("sending clear failed"); .expect("sending clear failed");
} }
let text = cli.text.iter().fold(String::new(), move |str, line| { let text = cli.text.join("\n");
let is_first = str.is_empty();
str + if is_first { "" } else { "\n" } + line
});
let grid = CharGrid::from(&*text); let grid = CharGrid::from(&*text);
let cp437_grid = Cp437Grid::from(&grid); let cp437_grid = Cp437Grid::from(&grid);
connection connection
.send(Command::Cp437Data(Origin::new(0, 0), cp437_grid)) .send(Command::Cp437Data(Origin::ZERO, cp437_grid))
.expect("sending text failed"); .expect("sending text failed");
} }

View file

@ -15,23 +15,24 @@ fn main() {
let connection = Connection::open(cli.destination) let connection = Connection::open(cli.destination)
.expect("could not connect to display"); .expect("could not connect to display");
let mut pixels = PixelGrid::max_sized(); let mut pixels = Bitmap::max_sized();
pixels.fill(true); pixels.fill(true);
let command = Command::BitmapLinearWin( let command = Command::BitmapLinearWin(
Origin::new(0, 0), Origin::ZERO,
pixels, pixels,
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
); );
connection.send(command).expect("send failed"); connection.send(command).expect("send failed");
let max_brightness = usize::from(u8::from(Brightness::MAX)); let max_brightness: u8 = Brightness::MAX.into();
let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT); let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT);
for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() {
*byte = Brightness::try_from((index % max_brightness) as u8).unwrap(); let level = index as u8 % max_brightness;
*byte = Brightness::try_from(level).unwrap();
} }
connection connection
.send(Command::CharBrightness(Origin::new(0, 0), brightnesses)) .send(Command::CharBrightness(Origin::ZERO, brightnesses))
.expect("send failed"); .expect("send failed");
} }

View file

@ -24,7 +24,7 @@ fn main() {
loop { loop {
let command = Command::BitmapLinearWin( let command = Command::BitmapLinearWin(
Origin::new(0, 0), Origin::ZERO,
field.clone(), field.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
); );
@ -34,7 +34,7 @@ fn main() {
} }
} }
fn iteration(field: PixelGrid) -> PixelGrid { fn iteration(field: Bitmap) -> Bitmap {
let mut next = field.clone(); let mut next = field.clone();
for x in 0..field.width() { for x in 0..field.width() {
for y in 0..field.height() { for y in 0..field.height() {
@ -51,7 +51,7 @@ fn iteration(field: PixelGrid) -> PixelGrid {
next next
} }
fn count_neighbors(field: &PixelGrid, x: i32, y: i32) -> i32 { fn count_neighbors(field: &Bitmap, x: i32, y: i32) -> i32 {
let mut count = 0; let mut count = 0;
for nx in x - 1..=x + 1 { for nx in x - 1..=x + 1 {
for ny in y - 1..=y + 1 { for ny in y - 1..=y + 1 {
@ -78,8 +78,8 @@ fn count_neighbors(field: &PixelGrid, x: i32, y: i32) -> i32 {
count count
} }
fn make_random_field(probability: f64) -> PixelGrid { fn make_random_field(probability: f64) -> Bitmap {
let mut field = PixelGrid::max_sized(); let mut field = Bitmap::max_sized();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let d = distributions::Bernoulli::new(probability).unwrap(); let d = distributions::Bernoulli::new(probability).unwrap();
for x in 0..field.width() { for x in 0..field.width() {

View file

@ -16,7 +16,7 @@ fn main() {
let connection = Connection::open(Cli::parse().destination) let connection = Connection::open(Cli::parse().destination)
.expect("could not connect to display"); .expect("could not connect to display");
let mut pixels = PixelGrid::max_sized(); let mut pixels = Bitmap::max_sized();
for x_offset in 0..usize::MAX { for x_offset in 0..usize::MAX {
pixels.fill(false); pixels.fill(false);
@ -25,7 +25,7 @@ fn main() {
} }
let command = Command::BitmapLinearWin( let command = Command::BitmapLinearWin(
Origin::new(0, 0), Origin::ZERO,
pixels.clone(), pixels.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
); );

View file

@ -28,11 +28,11 @@ fn main() {
// put all pixels in on state // put all pixels in on state
if cli.enable_all { if cli.enable_all {
let mut filled_grid = PixelGrid::max_sized(); let mut filled_grid = Bitmap::max_sized();
filled_grid.fill(true); filled_grid.fill(true);
let command = BitmapLinearWin( let command = BitmapLinearWin(
Origin::new(0, 0), Origin::ZERO,
filled_grid, filled_grid,
CompressionCode::Lzma, CompressionCode::Lzma,
); );

View file

@ -1,7 +1,7 @@
//! Example for how to use the WebSocket connection //! Example for how to use the WebSocket connection
use servicepoint::{ use servicepoint::{
Command, CompressionCode, Connection, Grid, Origin, PixelGrid, Bitmap, Command, CompressionCode, Connection, Grid, Origin,
}; };
fn main() { fn main() {
@ -13,7 +13,7 @@ fn main() {
// use send_mut instead of send // use send_mut instead of send
connection.send_mut(Command::Clear).unwrap(); connection.send_mut(Command::Clear).unwrap();
let mut pixels = PixelGrid::max_sized(); let mut pixels = Bitmap::max_sized();
pixels.fill(true); pixels.fill(true);
// use send_mut instead of send // use send_mut instead of send

View file

@ -25,7 +25,7 @@ fn main() {
let connection = Connection::open(cli.destination) let connection = Connection::open(cli.destination)
.expect("could not connect to display"); .expect("could not connect to display");
let mut enabled_pixels = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); let mut enabled_pixels = Bitmap::max_sized();
enabled_pixels.fill(true); enabled_pixels.fill(true);
for x_offset in 0..PIXEL_WIDTH { for x_offset in 0..PIXEL_WIDTH {

View file

@ -6,21 +6,21 @@ use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH};
/// A grid of pixels stored in packed bytes. /// A grid of pixels stored in packed bytes.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct PixelGrid { pub struct Bitmap {
width: usize, width: usize,
height: usize, height: usize,
bit_vec: SpBitVec, bit_vec: SpBitVec,
} }
impl PixelGrid { impl Bitmap {
/// Creates a new [PixelGrid] with the specified dimensions. /// Creates a new [Bitmap] with the specified dimensions.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `width`: size in pixels in x-direction /// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction /// - `height`: size in pixels in y-direction
/// ///
/// returns: [PixelGrid] initialized to all pixels off /// returns: [Bitmap] initialized to all pixels off
/// ///
/// # Panics /// # Panics
/// ///
@ -40,14 +40,14 @@ impl PixelGrid {
Self::new(PIXEL_WIDTH, PIXEL_HEIGHT) Self::new(PIXEL_WIDTH, PIXEL_HEIGHT)
} }
/// Loads a [PixelGrid] with the specified dimensions from the provided data. /// Loads a [Bitmap] with the specified dimensions from the provided data.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `width`: size in pixels in x-direction /// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction /// - `height`: size in pixels in y-direction
/// ///
/// returns: [PixelGrid] that contains a copy of the provided data /// returns: [Bitmap] that contains a copy of the provided data
/// ///
/// # Panics /// # Panics
/// ///
@ -64,12 +64,12 @@ impl PixelGrid {
} }
} }
/// Iterate over all cells in [PixelGrid]. /// Iterate over all cells in [Bitmap].
/// ///
/// Order is equivalent to the following loop: /// Order is equivalent to the following loop:
/// ``` /// ```
/// # use servicepoint::{PixelGrid, Grid}; /// # use servicepoint::{Bitmap, Grid};
/// # let grid = PixelGrid::new(8,2); /// # let grid = Bitmap::new(8,2);
/// for y in 0..grid.height() { /// for y in 0..grid.height() {
/// for x in 0..grid.width() { /// for x in 0..grid.width() {
/// grid.get(x, y); /// grid.get(x, y);
@ -80,12 +80,12 @@ impl PixelGrid {
self.bit_vec.iter().by_refs() self.bit_vec.iter().by_refs()
} }
/// Iterate over all cells in [PixelGrid] mutably. /// Iterate over all cells in [Bitmap] mutably.
/// ///
/// Order is equivalent to the following loop: /// Order is equivalent to the following loop:
/// ``` /// ```
/// # use servicepoint::{PixelGrid, Grid}; /// # use servicepoint::{Bitmap, Grid};
/// # let mut grid = PixelGrid::new(8,2); /// # let mut grid = Bitmap::new(8,2);
/// # let value = false; /// # let value = false;
/// for y in 0..grid.height() { /// for y in 0..grid.height() {
/// for x in 0..grid.width() { /// for x in 0..grid.width() {
@ -96,8 +96,8 @@ impl PixelGrid {
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # use servicepoint::{PixelGrid, Grid}; /// # use servicepoint::{Bitmap, Grid};
/// # let mut grid = PixelGrid::new(8,2); /// # let mut grid = Bitmap::new(8,2);
/// # let value = false; /// # let value = false;
/// for (index, mut pixel) in grid.iter_mut().enumerate() { /// for (index, mut pixel) in grid.iter_mut().enumerate() {
/// pixel.set(index % 2 == 0) /// pixel.set(index % 2 == 0)
@ -107,17 +107,17 @@ impl PixelGrid {
self.bit_vec.iter_mut() self.bit_vec.iter_mut()
} }
/// Iterate over all rows in [PixelGrid] top to bottom. /// Iterate over all rows in [Bitmap] top to bottom.
pub fn iter_rows(&self) -> IterRows { pub fn iter_rows(&self) -> IterRows {
IterRows { IterRows {
pixel_grid: self, bitmap: self,
row: 0, row: 0,
} }
} }
} }
impl Grid<bool> for PixelGrid { impl Grid<bool> for Bitmap {
/// Sets the value of the specified position in the [PixelGrid]. /// Sets the value of the specified position in the [Bitmap].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -139,7 +139,7 @@ impl Grid<bool> for PixelGrid {
self.bit_vec[x + y * self.width] self.bit_vec[x + y * self.width]
} }
/// Sets the state of all pixels in the [PixelGrid]. /// Sets the state of all pixels in the [Bitmap].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -158,7 +158,7 @@ impl Grid<bool> for PixelGrid {
} }
} }
impl DataRef<u8> for PixelGrid { impl DataRef<u8> for Bitmap {
fn data_ref_mut(&mut self) -> &mut [u8] { fn data_ref_mut(&mut self) -> &mut [u8] {
self.bit_vec.as_raw_mut_slice() self.bit_vec.as_raw_mut_slice()
} }
@ -168,15 +168,15 @@ impl DataRef<u8> for PixelGrid {
} }
} }
impl From<PixelGrid> for Vec<u8> { impl From<Bitmap> for Vec<u8> {
/// Turns a [PixelGrid] into the underlying [`Vec<u8>`]. /// Turns a [Bitmap] into the underlying [`Vec<u8>`].
fn from(value: PixelGrid) -> Self { fn from(value: Bitmap) -> Self {
value.bit_vec.into() value.bit_vec.into()
} }
} }
pub struct IterRows<'t> { pub struct IterRows<'t> {
pixel_grid: &'t PixelGrid, bitmap: &'t Bitmap,
row: usize, row: usize,
} }
@ -184,24 +184,24 @@ impl<'t> Iterator for IterRows<'t> {
type Item = &'t BitSlice<u8, Msb0>; type Item = &'t BitSlice<u8, Msb0>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.pixel_grid.height { if self.row >= self.bitmap.height {
return None; return None;
} }
let start = self.row * self.pixel_grid.width; let start = self.row * self.bitmap.width;
let end = start + self.pixel_grid.width; let end = start + self.bitmap.width;
self.row += 1; self.row += 1;
Some(&self.pixel_grid.bit_vec[start..end]) Some(&self.bitmap.bit_vec[start..end])
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{DataRef, Grid, PixelGrid}; use crate::{Bitmap, DataRef, Grid};
#[test] #[test]
fn fill() { fn fill() {
let mut grid = PixelGrid::new(8, 2); let mut grid = Bitmap::new(8, 2);
assert_eq!(grid.data_ref(), [0x00, 0x00]); assert_eq!(grid.data_ref(), [0x00, 0x00]);
grid.fill(true); grid.fill(true);
@ -213,7 +213,7 @@ mod tests {
#[test] #[test]
fn get_set() { fn get_set() {
let mut grid = PixelGrid::new(8, 2); let mut grid = Bitmap::new(8, 2);
assert!(!grid.get(0, 0)); assert!(!grid.get(0, 0));
assert!(!grid.get(1, 1)); assert!(!grid.get(1, 1));
@ -228,7 +228,7 @@ mod tests {
#[test] #[test]
fn load() { fn load() {
let mut grid = PixelGrid::new(8, 3); let mut grid = Bitmap::new(8, 3);
for x in 0..grid.width { for x in 0..grid.width {
for y in 0..grid.height { for y in 0..grid.height {
grid.set(x, y, (x + y) % 2 == 0); grid.set(x, y, (x + y) % 2 == 0);
@ -239,33 +239,33 @@ mod tests {
let data: Vec<u8> = grid.into(); let data: Vec<u8> = grid.into();
let grid = PixelGrid::load(8, 3, &data); let grid = Bitmap::load(8, 3, &data);
assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn out_of_bounds_x() { fn out_of_bounds_x() {
let vec = PixelGrid::new(8, 2); let vec = Bitmap::new(8, 2);
vec.get(8, 1); vec.get(8, 1);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn out_of_bounds_y() { fn out_of_bounds_y() {
let mut vec = PixelGrid::new(8, 2); let mut vec = Bitmap::new(8, 2);
vec.set(1, 2, false); vec.set(1, 2, false);
} }
#[test] #[test]
fn iter() { fn iter() {
let grid = PixelGrid::new(8, 2); let grid = Bitmap::new(8, 2);
assert_eq!(16, grid.iter().count()) assert_eq!(16, grid.iter().count())
} }
#[test] #[test]
fn iter_rows() { fn iter_rows() {
let grid = PixelGrid::load(8, 2, &[0x04, 0x40]); let grid = Bitmap::load(8, 2, &[0x04, 0x40]);
let mut iter = grid.iter_rows(); let mut iter = grid.iter_rows();
assert_eq!(iter.next().unwrap().count_ones(), 1); assert_eq!(iter.next().unwrap().count_ones(), 1);
@ -275,7 +275,7 @@ mod tests {
#[test] #[test]
fn iter_mut() { fn iter_mut() {
let mut grid = PixelGrid::new(8, 2); let mut grid = Bitmap::new(8, 2);
for (index, mut pixel) in grid.iter_mut().enumerate() { for (index, mut pixel) in grid.iter_mut().enumerate() {
pixel.set(index % 2 == 0); pixel.set(index % 2 == 0);
} }
@ -284,7 +284,7 @@ mod tests {
#[test] #[test]
fn data_ref_mut() { fn data_ref_mut() {
let mut grid = PixelGrid::new(8, 2); let mut grid = Bitmap::new(8, 2);
let data = grid.data_ref_mut(); let data = grid.data_ref_mut();
data[1] = 0x0F; data[1] = 0x0F;
assert!(grid.get(7, 1)); assert!(grid.get(7, 1));

View file

@ -60,6 +60,17 @@ impl Brightness {
pub const MAX: Brightness = Brightness(11); pub const MAX: Brightness = Brightness(11);
/// lowest possible brightness value, 0 /// lowest possible brightness value, 0
pub const MIN: Brightness = Brightness(0); pub const MIN: Brightness = Brightness(0);
/// Create a brightness value without returning an error for brightnesses above [Brightness::MAX].
///
/// returns: the specified value as a [Brightness], or [Brightness::MAX].
pub fn saturating_from(value: u8) -> Brightness {
if value > Brightness::MAX.into() {
Brightness::MAX
} else {
Brightness(value)
}
}
} }
impl Default for Brightness { impl Default for Brightness {
@ -138,4 +149,10 @@ mod tests {
let actual = PrimitiveGrid::from(&grid); let actual = PrimitiveGrid::from(&grid);
assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); assert_eq!(actual.data_ref(), &[11, 0, 11, 11]);
} }
#[test]
fn saturating_convert() {
assert_eq!(Brightness::MAX, Brightness::saturating_from(100));
assert_eq!(Brightness(5), Brightness::saturating_from(5));
}
} }

View file

@ -4,7 +4,7 @@ use crate::{
command_code::CommandCode, command_code::CommandCode,
compression::into_decompressed, compression::into_decompressed,
packet::{Header, Packet}, packet::{Header, Packet},
Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, PixelGrid, Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin,
Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE,
}; };
@ -76,12 +76,7 @@ pub enum Command {
/// Show text on the screen. /// Show text on the screen.
/// ///
/// The text is sent in the form of a 2D grid of characters. /// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
///
/// <div class="warning">
/// The library does not currently convert between UTF-8 and CP-437.
/// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
/// </div>
/// ///
/// # Examples /// # Examples
/// ///
@ -100,6 +95,7 @@ pub enum Command {
/// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap(); /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
/// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap(); /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
/// ``` /// ```
/// [CP-437]: https://en.wikipedia.org/wiki/Code_page_437
Cp437Data(Origin<Tiles>, Cp437Grid), Cp437Data(Origin<Tiles>, Cp437Grid),
/// Overwrites a rectangular region of pixels. /// Overwrites a rectangular region of pixels.
@ -109,23 +105,23 @@ pub enum Command {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; /// # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
/// # let connection = servicepoint::Connection::Fake; /// # let connection = servicepoint::Connection::Fake;
/// # /// #
/// let mut pixels = PixelGrid::max_sized(); /// let mut pixels = Bitmap::max_sized();
/// // draw something to the pixels here /// // draw something to the pixels here
/// # pixels.set(2, 5, true); /// # pixels.set(2, 5, true);
/// ///
/// // create command to send pixels /// // create command to send pixels
/// let command = Command::BitmapLinearWin( /// let command = Command::BitmapLinearWin(
/// servicepoint::Origin::new(0, 0), /// servicepoint::Origin::ZERO,
/// pixels, /// pixels,
/// CompressionCode::Uncompressed /// CompressionCode::Uncompressed
/// ); /// );
/// ///
/// connection.send(command).expect("send failed"); /// connection.send(command).expect("send failed");
/// ``` /// ```
BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode), BitmapLinearWin(Origin<Pixels>, Bitmap, CompressionCode),
/// Set the brightness of all tiles to the same value. /// Set the brightness of all tiles to the same value.
/// ///
@ -217,9 +213,8 @@ pub enum Command {
BitmapLegacy, BitmapLegacy,
} }
#[derive(Debug)]
/// Err values for [Command::try_from]. /// Err values for [Command::try_from].
#[derive(PartialEq)] #[derive(Debug, PartialEq)]
pub enum TryFromPacketError { pub enum TryFromPacketError {
/// the contained command code does not correspond to a known command /// the contained command code does not correspond to a known command
InvalidCommand(u16), InvalidCommand(u16),
@ -342,7 +337,7 @@ impl Command {
Ok(Command::BitmapLinearWin( Ok(Command::BitmapLinearWin(
Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize),
PixelGrid::load( Bitmap::load(
tile_w as usize * TILE_SIZE, tile_w as usize * TILE_SIZE,
pixel_h as usize, pixel_h as usize,
&payload, &payload,
@ -376,7 +371,7 @@ impl Command {
} }
} }
/// Helper method for Packets into `BitMapLinear*`-Commands /// Helper method for Packets into `BitmapLinear*`-Commands
fn packet_into_linear_bitmap( fn packet_into_linear_bitmap(
packet: Packet, packet: Packet,
) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> {
@ -500,7 +495,8 @@ mod tests {
command_code::CommandCode, command_code::CommandCode,
origin::Pixels, origin::Pixels,
packet::{Header, Packet}, packet::{Header, Packet},
Brightness, Command, CompressionCode, Origin, PixelGrid, PrimitiveGrid, Bitmap, Brightness, BrightnessGrid, Command, CompressionCode, Origin,
PrimitiveGrid,
}; };
fn round_trip(original: Command) { fn round_trip(original: Command) {
@ -592,8 +588,8 @@ mod tests {
compression, compression,
)); ));
round_trip(Command::BitmapLinearWin( round_trip(Command::BitmapLinearWin(
Origin::new(0, 0), Origin::ZERO,
PixelGrid::max_sized(), Bitmap::max_sized(),
compression, compression,
)); ));
} }
@ -718,7 +714,7 @@ mod tests {
for compression in all_compressions().to_owned() { for compression in all_compressions().to_owned() {
let p: Packet = Command::BitmapLinearWin( let p: Packet = Command::BitmapLinearWin(
Origin::new(16, 8), Origin::new(16, 8),
PixelGrid::new(8, 8), Bitmap::new(8, 8),
compression, compression,
) )
.into(); .into();
@ -907,4 +903,28 @@ mod tests {
Origin::new(1, 0) + Origin::new(3, 2) Origin::new(1, 0) + Origin::new(3, 2)
); );
} }
#[test]
fn packet_into_char_brightness_invalid() {
let grid = BrightnessGrid::new(2, 2);
let command = Command::CharBrightness(Origin::ZERO, grid);
let mut packet: Packet = command.into();
let slot = packet.payload.get_mut(1).unwrap();
*slot = 23;
assert_eq!(
Command::try_from(packet),
Err(TryFromPacketError::InvalidBrightness(23))
);
}
#[test]
fn packet_into_brightness_invalid() {
let mut packet: Packet = Command::Brightness(Brightness::MAX).into();
let slot = packet.payload.get_mut(0).unwrap();
*slot = 42;
assert_eq!(
Command::try_from(packet),
Err(TryFromPacketError::InvalidBrightness(42))
);
}
} }

View file

@ -3,14 +3,14 @@
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid}; /// # use servicepoint::{Command, CompressionCode, Origin, Bitmap};
/// // create command without payload compression /// // create command without payload compression
/// # let pixels = PixelGrid::max_sized(); /// # let pixels = Bitmap::max_sized();
/// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed); /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Uncompressed);
/// ///
/// // create command with payload compressed with lzma and appropriate header flags /// // create command with payload compressed with lzma and appropriate header flags
/// # let pixels = PixelGrid::max_sized(); /// # let pixels = Bitmap::max_sized();
/// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Lzma); /// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Lzma);
/// ``` /// ```
#[repr(u16)] #[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]

View file

@ -1,4 +1,7 @@
use crate::cp437::Cp437LoadError::InvalidChar; //! Conversion between UTF-8 and CP-437.
//!
//! Most of the functionality is only available with feature "cp437" enabled.
use crate::{Grid, PrimitiveGrid}; use crate::{Grid, PrimitiveGrid};
use std::collections::HashMap; use std::collections::HashMap;
@ -10,9 +13,16 @@ pub type Cp437Grid = PrimitiveGrid<u8>;
/// A grid containing UTF-8 characters. /// A grid containing UTF-8 characters.
pub type CharGrid = PrimitiveGrid<char>; pub type CharGrid = PrimitiveGrid<char>;
#[derive(Debug)] /// Errors that can occur when loading CP-437.
#[derive(Debug, PartialEq)]
pub enum Cp437LoadError { pub enum Cp437LoadError {
InvalidChar { index: usize, char: char }, /// Invalid character in input prevented loading
InvalidChar {
/// invalid character is at this position in input
index: usize,
/// the invalid character
char: char,
},
} }
impl Cp437Grid { impl Cp437Grid {
@ -36,7 +46,7 @@ impl Cp437Grid {
for (index, char) in value.chars().enumerate() { for (index, char) in value.chars().enumerate() {
if !char.is_ascii() { if !char.is_ascii() {
return Err(InvalidChar { index, char }); return Err(Cp437LoadError::InvalidChar { index, char });
} }
let is_lf = char == '\n'; let is_lf = char == '\n';
@ -85,21 +95,15 @@ mod feature_cp437 {
/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
/// ///
/// Mostly follows CP437, except for: /// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
/// * 0x0A & 0x0D are kept for use as line endings.
/// * 0x1A is used for SAUCE.
/// * 0x1B is used for ANSI escape sequences.
///
/// These exclusions should be fine since most programs can't even use them
/// without issues. And this makes rendering simpler too.
/// ///
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set> /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
/// ///
/// Copied from https://github.com/kip93/cp437-tools. License: GPL-3.0 /// Mostly copied from https://github.com/kip93/cp437-tools. License: GPL-3.0
#[rustfmt::skip] #[rustfmt::skip]
const CP437_TO_UTF8: [char; 256] = [ pub const CP437_TO_UTF8: [char; 256] = [
/* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '\r', '♫', '☼', /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '', '♫', '☼',
/* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '', '', '∟', '↔', '▲', '▼', /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼',
/* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/',
/* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
/* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
@ -116,7 +120,7 @@ mod feature_cp437 {
/* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ',
]; ];
const UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> = static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> =
once_cell::sync::Lazy::new(|| { once_cell::sync::Lazy::new(|| {
let pairs = CP437_TO_UTF8 let pairs = CP437_TO_UTF8
.iter() .iter()
@ -125,48 +129,34 @@ mod feature_cp437 {
HashMap::from_iter(pairs) HashMap::from_iter(pairs)
}); });
const MISSING_CHAR_CP437: u8 = 0x3F; const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
impl From<&Cp437Grid> for CharGrid { impl From<&Cp437Grid> for CharGrid {
fn from(value: &Cp437Grid) -> Self { fn from(value: &Cp437Grid) -> Self {
let mut grid = Self::new(value.width(), value.height()); value.map(cp437_to_char)
for y in 0..grid.height() {
for x in 0..grid.width() {
let converted = CP437_TO_UTF8[value.get(x, y) as usize];
grid.set(x, y, converted);
}
}
grid
} }
} }
impl From<&CharGrid> for Cp437Grid { impl From<&CharGrid> for Cp437Grid {
fn from(value: &CharGrid) -> Self { fn from(value: &CharGrid) -> Self {
let mut grid = Self::new(value.width(), value.height()); value.map(char_to_cp437)
for y in 0..grid.height() {
for x in 0..grid.width() {
let char = value.get(x, y);
let converted = *UTF8_TO_CP437
.get(&char)
.unwrap_or(&MISSING_CHAR_CP437);
grid.set(x, y, converted);
}
}
grid
} }
} }
impl From<&str> for CharGrid { impl From<&str> for CharGrid {
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
let value = value.replace("\r\n", "\n"); let value = value.replace("\r\n", "\n");
let lines = value.split('\n').collect::<Vec<_>>(); let mut lines = value
.split('\n')
.map(move |line| line.trim_end())
.collect::<Vec<_>>();
let width = let width =
lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len()));
while lines.last().is_some_and(move |line| line.is_empty()) {
_ = lines.pop();
}
let mut grid = Self::new(width, lines.len()); let mut grid = Self::new(width, lines.len());
for (y, line) in lines.iter().enumerate() { for (y, line) in lines.iter().enumerate() {
for (x, char) in line.chars().enumerate() { for (x, char) in line.chars().enumerate() {
@ -177,6 +167,44 @@ mod feature_cp437 {
grid grid
} }
} }
impl From<&CharGrid> for String {
fn from(value: &CharGrid) -> Self {
value
.iter_rows()
.map(move |chars| {
chars
.collect::<String>()
.replace('\0', " ")
.trim_end()
.to_string()
})
.collect::<Vec<_>>()
.join("\n")
}
}
/// Convert the provided bytes to UTF-8.
pub fn cp437_to_str(cp437: &[u8]) -> String {
cp437.iter().map(move |char| cp437_to_char(*char)).collect()
}
/// Convert a single CP-437 character to UTF-8.
pub fn cp437_to_char(cp437: u8) -> char {
CP437_TO_UTF8[cp437 as usize]
}
/// Convert the provided text to CP-437 bytes.
///
/// Characters that are not available are mapped to '?'.
pub fn str_to_cp437(utf8: &str) -> Vec<u8> {
utf8.chars().map(char_to_cp437).collect()
}
/// Convert a single UTF-8 character to CP-437.
pub fn char_to_cp437(utf8: char) -> u8 {
*UTF8_TO_CP437.get(&utf8).unwrap_or(&MISSING_CHAR_CP437)
}
} }
#[cfg(test)] #[cfg(test)]
@ -204,12 +232,23 @@ mod tests {
// line break will be added // line break will be added
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test]
fn load_ascii_invalid() {
assert_eq!(
Err(Cp437LoadError::InvalidChar {
char: '🥶',
index: 2
}),
Cp437Grid::load_ascii("?#🥶42", 3, false)
);
}
} }
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "cp437")] #[cfg(feature = "cp437")]
mod tests_feature_cp437 { mod tests_feature_cp437 {
use crate::{CharGrid, Cp437Grid}; use super::*;
#[test] #[test]
fn round_trip_cp437() { fn round_trip_cp437() {
@ -218,4 +257,48 @@ mod tests_feature_cp437 {
let actual = CharGrid::from(&cp437); let actual = CharGrid::from(&cp437);
assert_eq!(actual, utf8); assert_eq!(actual, utf8);
} }
#[test]
fn convert_str() {
// test text from https://int10h.org/oldschool-pc-fonts/fontlist/font?ibm_bios
let utf8 = r#"A quick brown fox jumps over the lazy dog.
0123456789 ¿?¡!`'"., <>()[]{} &@%*^#$\/
* Wieniläinen sioux'ta puhuva ökyzombie diggaa Åsan roquefort-tacoja.
* Ça me fait peur de fêter noël , sur cette île bizarroïde une mère et sa môme essaient de me tuer avec un gâteau à la cigüe brûlé.
* Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich.
* El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.
.·°·.
$ ¢ £ ¥
dx Σ x²·δx
"#;
let cp437 = str_to_cp437(utf8);
let actual = cp437_to_str(&*cp437);
assert_eq!(utf8, actual)
}
#[test]
fn convert_invalid() {
assert_eq!(cp437_to_char(char_to_cp437('😜')), '?');
}
#[test]
fn str_to_char_grid() {
let original = "Hello\r\nWorld!\n...\n";
let grid = CharGrid::from(original);
assert_eq!(3, grid.height());
let actual = String::from(&grid);
assert_eq!("Hello\nWorld!\n...", actual);
}
} }

View file

@ -76,15 +76,9 @@ pub trait Grid<T> {
/// ///
/// When the specified position is out of bounds for this grid. /// When the specified position is out of bounds for this grid.
fn assert_in_bounds(&self, x: usize, y: usize) { fn assert_in_bounds(&self, x: usize, y: usize) {
assert!( let width = self.width();
x < self.width(), assert!(x < width, "cannot access index [{x}, {y}] because x is outside of bounds [0..{width})");
"cannot access index [{x}, {y}] because x is outside of bounds 0..{}", let height = self.height();
self.width() - 1 assert!(y < height, "cannot access index [{x}, {y}] because x is outside of bounds [0..{height})");
);
assert!(
y < self.height(),
"cannot access index [{x}, {y}] because y is outside of bounds 0..{}",
self.height() - 1
);
} }
} }

View file

@ -7,7 +7,7 @@
//! # Examples //! # Examples
//! //!
//! ```rust //! ```rust
//! use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; //! use servicepoint::{Command, CompressionCode, Grid, Bitmap};
//! //!
//! let connection = servicepoint::Connection::open("127.0.0.1:2342") //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
//! .expect("connection failed"); //! .expect("connection failed");
@ -18,15 +18,15 @@
//! ``` //! ```
//! //!
//! ```rust //! ```rust
//! # use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; //! # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed"); //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
//! // turn on all pixels in a grid //! // turn on all pixels in a grid
//! let mut pixels = PixelGrid::max_sized(); //! let mut pixels = Bitmap::max_sized();
//! pixels.fill(true); //! pixels.fill(true);
//! //!
//! // create command to send pixels //! // create command to send pixels
//! let command = Command::BitmapLinearWin( //! let command = Command::BitmapLinearWin(
//! servicepoint::Origin::new(0, 0), //! servicepoint::Origin::ZERO,
//! pixels, //! pixels,
//! CompressionCode::Uncompressed //! CompressionCode::Uncompressed
//! ); //! );
@ -40,6 +40,7 @@ use std::time::Duration;
pub use bitvec; pub use bitvec;
use bitvec::prelude::{BitVec, Msb0}; use bitvec::prelude::{BitVec, Msb0};
pub use crate::bitmap::Bitmap;
pub use crate::brightness::{Brightness, BrightnessGrid}; pub use crate::brightness::{Brightness, BrightnessGrid};
pub use crate::command::{Command, Offset}; pub use crate::command::{Command, Offset};
pub use crate::compression_code::CompressionCode; pub use crate::compression_code::CompressionCode;
@ -48,23 +49,22 @@ pub use crate::cp437::{CharGrid, Cp437Grid};
pub use crate::data_ref::DataRef; pub use crate::data_ref::DataRef;
pub use crate::grid::Grid; pub use crate::grid::Grid;
pub use crate::origin::{Origin, Pixels, Tiles}; pub use crate::origin::{Origin, Pixels, Tiles};
pub use crate::pixel_grid::PixelGrid;
pub use crate::primitive_grid::PrimitiveGrid; pub use crate::primitive_grid::PrimitiveGrid;
type SpBitVec = BitVec<u8, Msb0>; type SpBitVec = BitVec<u8, Msb0>;
mod bitmap;
mod brightness; mod brightness;
mod command; mod command;
mod command_code; mod command_code;
mod compression; mod compression;
mod compression_code; mod compression_code;
mod connection; mod connection;
mod cp437; pub mod cp437;
mod data_ref; mod data_ref;
mod grid; mod grid;
mod origin; mod origin;
pub mod packet; pub mod packet;
mod pixel_grid;
mod primitive_grid; mod primitive_grid;
/// size of a single tile in one dimension /// size of a single tile in one dimension
@ -95,8 +95,8 @@ pub const TILE_HEIGHT: usize = 20;
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid}; /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
/// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
/// ``` /// ```
pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE;
@ -105,8 +105,8 @@ pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE;
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid}; /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
/// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
/// ``` /// ```
pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE;
@ -119,10 +119,10 @@ pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT;
/// ///
/// ```rust /// ```rust
/// # use std::time::Instant; /// # use std::time::Instant;
/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, PixelGrid}; /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
/// # let connection = servicepoint::Connection::open("172.23.42.29:2342") /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// # .expect("connection failed"); /// # .expect("connection failed");
/// # let pixels = PixelGrid::max_sized(); /// # let pixels = Bitmap::max_sized();
/// loop { /// loop {
/// let start = Instant::now(); /// let start = Instant::now();
/// ///

View file

@ -12,7 +12,7 @@ pub struct Origin<Unit: DisplayUnit> {
} }
impl<Unit: DisplayUnit> Origin<Unit> { impl<Unit: DisplayUnit> Origin<Unit> {
/// Top-left. Equivalent to `Origin::new(0, 0)`. /// Top-left. Equivalent to `Origin::ZERO`.
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
x: 0, x: 0,
y: 0, y: 0,

View file

@ -27,8 +27,8 @@ use std::mem::size_of;
use crate::compression::into_compressed; use crate::compression::into_compressed;
use crate::{ use crate::{
command_code::CommandCode, Command, CompressionCode, Grid, Offset, Origin, command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset,
PixelGrid, Pixels, Tiles, TILE_SIZE, Origin, Pixels, Tiles, TILE_SIZE,
}; };
/// A raw header. /// A raw header.
@ -214,7 +214,7 @@ impl From<Command> for Packet {
} }
impl Packet { impl Packet {
/// Helper method for `BitMapLinear*`-Commands into [Packet] /// Helper method for `BitmapLinear*`-Commands into [Packet]
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn bitmap_linear_into_packet( fn bitmap_linear_into_packet(
command: CommandCode, command: CommandCode,
@ -239,7 +239,7 @@ impl Packet {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn bitmap_win_into_packet( fn bitmap_win_into_packet(
origin: Origin<Pixels>, origin: Origin<Pixels>,
pixels: PixelGrid, pixels: Bitmap,
compression: CompressionCode, compression: CompressionCode,
) -> Packet { ) -> Packet {
debug_assert_eq!(origin.x % 8, 0); debug_assert_eq!(origin.x % 8, 0);

View file

@ -110,6 +110,34 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
None None
} }
} }
/// Convert between PrimitiveGrid types.
///
/// See also [Iterator::map].
///
/// # Examples
///
/// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command].
/// ```
/// # fn foo(grid: &mut PrimitiveGrid<u8>) {}
/// # use servicepoint::{Brightness, BrightnessGrid, Command, Origin, PrimitiveGrid, TILE_HEIGHT, TILE_WIDTH};
/// let mut grid: PrimitiveGrid<u8> = PrimitiveGrid::new(TILE_WIDTH, TILE_HEIGHT);
/// foo(&mut grid);
/// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
/// let command = Command::CharBrightness(Origin::ZERO, grid);
/// ```
pub fn map<TConverted, F>(&self, f: F) -> PrimitiveGrid<TConverted>
where
TConverted: PrimitiveGridType,
F: Fn(T) -> TConverted,
{
let data = self
.data_ref()
.iter()
.map(|elem| f(*elem))
.collect::<Vec<_>>();
PrimitiveGrid::load(self.width(), self.height(), &data)
}
} }
impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> {

View file

@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
cbindgen = "0.27.0" cbindgen = "0.27.0"
[dependencies.servicepoint] [dependencies.servicepoint]
version = "0.9.1" version = "0.10.0"
path = "../servicepoint" path = "../servicepoint"
features = ["all_compressions"] features = ["all_compressions"]

View file

@ -21,8 +21,8 @@ int main(void) {
if (connection == NULL) if (connection == NULL)
return 1; return 1;
SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
sp_pixel_grid_fill(pixels, true); sp_bitmap_fill(pixels, true);
SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
while (sp_connection_send_command(connection, sp_command_clone(command))); while (sp_connection_send_command(connection, sp_command_clone(command)));

View file

@ -16,7 +16,7 @@ line_endings = "LF"
############################# Codegen Options ################################## ############################# Codegen Options ##################################
style = "both" style = "type"
usize_is_size_t = true usize_is_size_t = true
# this is needed because otherwise the order in the C# bindings is different on different machines # this is needed because otherwise the order in the C# bindings is different on different machines
@ -31,3 +31,6 @@ all_features = true
[export] [export]
include = [] include = []
exclude = [] exclude = []
[enum]
rename_variants = "QualifiedScreamingSnakeCase"

View file

@ -6,10 +6,10 @@ int main(void) {
if (connection == NULL) if (connection == NULL)
return 1; return 1;
SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
sp_pixel_grid_fill(pixels, true); sp_bitmap_fill(pixels, true);
SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED);
while (sp_connection_send_command(connection, sp_command_clone(command))); while (sp_connection_send_command(connection, sp_command_clone(command)));
sp_command_free(command); sp_command_free(command);

View file

@ -0,0 +1,281 @@
//! C functions for interacting with [SPBitmap]s
//!
//! prefix `sp_bitmap_`
use std::ptr::NonNull;
use servicepoint::{DataRef, Grid};
use crate::byte_slice::SPByteSlice;
/// A grid of pixels.
///
/// # Examples
///
/// ```C
/// Cp437Grid grid = sp_bitmap_new(8, 3);
/// sp_bitmap_fill(grid, true);
/// sp_bitmap_set(grid, 0, 0, false);
/// sp_bitmap_free(grid);
/// ```
pub struct SPBitmap(pub(crate) servicepoint::Bitmap);
/// Creates a new [SPBitmap] with the specified dimensions.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
///
/// # Panics
///
/// - when the width is not dividable by 8
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bitmap_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_new(
width: usize,
height: usize,
) -> NonNull<SPBitmap> {
let result = Box::new(SPBitmap(servicepoint::Bitmap::new(
width, height,
)));
NonNull::from(Box::leak(result))
}
/// Loads a [SPBitmap] with the specified dimensions from the provided data.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: [SPBitmap] that contains a copy of the provided data. Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
/// - when the dimensions and data size do not match exactly.
/// - when the width is not dividable by 8
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `data` points to a valid memory location of at least `data_length` bytes in size.
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bitmap_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> NonNull<SPBitmap> {
assert!(!data.is_null());
let data = std::slice::from_raw_parts(data, data_length);
let result = Box::new(SPBitmap(servicepoint::Bitmap::load(
width, height, data,
)));
NonNull::from(Box::leak(result))
}
/// Clones a [SPBitmap].
///
/// Will never return NULL.
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bitmap_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_clone(
bitmap: *const SPBitmap,
) -> NonNull<SPBitmap> {
assert!(!bitmap.is_null());
let result = Box::new(SPBitmap((*bitmap).0.clone()));
NonNull::from(Box::leak(result))
}
/// Deallocates a [SPBitmap].
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not used concurrently or after bitmap call
/// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) {
assert!(!bitmap.is_null());
_ = Box::from_raw(bitmap);
}
/// Gets the current value at the specified position in the [SPBitmap].
///
/// # Arguments
///
/// - `bitmap`: instance to read from
/// - `x` and `y`: position of the cell to read
///
/// # Panics
///
/// - when `bitmap` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_get(
bitmap: *const SPBitmap,
x: usize,
y: usize,
) -> bool {
assert!(!bitmap.is_null());
(*bitmap).0.get(x, y)
}
/// Sets the value of the specified position in the [SPBitmap].
///
/// # Arguments
///
/// - `bitmap`: instance to write to
/// - `x` and `y`: position of the cell
/// - `value`: the value to write to the cell
///
/// returns: old value of the cell
///
/// # Panics
///
/// - when `bitmap` is NULL
/// - when accessing `x` or `y` out of bounds
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_set(
bitmap: *mut SPBitmap,
x: usize,
y: usize,
value: bool,
) {
assert!(!bitmap.is_null());
(*bitmap).0.set(x, y, value);
}
/// Sets the state of all pixels in the [SPBitmap].
///
/// # Arguments
///
/// - `bitmap`: instance to write to
/// - `value`: the value to set all pixels to
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - `bitmap` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_fill(bitmap: *mut SPBitmap, value: bool) {
assert!(!bitmap.is_null());
(*bitmap).0.fill(value);
}
/// Gets the width in pixels of the [SPBitmap] instance.
///
/// # Arguments
///
/// - `bitmap`: instance to read from
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_width(bitmap: *const SPBitmap) -> usize {
assert!(!bitmap.is_null());
(*bitmap).0.width()
}
/// Gets the height in pixels of the [SPBitmap] instance.
///
/// # Arguments
///
/// - `bitmap`: instance to read from
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_height(bitmap: *const SPBitmap) -> usize {
assert!(!bitmap.is_null());
(*bitmap).0.height()
}
/// Gets an unsafe reference to the data of the [SPBitmap] instance.
///
/// # Panics
///
/// - when `bitmap` is NULL
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `bitmap` points to a valid [SPBitmap]
/// - the returned memory range is never accessed after the passed [SPBitmap] has been freed
/// - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref(
bitmap: *mut SPBitmap,
) -> SPByteSlice {
assert!(!bitmap.is_null());
let data = (*bitmap).0.data_ref_mut();
SPByteSlice {
start: NonNull::new(data.as_mut_ptr_range().start).unwrap(),
length: data.len(),
}
}

View file

@ -1,7 +1,8 @@
//! C functions for interacting with `SPBitVec`s //! C functions for interacting with [SPBitVec]s
//! //!
//! prefix `sp_bit_vec_` //! prefix `sp_bitvec_`
use std::ptr::NonNull;
use crate::SPByteSlice; use crate::SPByteSlice;
use servicepoint::bitvec::prelude::{BitVec, Msb0}; use servicepoint::bitvec::prelude::{BitVec, Msb0};
@ -9,9 +10,9 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0};
/// ///
/// # Examples /// # Examples
/// ```C /// ```C
/// SPBitVec vec = sp_bit_vec_new(8); /// SPBitVec vec = sp_bitvec_new(8);
/// sp_bit_vec_set(vec, 5, true); /// sp_bitvec_set(vec, 5, true);
/// sp_bit_vec_free(vec); /// sp_bitvec_free(vec);
/// ``` /// ```
pub struct SPBitVec(BitVec<u8, Msb0>); pub struct SPBitVec(BitVec<u8, Msb0>);
@ -33,30 +34,37 @@ impl Clone for SPBitVec {
} }
} }
/// Creates a new `SPBitVec` instance. /// Creates a new [SPBitVec] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `size`: size in bits. /// - `size`: size in bits.
/// ///
/// returns: `SPBitVec` with all bits set to false. Will never return NULL. /// returns: [SPBitVec] with all bits set to false. Will never return NULL.
/// ///
/// # Panics /// # Panics
/// ///
/// When `size` is not divisible by 8. /// - when `size` is not divisible by 8.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bit_vec_free`. /// by explicitly calling `sp_bitvec_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec { pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> {
Box::into_raw(Box::new(SPBitVec(BitVec::repeat(false, size)))) let result = Box::new(SPBitVec(BitVec::repeat(false, size)));
NonNull::from(Box::leak(result))
} }
/// Interpret the data as a series of bits and load then into a new `SPBitVec` instance. /// Interpret the data as a series of bits and load then into a new [SPBitVec] instance.
///
/// returns: [SPBitVec] instance containing data. Will never return NULL.
///
/// # Panics
///
/// - when `data` is NULL
/// ///
/// # Safety /// # Safety
/// ///
@ -65,48 +73,63 @@ pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec {
/// - `data` points to a valid memory location of at least `data_length` /// - `data` points to a valid memory location of at least `data_length`
/// bytes in size. /// bytes in size.
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bit_vec_free`. /// by explicitly calling `sp_bitvec_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_load( pub unsafe extern "C" fn sp_bitvec_load(
data: *const u8, data: *const u8,
data_length: usize, data_length: usize,
) -> *mut SPBitVec { ) -> NonNull<SPBitVec> {
assert!(!data.is_null());
let data = std::slice::from_raw_parts(data, data_length); let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(SPBitVec(BitVec::from_slice(data)))) let result = Box::new(SPBitVec(BitVec::from_slice(data)));
NonNull::from(Box::leak(result))
} }
/// Clones a `SPBitVec`. /// Clones a [SPBitVec].
///
/// returns: new [SPBitVec] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to concurrently /// - `bit_vec` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bit_vec_free`. /// by explicitly calling `sp_bitvec_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_clone( pub unsafe extern "C" fn sp_bitvec_clone(
bit_vec: *const SPBitVec, bit_vec: *const SPBitVec,
) -> *mut SPBitVec { ) -> NonNull<SPBitVec> {
Box::into_raw(Box::new((*bit_vec).clone())) assert!(!bit_vec.is_null());
let result = Box::new((*bit_vec).clone());
NonNull::from(Box::leak(result))
} }
/// Deallocates a `SPBitVec`. /// Deallocates a [SPBitVec].
///
/// # Panics
///
/// - when `but_vec` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` is not used concurrently or after this call
/// - `bit_vec` was not passed to another consuming function, e.g. to create a `SPCommand` /// - `bit_vec` was not passed to another consuming function, e.g. to create a [SPCommand]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) { pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) {
assert!(!bit_vec.is_null());
_ = Box::from_raw(bit_vec); _ = Box::from_raw(bit_vec);
} }
/// Gets the value of a bit from the `SPBitVec`. /// Gets the value of a bit from the [SPBitVec].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -117,23 +140,25 @@ pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) {
/// ///
/// # Panics /// # Panics
/// ///
/// When accessing `index` out of bounds. /// - when `bit_vec` is NULL
/// - when accessing `index` out of bounds
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to concurrently /// - `bit_vec` is not written to concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_get( pub unsafe extern "C" fn sp_bitvec_get(
bit_vec: *const SPBitVec, bit_vec: *const SPBitVec,
index: usize, index: usize,
) -> bool { ) -> bool {
assert!(!bit_vec.is_null());
*(*bit_vec).0.get(index).unwrap() *(*bit_vec).0.get(index).unwrap()
} }
/// Sets the value of a bit in the `SPBitVec`. /// Sets the value of a bit in the [SPBitVec].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -141,58 +166,68 @@ pub unsafe extern "C" fn sp_bit_vec_get(
/// - `index`: the bit index to edit /// - `index`: the bit index to edit
/// - `value`: the value to set the bit to /// - `value`: the value to set the bit to
/// ///
/// returns: old value of the bit
///
/// # Panics /// # Panics
/// ///
/// When accessing `index` out of bounds. /// - when `bit_vec` is NULL
/// - when accessing `index` out of bounds
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to or read from concurrently /// - `bit_vec` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_set( pub unsafe extern "C" fn sp_bitvec_set(
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
index: usize, index: usize,
value: bool, value: bool,
) { ) {
assert!(!bit_vec.is_null());
(*bit_vec).0.set(index, value) (*bit_vec).0.set(index, value)
} }
/// Sets the value of all bits in the `SPBitVec`. /// Sets the value of all bits in the [SPBitVec].
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `bit_vec`: instance to write to /// - `bit_vec`: instance to write to
/// - `value`: the value to set all bits to /// - `value`: the value to set all bits to
/// ///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - `bit_vec` is not written to or read from concurrently /// - `bit_vec` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_fill(bit_vec: *mut SPBitVec, value: bool) { pub unsafe extern "C" fn sp_bitvec_fill(bit_vec: *mut SPBitVec, value: bool) {
assert!(!bit_vec.is_null());
(*bit_vec).0.fill(value) (*bit_vec).0.fill(value)
} }
/// Gets the length of the `SPBitVec` in bits. /// Gets the length of the [SPBitVec] in bits.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `bit_vec`: instance to write to /// - `bit_vec`: instance to write to
/// ///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_len(bit_vec: *const SPBitVec) -> usize { pub unsafe extern "C" fn sp_bitvec_len(bit_vec: *const SPBitVec) -> usize {
assert!(!bit_vec.is_null());
(*bit_vec).0.len() (*bit_vec).0.len()
} }
@ -202,36 +237,46 @@ pub unsafe extern "C" fn sp_bit_vec_len(bit_vec: *const SPBitVec) -> usize {
/// ///
/// - `bit_vec`: instance to write to /// - `bit_vec`: instance to write to
/// ///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_is_empty(bit_vec: *const SPBitVec) -> bool { pub unsafe extern "C" fn sp_bitvec_is_empty(bit_vec: *const SPBitVec) -> bool {
assert!(!bit_vec.is_null());
(*bit_vec).0.is_empty() (*bit_vec).0.is_empty()
} }
/// Gets an unsafe reference to the data of the `SPBitVec` instance. /// Gets an unsafe reference to the data of the [SPBitVec] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `bit_vec`: instance to write to /// - `bit_vec`: instance to write to
/// ///
/// # Panics
///
/// - when `bit_vec` is NULL
///
/// ## Safety /// ## Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid `SPBitVec` /// - `bit_vec` points to a valid [SPBitVec]
/// - the returned memory range is never accessed after the passed `SPBitVec` has been freed /// - the returned memory range is never accessed after the passed [SPBitVec] has been freed
/// - the returned memory range is never accessed concurrently, either via the `SPBitVec` or directly /// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref( pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref(
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
) -> SPByteSlice { ) -> SPByteSlice {
assert!(!bit_vec.is_null());
let data = (*bit_vec).0.as_raw_mut_slice(); let data = (*bit_vec).0.as_raw_mut_slice();
SPByteSlice { SPByteSlice {
start: data.as_mut_ptr_range().start, start: NonNull::new(data.as_mut_ptr_range().start).unwrap(),
length: data.len(), length: data.len(),
} }
} }

View file

@ -1,10 +1,19 @@
//! C functions for interacting with `SPBrightnessGrid`s //! C functions for interacting with [SPBrightnessGrid]s
//! //!
//! prefix `sp_brightness_grid_` //! prefix `sp_brightness_grid_`
use crate::SPByteSlice; use crate::SPByteSlice;
use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid};
use std::convert::Into;
use std::intrinsics::transmute; use std::intrinsics::transmute;
use std::ptr::NonNull;
/// see [Brightness::MIN]
pub const SP_BRIGHTNESS_MIN: u8 = 0;
/// see [Brightness::MAX]
pub const SP_BRIGHTNESS_MAX: u8 = 11;
/// Count of possible brightness values
pub const SP_BRIGHTNESS_LEVELS: u8 = 12;
/// A grid containing brightness values. /// A grid containing brightness values.
/// ///
@ -21,17 +30,12 @@ use std::intrinsics::transmute;
/// SPCommand command = sp_command_char_brightness(grid); /// SPCommand command = sp_command_char_brightness(grid);
/// sp_connection_free(connection); /// sp_connection_free(connection);
/// ``` /// ```
#[derive(Clone)]
pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid);
impl Clone for SPBrightnessGrid { /// Creates a new [SPBrightnessGrid] with the specified dimensions.
fn clone(&self) -> Self {
SPBrightnessGrid(self.0.clone())
}
}
/// Creates a new `SPBrightnessGrid` with the specified dimensions.
/// ///
/// returns: `SPBrightnessGrid` initialized to 0. Will never return NULL. /// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL.
/// ///
/// # Safety /// # Safety
/// ///
@ -43,17 +47,21 @@ impl Clone for SPBrightnessGrid {
pub unsafe extern "C" fn sp_brightness_grid_new( pub unsafe extern "C" fn sp_brightness_grid_new(
width: usize, width: usize,
height: usize, height: usize,
) -> *mut SPBrightnessGrid { ) -> NonNull<SPBrightnessGrid> {
Box::into_raw(Box::new(SPBrightnessGrid( let result = Box::new(SPBrightnessGrid(
servicepoint::BrightnessGrid::new(width, height), servicepoint::BrightnessGrid::new(width, height),
))) ));
NonNull::from(Box::leak(result))
} }
/// Loads a `SPBrightnessGrid` with the specified dimensions from the provided data. /// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data.
///
/// returns: new [SPBrightnessGrid] instance. Will never return NULL.
/// ///
/// # Panics /// # Panics
/// ///
/// When the provided `data_length` is not sufficient for the `height` and `width` /// - when `data` is NULL
/// - when the provided `data_length` does not match `height` and `width`
/// ///
/// # Safety /// # Safety
/// ///
@ -69,52 +77,67 @@ pub unsafe extern "C" fn sp_brightness_grid_load(
height: usize, height: usize,
data: *const u8, data: *const u8,
data_length: usize, data_length: usize,
) -> *mut SPBrightnessGrid { ) -> NonNull<SPBrightnessGrid> {
assert!(!data.is_null());
let data = std::slice::from_raw_parts(data, data_length); let data = std::slice::from_raw_parts(data, data_length);
let grid = PrimitiveGrid::load(width, height, data); let grid = PrimitiveGrid::load(width, height, data);
let grid = servicepoint::BrightnessGrid::try_from(grid) let grid = servicepoint::BrightnessGrid::try_from(grid)
.expect("invalid brightness value"); .expect("invalid brightness value");
Box::into_raw(Box::new(SPBrightnessGrid(grid))) let result = Box::new(SPBrightnessGrid(grid));
NonNull::from(Box::leak(result))
} }
/// Clones a `SPBrightnessGrid`. /// Clones a [SPBrightnessGrid].
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// ///
/// returns: new [SPBrightnessGrid] instance. Will never return NULL.
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to concurrently /// - `brightness_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_free`. /// by explicitly calling `sp_brightness_grid_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_clone( pub unsafe extern "C" fn sp_brightness_grid_clone(
brightness_grid: *const SPBrightnessGrid, brightness_grid: *const SPBrightnessGrid,
) -> *mut SPBrightnessGrid { ) -> NonNull<SPBrightnessGrid> {
Box::into_raw(Box::new((*brightness_grid).clone())) assert!(!brightness_grid.is_null());
let result = Box::new((*brightness_grid).clone());
NonNull::from(Box::leak(result))
} }
/// Deallocates a `SPBrightnessGrid`. /// Deallocates a [SPBrightnessGrid].
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// ///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not used concurrently or after this call /// - `brightness_grid` is not used concurrently or after this call
/// - `brightness_grid` was not passed to another consuming function, e.g. to create a `SPCommand` /// - `brightness_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_free( pub unsafe extern "C" fn sp_brightness_grid_free(
brightness_grid: *mut SPBrightnessGrid, brightness_grid: *mut SPBrightnessGrid,
) { ) {
assert!(!brightness_grid.is_null());
_ = Box::from_raw(brightness_grid); _ = Box::from_raw(brightness_grid);
} }
@ -125,15 +148,18 @@ pub unsafe extern "C" fn sp_brightness_grid_free(
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// - `x` and `y`: position of the cell to read /// - `x` and `y`: position of the cell to read
/// ///
/// returns: value at position
///
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// - when `brightness_grid` is NULL
/// - When accessing `x` or `y` out of bounds.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to concurrently /// - `brightness_grid` is not written to concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_get( pub unsafe extern "C" fn sp_brightness_grid_get(
@ -141,10 +167,11 @@ pub unsafe extern "C" fn sp_brightness_grid_get(
x: usize, x: usize,
y: usize, y: usize,
) -> u8 { ) -> u8 {
assert!(!brightness_grid.is_null());
(*brightness_grid).0.get(x, y).into() (*brightness_grid).0.get(x, y).into()
} }
/// Sets the value of the specified position in the `SPBrightnessGrid`. /// Sets the value of the specified position in the [SPBrightnessGrid].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -156,6 +183,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get(
/// ///
/// # Panics /// # Panics
/// ///
/// - when `brightness_grid` is NULL
/// - When accessing `x` or `y` out of bounds. /// - When accessing `x` or `y` out of bounds.
/// - When providing an invalid brightness value /// - When providing an invalid brightness value
/// ///
@ -163,7 +191,7 @@ pub unsafe extern "C" fn sp_brightness_grid_get(
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBitVec` /// - `brightness_grid` points to a valid [SPBitVec]
/// - `brightness_grid` is not written to or read from concurrently /// - `brightness_grid` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_set( pub unsafe extern "C" fn sp_brightness_grid_set(
@ -172,12 +200,13 @@ pub unsafe extern "C" fn sp_brightness_grid_set(
y: usize, y: usize,
value: u8, value: u8,
) { ) {
assert!(!brightness_grid.is_null());
let brightness = let brightness =
Brightness::try_from(value).expect("invalid brightness value"); Brightness::try_from(value).expect("invalid brightness value");
(*brightness_grid).0.set(x, y, brightness); (*brightness_grid).0.set(x, y, brightness);
} }
/// Sets the value of all cells in the `SPBrightnessGrid`. /// Sets the value of all cells in the [SPBrightnessGrid].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -186,83 +215,106 @@ pub unsafe extern "C" fn sp_brightness_grid_set(
/// ///
/// # Panics /// # Panics
/// ///
/// - when `brightness_grid` is NULL
/// - When providing an invalid brightness value /// - When providing an invalid brightness value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to or read from concurrently /// - `brightness_grid` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_fill( pub unsafe extern "C" fn sp_brightness_grid_fill(
brightness_grid: *mut SPBrightnessGrid, brightness_grid: *mut SPBrightnessGrid,
value: u8, value: u8,
) { ) {
assert!(!brightness_grid.is_null());
let brightness = let brightness =
Brightness::try_from(value).expect("invalid brightness value"); Brightness::try_from(value).expect("invalid brightness value");
(*brightness_grid).0.fill(brightness); (*brightness_grid).0.fill(brightness);
} }
/// Gets the width of the `SPBrightnessGrid` instance. /// Gets the width of the [SPBrightnessGrid] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// ///
/// returns: width
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_width( pub unsafe extern "C" fn sp_brightness_grid_width(
brightness_grid: *const SPBrightnessGrid, brightness_grid: *const SPBrightnessGrid,
) -> usize { ) -> usize {
assert!(!brightness_grid.is_null());
(*brightness_grid).0.width() (*brightness_grid).0.width()
} }
/// Gets the height of the `SPBrightnessGrid` instance. /// Gets the height of the [SPBrightnessGrid] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// ///
/// returns: height
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_height( pub unsafe extern "C" fn sp_brightness_grid_height(
brightness_grid: *const SPBrightnessGrid, brightness_grid: *const SPBrightnessGrid,
) -> usize { ) -> usize {
assert!(!brightness_grid.is_null());
(*brightness_grid).0.height() (*brightness_grid).0.height()
} }
/// Gets an unsafe reference to the data of the `SPBrightnessGrid` instance. /// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `brightness_grid`: instance to read from /// - `brightness_grid`: instance to read from
/// ///
/// ## Safety /// returns: slice of bytes underlying the `brightness_grid`.
///
/// # Panics
///
/// - when `brightness_grid` is NULL
///
/// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `brightness_grid` points to a valid `SPBrightnessGrid` /// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - the returned memory range is never accessed after the passed `SPBrightnessGrid` has been freed /// - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed
/// - the returned memory range is never accessed concurrently, either via the `SPBrightnessGrid` or directly /// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref(
brightness_grid: *mut SPBrightnessGrid, brightness_grid: *mut SPBrightnessGrid,
) -> SPByteSlice { ) -> SPByteSlice {
assert!(!brightness_grid.is_null());
assert_eq!(core::mem::size_of::<Brightness>(), 1); assert_eq!(core::mem::size_of::<Brightness>(), 1);
let data = (*brightness_grid).0.data_ref_mut(); let data = (*brightness_grid).0.data_ref_mut();
// this assumes more about the memory layout than rust guarantees. yikes!
let data: &mut [u8] = transmute(data); let data: &mut [u8] = transmute(data);
SPByteSlice { SPByteSlice {
start: data.as_mut_ptr_range().start, start: NonNull::new(data.as_mut_ptr_range().start).unwrap(),
length: data.len(), length: data.len(),
} }
} }

View file

@ -1,5 +1,7 @@
//! FFI slice helper //! FFI slice helper
use std::ptr::NonNull;
#[repr(C)] #[repr(C)]
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
/// ///
@ -16,7 +18,7 @@
/// will try to free the memory of a potentially separate allocator. /// will try to free the memory of a potentially separate allocator.
pub struct SPByteSlice { pub struct SPByteSlice {
/// The start address of the memory /// The start address of the memory
pub start: *mut u8, pub start: NonNull<u8>,
/// The amount of memory in bytes /// The amount of memory in bytes
pub length: usize, pub length: usize,
} }

View file

@ -1,21 +1,21 @@
//! C functions for interacting with `SPCommand`s //! C functions for interacting with [SPCommand]s
//! //!
//! prefix `sp_command_` //! prefix `sp_command_`
use std::ptr::null_mut; use std::ptr::{null_mut, NonNull};
use servicepoint::{Brightness, Origin}; use servicepoint::{Brightness, Origin};
use crate::{ use crate::{
SPBitVec, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, SPPacket, SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid,
SPPixelGrid, SPPacket,
}; };
/// A low-level display command. /// A low-level display command.
/// ///
/// This struct and associated functions implement the UDP protocol for the display. /// This struct and associated functions implement the UDP protocol for the display.
/// ///
/// To send a `SPCommand`, use a `SPConnection`. /// To send a [SPCommand], use a [SPConnection].
/// ///
/// # Examples /// # Examples
/// ///
@ -31,20 +31,24 @@ impl Clone for SPCommand {
} }
} }
/// Tries to turn a `SPPacket` into a `SPCommand`. /// Tries to turn a [SPPacket] into a [SPCommand].
/// ///
/// The packet is deallocated in the process. /// The packet is deallocated in the process.
/// ///
/// Returns: pointer to new `SPCommand` instance or NULL /// Returns: pointer to new [SPCommand] instance or NULL if parsing failed.
///
/// # Panics
///
/// - when `packet` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `SPPacket` points to a valid instance of `SPPacket` /// - [SPPacket] points to a valid instance of [SPPacket]
/// - `SPPacket` is not used concurrently or after this call /// - [SPPacket] is not used concurrently or after this call
/// - the result is checked for NULL /// - the result is checked for NULL
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_try_from_packet( pub unsafe extern "C" fn sp_command_try_from_packet(
@ -57,28 +61,36 @@ pub unsafe extern "C" fn sp_command_try_from_packet(
} }
} }
/// Clones a `SPCommand` instance. /// Clones a [SPCommand] instance.
///
/// returns: new [SPCommand] instance. Will never return NULL.
///
/// # Panics
///
/// - when `command` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `command` points to a valid instance of `SPCommand` /// - `command` points to a valid instance of [SPCommand]
/// - `command` is not written to concurrently /// - `command` is not written to concurrently
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_clone( pub unsafe extern "C" fn sp_command_clone(
command: *const SPCommand, command: *const SPCommand,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
Box::into_raw(Box::new((*command).clone())) assert!(!command.is_null());
let result = Box::new((*command).clone());
NonNull::from(Box::leak(result))
} }
/// Set all pixels to the off state. /// Set all pixels to the off state.
/// ///
/// Does not affect brightness. /// Does not affect brightness.
/// ///
/// Returns: a new `Command::Clear` instance. Will never return NULL. /// Returns: a new [Command::Clear] instance. Will never return NULL.
/// ///
/// # Examples /// # Examples
/// ///
@ -90,28 +102,30 @@ pub unsafe extern "C" fn sp_command_clone(
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_clear() -> *mut SPCommand { pub unsafe extern "C" fn sp_command_clear() -> NonNull<SPCommand> {
Box::into_raw(Box::new(SPCommand(servicepoint::Command::Clear))) let result = Box::new(SPCommand(servicepoint::Command::Clear));
NonNull::from(Box::leak(result))
} }
/// Kills the udp daemon on the display, which usually results in a restart. /// Kills the udp daemon on the display, which usually results in a restart.
/// ///
/// Please do not send this in your normal program flow. /// Please do not send this in your normal program flow.
/// ///
/// Returns: a new `Command::HardReset` instance. Will never return NULL. /// Returns: a new [Command::HardReset] instance. Will never return NULL.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand { pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<SPCommand> {
Box::into_raw(Box::new(SPCommand(servicepoint::Command::HardReset))) let result = Box::new(SPCommand(servicepoint::Command::HardReset));
NonNull::from(Box::leak(result))
} }
/// A yet-to-be-tested command. /// A yet-to-be-tested command.
@ -122,16 +136,17 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand {
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> {
Box::into_raw(Box::new(SPCommand(servicepoint::Command::FadeOut))) let result = Box::new(SPCommand(servicepoint::Command::FadeOut));
NonNull::from(Box::leak(result))
} }
/// Set the brightness of all tiles to the same value. /// Set the brightness of all tiles to the same value.
/// ///
/// Returns: a new `Command::Brightness` instance. Will never return NULL. /// Returns: a new [Command::Brightness] instance. Will never return NULL.
/// ///
/// # Panics /// # Panics
/// ///
@ -141,44 +156,50 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand {
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_brightness( pub unsafe extern "C" fn sp_command_brightness(
brightness: u8, brightness: u8,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
let brightness = let brightness =
Brightness::try_from(brightness).expect("invalid brightness"); Brightness::try_from(brightness).expect("invalid brightness");
Box::into_raw(Box::new(SPCommand(servicepoint::Command::Brightness( let result = Box::new(SPCommand(
brightness, servicepoint::Command::Brightness(brightness),
)))) ));
NonNull::from(Box::leak(result))
} }
/// Set the brightness of individual tiles in a rectangular area of the display. /// Set the brightness of individual tiles in a rectangular area of the display.
/// ///
/// The passed `SPBrightnessGrid` gets consumed. /// The passed [SPBrightnessGrid] gets consumed.
/// ///
/// Returns: a new `Command::CharBrightness` instance. Will never return NULL. /// Returns: a new [Command::CharBrightness] instance. Will never return NULL.
///
/// # Panics
///
/// - when `grid` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `grid` points to a valid instance of `SPBrightnessGrid` /// - `grid` points to a valid instance of [SPBrightnessGrid]
/// - `grid` is not used concurrently or after this call /// - `grid` is not used concurrently or after this call
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_char_brightness( pub unsafe extern "C" fn sp_command_char_brightness(
x: usize, x: usize,
y: usize, y: usize,
grid: *mut SPBrightnessGrid, grid: *mut SPBrightnessGrid,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!grid.is_null());
let byte_grid = *Box::from_raw(grid); let byte_grid = *Box::from_raw(grid);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::CharBrightness( let result = Box::new(SPCommand(
Origin::new(x, y), servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0),
byte_grid.0, ));
)))) NonNull::from(Box::leak(result))
} }
/// Set pixel data starting at the pixel offset on screen. /// Set pixel data starting at the pixel offset on screen.
@ -186,33 +207,42 @@ pub unsafe extern "C" fn sp_command_char_brightness(
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0. /// once the starting row is full, overwriting will continue on column 0.
/// ///
/// The contained `SPBitVec` is always uncompressed. /// The contained [SPBitVec] is always uncompressed.
/// ///
/// The passed `SPBitVec` gets consumed. /// The passed [SPBitVec] gets consumed.
/// ///
/// Returns: a new `Command::BitmapLinear` instance. Will never return NULL. /// Returns: a new [Command::BitmapLinear] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid instance of `SPBitVec` /// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values /// - `compression` matches one of the allowed enum values
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear( pub unsafe extern "C" fn sp_command_bitmap_linear(
offset: usize, offset: usize,
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
compression: SPCompressionCode, compression: SPCompressionCode,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!bit_vec.is_null());
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinear( let result = Box::new(SPCommand(
offset, servicepoint::Command::BitmapLinear(
bit_vec.into(), offset,
compression.try_into().expect("invalid compression code"), bit_vec.into(),
)))) compression.try_into().expect("invalid compression code"),
),
));
NonNull::from(Box::leak(result))
} }
/// Set pixel data according to an and-mask starting at the offset. /// Set pixel data according to an and-mask starting at the offset.
@ -220,33 +250,42 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0. /// once the starting row is full, overwriting will continue on column 0.
/// ///
/// The contained `SPBitVec` is always uncompressed. /// The contained [SPBitVec] is always uncompressed.
/// ///
/// The passed `SPBitVec` gets consumed. /// The passed [SPBitVec] gets consumed.
/// ///
/// Returns: a new `Command::BitmapLinearAnd` instance. Will never return NULL. /// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid instance of `SPBitVec` /// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values /// - `compression` matches one of the allowed enum values
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_and( pub unsafe extern "C" fn sp_command_bitmap_linear_and(
offset: usize, offset: usize,
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
compression: SPCompressionCode, compression: SPCompressionCode,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!bit_vec.is_null());
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( let result = Box::new(SPCommand(
offset, servicepoint::Command::BitmapLinearAnd(
bit_vec.into(), offset,
compression.try_into().expect("invalid compression code"), bit_vec.into(),
)))) compression.try_into().expect("invalid compression code"),
),
));
NonNull::from(Box::leak(result))
} }
/// Set pixel data according to an or-mask starting at the offset. /// Set pixel data according to an or-mask starting at the offset.
@ -254,33 +293,42 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0. /// once the starting row is full, overwriting will continue on column 0.
/// ///
/// The contained `SPBitVec` is always uncompressed. /// The contained [SPBitVec] is always uncompressed.
/// ///
/// The passed `SPBitVec` gets consumed. /// The passed [SPBitVec] gets consumed.
/// ///
/// Returns: a new `Command::BitmapLinearOr` instance. Will never return NULL. /// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid instance of `SPBitVec` /// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values /// - `compression` matches one of the allowed enum values
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_or( pub unsafe extern "C" fn sp_command_bitmap_linear_or(
offset: usize, offset: usize,
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
compression: SPCompressionCode, compression: SPCompressionCode,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!bit_vec.is_null());
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( let result = Box::new(SPCommand(
offset, servicepoint::Command::BitmapLinearOr(
bit_vec.into(), offset,
compression.try_into().expect("invalid compression code"), bit_vec.into(),
)))) compression.try_into().expect("invalid compression code"),
),
));
NonNull::from(Box::leak(result))
} }
/// Set pixel data according to a xor-mask starting at the offset. /// Set pixel data according to a xor-mask starting at the offset.
@ -288,100 +336,118 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0. /// once the starting row is full, overwriting will continue on column 0.
/// ///
/// The contained `SPBitVec` is always uncompressed. /// The contained [SPBitVec] is always uncompressed.
/// ///
/// The passed `SPBitVec` gets consumed. /// The passed [SPBitVec] gets consumed.
/// ///
/// Returns: a new `Command::BitmapLinearXor` instance. Will never return NULL. /// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bit_vec` is null
/// - when `compression_code` is not a valid value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `bit_vec` points to a valid instance of `SPBitVec` /// - `bit_vec` points to a valid instance of [SPBitVec]
/// - `bit_vec` is not used concurrently or after this call /// - `bit_vec` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values /// - `compression` matches one of the allowed enum values
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_xor( pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
offset: usize, offset: usize,
bit_vec: *mut SPBitVec, bit_vec: *mut SPBitVec,
compression: SPCompressionCode, compression: SPCompressionCode,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!bit_vec.is_null());
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( let result = Box::new(SPCommand(
offset, servicepoint::Command::BitmapLinearXor(
bit_vec.into(), offset,
compression.try_into().expect("invalid compression code"), bit_vec.into(),
)))) compression.try_into().expect("invalid compression code"),
),
));
NonNull::from(Box::leak(result))
} }
/// Show text on the screen. /// Show text on the screen.
/// ///
/// <div class="warning"> /// The passed [SPCp437Grid] gets consumed.
/// The library does not currently convert between UTF-8 and CP-437.
/// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
/// </div>
/// ///
/// The passed `SPCp437Grid` gets consumed./// /// Returns: a new [Command::Cp437Data] instance. Will never return NULL.
/// ///
/// Returns: a new `Command::Cp437Data` instance. Will never return NULL. /// # Panics
///
/// - when `grid` is null
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `grid` points to a valid instance of `SPCp437Grid` /// - `grid` points to a valid instance of [SPCp437Grid]
/// - `grid` is not used concurrently or after this call /// - `grid` is not used concurrently or after this call
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_cp437_data( pub unsafe extern "C" fn sp_command_cp437_data(
x: usize, x: usize,
y: usize, y: usize,
grid: *mut SPCp437Grid, grid: *mut SPCp437Grid,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
assert!(!grid.is_null());
let grid = *Box::from_raw(grid); let grid = *Box::from_raw(grid);
Box::into_raw(Box::new(SPCommand(servicepoint::Command::Cp437Data( let result = Box::new(SPCommand(
Origin::new(x, y), servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0),
grid.0, ));
)))) NonNull::from(Box::leak(result))
} }
/// Sets a window of pixels to the specified values. /// Sets a window of pixels to the specified values.
/// ///
/// The passed `SPPixelGrid` gets consumed. /// The passed [SPBitmap] gets consumed.
/// ///
/// Returns: a new `Command::BitmapLinearWin` instance. Will never return NULL. /// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL.
///
/// # Panics
///
/// - when `bitmap` is null
/// - when `compression_code` is not a valid value
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `pixel_grid` points to a valid instance of `SPPixelGrid` /// - `bitmap` points to a valid instance of [SPBitmap]
/// - `pixel_grid` is not used concurrently or after this call /// - `bitmap` is not used concurrently or after this call
/// - `compression` matches one of the allowed enum values /// - `compression` matches one of the allowed enum values
/// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_free`. /// by explicitly calling `sp_command_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_win( pub unsafe extern "C" fn sp_command_bitmap_linear_win(
x: usize, x: usize,
y: usize, y: usize,
pixel_grid: *mut SPPixelGrid, bitmap: *mut SPBitmap,
compression_code: SPCompressionCode, compression_code: SPCompressionCode,
) -> *mut SPCommand { ) -> NonNull<SPCommand> {
let byte_grid = (*Box::from_raw(pixel_grid)).0; assert!(!bitmap.is_null());
Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( let byte_grid = (*Box::from_raw(bitmap)).0;
Origin::new(x, y), let result = Box::new(SPCommand(
byte_grid, servicepoint::Command::BitmapLinearWin(
compression_code Origin::new(x, y),
.try_into() byte_grid,
.expect("invalid compression code"), compression_code
)))) .try_into()
.expect("invalid compression code"),
),
));
NonNull::from(Box::leak(result))
} }
/// Deallocates a `SPCommand`. /// Deallocates a [SPCommand].
/// ///
/// # Examples /// # Examples
/// ///
@ -390,14 +456,19 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win(
/// sp_command_free(c); /// sp_command_free(c);
/// ``` /// ```
/// ///
/// # Panics
///
/// - when `command` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `command` points to a valid `SPCommand` /// - `command` points to a valid [SPCommand]
/// - `command` is not used concurrently or after this call /// - `command` is not used concurrently or after this call
/// - `command` was not passed to another consuming function, e.g. to create a `SPPacket` /// - `command` was not passed to another consuming function, e.g. to create a [SPPacket]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) {
assert!(!command.is_null());
_ = Box::from_raw(command); _ = Box::from_raw(command);
} }

View file

@ -1,4 +1,4 @@
//! C functions for interacting with `SPConnection`s //! C functions for interacting with [SPConnection]s
//! //!
//! prefix `sp_connection_` //! prefix `sp_connection_`
@ -18,13 +18,13 @@ use crate::{SPCommand, SPPacket};
/// ``` /// ```
pub struct SPConnection(pub(crate) servicepoint::Connection); pub struct SPConnection(pub(crate) servicepoint::Connection);
/// Creates a new instance of `SPConnection`. /// Creates a new instance of [SPConnection].
/// ///
/// returns: NULL if connection fails, or connected instance /// returns: NULL if connection fails, or connected instance
/// ///
/// # Panics /// # Panics
/// ///
/// Bad string encoding /// - when `host` is null or an invalid host
/// ///
/// # Safety /// # Safety
/// ///
@ -36,6 +36,7 @@ pub struct SPConnection(pub(crate) servicepoint::Connection);
pub unsafe extern "C" fn sp_connection_open( pub unsafe extern "C" fn sp_connection_open(
host: *const c_char, host: *const c_char,
) -> *mut SPConnection { ) -> *mut SPConnection {
assert!(!host.is_null());
let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); let host = CStr::from_ptr(host).to_str().expect("Bad encoding");
let connection = match servicepoint::Connection::open(host) { let connection = match servicepoint::Connection::open(host) {
Err(_) => return null_mut(), Err(_) => return null_mut(),
@ -45,59 +46,78 @@ pub unsafe extern "C" fn sp_connection_open(
Box::into_raw(Box::new(SPConnection(connection))) Box::into_raw(Box::new(SPConnection(connection)))
} }
/// Sends a `SPPacket` to the display using the `SPConnection`. /// Sends a [SPPacket] to the display using the [SPConnection].
/// ///
/// The passed `packet` gets consumed. /// The passed `packet` gets consumed.
/// ///
/// returns: true in case of success /// returns: true in case of success
/// ///
/// # Panics
///
/// - when `connection` is NULL
/// - when `packet` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `connection` points to a valid instance of `SPConnection` /// - `connection` points to a valid instance of [SPConnection]
/// - `packet` points to a valid instance of `SPPacket` /// - `packet` points to a valid instance of [SPPacket]
/// - `packet` is not used concurrently or after this call /// - `packet` is not used concurrently or after this call
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_connection_send_packet( pub unsafe extern "C" fn sp_connection_send_packet(
connection: *const SPConnection, connection: *const SPConnection,
packet: *mut SPPacket, packet: *mut SPPacket,
) -> bool { ) -> bool {
assert!(!connection.is_null());
assert!(!packet.is_null());
let packet = Box::from_raw(packet); let packet = Box::from_raw(packet);
(*connection).0.send((*packet).0).is_ok() (*connection).0.send((*packet).0).is_ok()
} }
/// Sends a `SPCommand` to the display using the `SPConnection`. /// Sends a [SPCommand] to the display using the [SPConnection].
/// ///
/// The passed `command` gets consumed. /// The passed `command` gets consumed.
/// ///
/// returns: true in case of success /// returns: true in case of success
/// ///
/// # Panics
///
/// - when `connection` is NULL
/// - when `command` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `connection` points to a valid instance of `SPConnection` /// - `connection` points to a valid instance of [SPConnection]
/// - `command` points to a valid instance of `SPPacket` /// - `command` points to a valid instance of [SPPacket]
/// - `command` is not used concurrently or after this call /// - `command` is not used concurrently or after this call
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_connection_send_command( pub unsafe extern "C" fn sp_connection_send_command(
connection: *const SPConnection, connection: *const SPConnection,
command: *mut SPCommand, command: *mut SPCommand,
) -> bool { ) -> bool {
assert!(!connection.is_null());
assert!(!command.is_null());
let command = (*Box::from_raw(command)).0; let command = (*Box::from_raw(command)).0;
(*connection).0.send(command).is_ok() (*connection).0.send(command).is_ok()
} }
/// Closes and deallocates a `SPConnection`. /// Closes and deallocates a [SPConnection].
///
/// # Panics
///
/// - when `connection` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `connection` points to a valid `SPConnection` /// - `connection` points to a valid [SPConnection]
/// - `connection` is not used concurrently or after this call /// - `connection` is not used concurrently or after this call
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) {
assert!(!connection.is_null());
_ = Box::from_raw(connection); _ = Box::from_raw(connection);
} }

View file

@ -1,7 +1,8 @@
//! C functions for interacting with `SPCp437Grid`s //! C functions for interacting with [SPCp437Grid]s
//! //!
//! prefix `sp_cp437_grid_` //! prefix `sp_cp437_grid_`
use std::ptr::NonNull;
use crate::SPByteSlice; use crate::SPByteSlice;
use servicepoint::{DataRef, Grid}; use servicepoint::{DataRef, Grid};
@ -25,9 +26,9 @@ impl Clone for SPCp437Grid {
} }
} }
/// Creates a new `SPCp437Grid` with the specified dimensions. /// Creates a new [SPCp437Grid] with the specified dimensions.
/// ///
/// returns: `SPCp437Grid` initialized to 0. /// returns: [SPCp437Grid] initialized to 0. Will never return NULL.
/// ///
/// # Safety /// # Safety
/// ///
@ -39,19 +40,21 @@ impl Clone for SPCp437Grid {
pub unsafe extern "C" fn sp_cp437_grid_new( pub unsafe extern "C" fn sp_cp437_grid_new(
width: usize, width: usize,
height: usize, height: usize,
) -> *mut SPCp437Grid { ) -> NonNull<SPCp437Grid> {
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new( let result = Box::new(SPCp437Grid(
width, height, servicepoint::Cp437Grid::new(width, height),
)))) ));
NonNull::from(Box::leak(result))
} }
/// Loads a `SPCp437Grid` with the specified dimensions from the provided data. /// Loads a [SPCp437Grid] with the specified dimensions from the provided data.
/// ///
/// Will never return NULL. /// Will never return NULL.
/// ///
/// # Panics /// # Panics
/// ///
/// When the provided `data_length` is not sufficient for the `height` and `width` /// - when `data` is NULL
/// - when the provided `data_length` does not match `height` and `width`
/// ///
/// # Safety /// # Safety
/// ///
@ -67,43 +70,56 @@ pub unsafe extern "C" fn sp_cp437_grid_load(
height: usize, height: usize,
data: *const u8, data: *const u8,
data_length: usize, data_length: usize,
) -> *mut SPCp437Grid { ) -> NonNull<SPCp437Grid> {
assert!(data.is_null());
let data = std::slice::from_raw_parts(data, data_length); let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( let result = Box::new(SPCp437Grid(
width, height, data, servicepoint::Cp437Grid::load(width, height, data),
)))) ));
NonNull::from(Box::leak(result))
} }
/// Clones a `SPCp437Grid`. /// Clones a [SPCp437Grid].
/// ///
/// Will never return NULL. /// Will never return NULL.
/// ///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to concurrently /// - `cp437_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_cp437_grid_free`. /// by explicitly calling `sp_cp437_grid_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_clone( pub unsafe extern "C" fn sp_cp437_grid_clone(
cp437_grid: *const SPCp437Grid, cp437_grid: *const SPCp437Grid,
) -> *mut SPCp437Grid { ) -> NonNull<SPCp437Grid> {
Box::into_raw(Box::new((*cp437_grid).clone())) assert!(!cp437_grid.is_null());
let result = Box::new((*cp437_grid).clone());
NonNull::from(Box::leak(result))
} }
/// Deallocates a `SPCp437Grid`. /// Deallocates a [SPCp437Grid].
///
/// # Panics
///
/// - when `cp437_grid` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not used concurrently or after cp437_grid call /// - `cp437_grid` is not used concurrently or after cp437_grid call
/// - `cp437_grid` was not passed to another consuming function, e.g. to create a `SPCommand` /// - `cp437_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) {
assert!(!cp437_grid.is_null());
_ = Box::from_raw(cp437_grid); _ = Box::from_raw(cp437_grid);
} }
@ -116,13 +132,14 @@ pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) {
/// ///
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// - when `cp437_grid` is NULL
/// - when accessing `x` or `y` out of bounds
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to concurrently /// - `cp437_grid` is not written to concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_get( pub unsafe extern "C" fn sp_cp437_grid_get(
@ -130,10 +147,11 @@ pub unsafe extern "C" fn sp_cp437_grid_get(
x: usize, x: usize,
y: usize, y: usize,
) -> u8 { ) -> u8 {
assert!(!cp437_grid.is_null());
(*cp437_grid).0.get(x, y) (*cp437_grid).0.get(x, y)
} }
/// Sets the value of the specified position in the `SPCp437Grid`. /// Sets the value of the specified position in the [SPCp437Grid].
/// ///
/// # Arguments /// # Arguments
/// ///
@ -145,13 +163,14 @@ pub unsafe extern "C" fn sp_cp437_grid_get(
/// ///
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// - when `cp437_grid` is NULL
/// - when accessing `x` or `y` out of bounds
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPBitVec` /// - `cp437_grid` points to a valid [SPBitVec]
/// - `cp437_grid` is not written to or read from concurrently /// - `cp437_grid` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_set( pub unsafe extern "C" fn sp_cp437_grid_set(
@ -160,84 +179,104 @@ pub unsafe extern "C" fn sp_cp437_grid_set(
y: usize, y: usize,
value: u8, value: u8,
) { ) {
assert!(!cp437_grid.is_null());
(*cp437_grid).0.set(x, y, value); (*cp437_grid).0.set(x, y, value);
} }
/// Sets the value of all cells in the `SPCp437Grid`. /// Sets the value of all cells in the [SPCp437Grid].
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `cp437_grid`: instance to write to /// - `cp437_grid`: instance to write to
/// - `value`: the value to set all cells to /// - `value`: the value to set all cells to
/// ///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
/// - `cp437_grid` is not written to or read from concurrently /// - `cp437_grid` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_fill( pub unsafe extern "C" fn sp_cp437_grid_fill(
cp437_grid: *mut SPCp437Grid, cp437_grid: *mut SPCp437Grid,
value: u8, value: u8,
) { ) {
assert!(!cp437_grid.is_null());
(*cp437_grid).0.fill(value); (*cp437_grid).0.fill(value);
} }
/// Gets the width of the `SPCp437Grid` instance. /// Gets the width of the [SPCp437Grid] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `cp437_grid`: instance to read from /// - `cp437_grid`: instance to read from
/// ///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_width( pub unsafe extern "C" fn sp_cp437_grid_width(
cp437_grid: *const SPCp437Grid, cp437_grid: *const SPCp437Grid,
) -> usize { ) -> usize {
assert!(!cp437_grid.is_null());
(*cp437_grid).0.width() (*cp437_grid).0.width()
} }
/// Gets the height of the `SPCp437Grid` instance. /// Gets the height of the [SPCp437Grid] instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `cp437_grid`: instance to read from /// - `cp437_grid`: instance to read from
/// ///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_height( pub unsafe extern "C" fn sp_cp437_grid_height(
cp437_grid: *const SPCp437Grid, cp437_grid: *const SPCp437Grid,
) -> usize { ) -> usize {
assert!(!cp437_grid.is_null());
(*cp437_grid).0.height() (*cp437_grid).0.height()
} }
/// Gets an unsafe reference to the data of the `SPCp437Grid` instance. /// Gets an unsafe reference to the data of the [SPCp437Grid] instance.
/// ///
/// Will never return NULL. /// Will never return NULL.
/// ///
/// # Panics
///
/// - when `cp437_grid` is NULL
///
/// ## Safety /// ## Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `cp437_grid` points to a valid `SPCp437Grid` /// - `cp437_grid` points to a valid [SPCp437Grid]
/// - the returned memory range is never accessed after the passed `SPCp437Grid` has been freed /// - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed
/// - the returned memory range is never accessed concurrently, either via the `SPCp437Grid` or directly /// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref(
cp437_grid: *mut SPCp437Grid, cp437_grid: *mut SPCp437Grid,
) -> SPByteSlice { ) -> SPByteSlice {
let data = (*cp437_grid).0.data_ref_mut(); let data = (*cp437_grid).0.data_ref_mut();
SPByteSlice { SPByteSlice {
start: data.as_mut_ptr_range().start, start: NonNull::new(data.as_mut_ptr_range().start).unwrap(),
length: data.len(), length: data.len(),
} }
} }

View file

@ -13,8 +13,8 @@
//! if (connection == NULL) //! if (connection == NULL)
//! return 1; //! return 1;
//! //!
//! SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); //! SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
//! sp_pixel_grid_fill(pixels, true); //! sp_bitmap_fill(pixels, true);
//! //!
//! SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); //! SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
//! while (sp_connection_send_command(connection, sp_command_clone(command))); //! while (sp_connection_send_command(connection, sp_command_clone(command)));
@ -25,7 +25,8 @@
//! } //! }
//! ``` //! ```
pub use crate::bit_vec::*; pub use crate::bitvec::*;
pub use crate::bitmap::*;
pub use crate::brightness_grid::*; pub use crate::brightness_grid::*;
pub use crate::byte_slice::*; pub use crate::byte_slice::*;
pub use crate::command::*; pub use crate::command::*;
@ -33,9 +34,9 @@ pub use crate::connection::*;
pub use crate::constants::*; pub use crate::constants::*;
pub use crate::cp437_grid::*; pub use crate::cp437_grid::*;
pub use crate::packet::*; pub use crate::packet::*;
pub use crate::pixel_grid::*;
mod bit_vec; mod bitvec;
mod bitmap;
mod brightness_grid; mod brightness_grid;
mod byte_slice; mod byte_slice;
mod command; mod command;
@ -43,4 +44,3 @@ mod connection;
mod constants; mod constants;
mod cp437_grid; mod cp437_grid;
mod packet; mod packet;
mod pixel_grid;

View file

@ -1,53 +1,63 @@
//! C functions for interacting with `SPPacket`s //! C functions for interacting with [SPPacket]s
//! //!
//! prefix `sp_packet_` //! prefix `sp_packet_`
use std::ptr::null_mut; use std::ptr::{null_mut, NonNull};
use crate::SPCommand; use crate::SPCommand;
/// The raw packet /// The raw packet
pub struct SPPacket(pub(crate) servicepoint::packet::Packet); pub struct SPPacket(pub(crate) servicepoint::packet::Packet);
/// Turns a `SPCommand` into a `SPPacket`. /// Turns a [SPCommand] into a [SPPacket].
/// The `SPCommand` gets consumed. /// The [SPCommand] gets consumed.
/// ///
/// Will never return NULL. /// Will never return NULL.
/// ///
/// # Panics
///
/// - when `command` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `SPCommand` points to a valid instance of `SPCommand` /// - [SPCommand] points to a valid instance of [SPCommand]
/// - `SPCommand` is not used concurrently or after this call /// - [SPCommand] is not used concurrently or after this call
/// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`. /// by explicitly calling `sp_packet_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_from_command( pub unsafe extern "C" fn sp_packet_from_command(
command: *mut SPCommand, command: *mut SPCommand,
) -> *mut SPPacket { ) -> NonNull<SPPacket> {
assert!(!command.is_null());
let command = *Box::from_raw(command); let command = *Box::from_raw(command);
let packet = SPPacket(command.0.into()); let result = Box::new(SPPacket(command.0.into()));
Box::into_raw(Box::new(packet)) NonNull::from(Box::leak(result))
} }
/// Tries to load a `SPPacket` from the passed array with the specified length. /// Tries to load a [SPPacket] from the passed array with the specified length.
/// ///
/// returns: NULL in case of an error, pointer to the allocated packet otherwise /// returns: NULL in case of an error, pointer to the allocated packet otherwise
/// ///
/// # Panics
///
/// - when `data` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `data` points to a valid memory region of at least `length` bytes /// - `data` points to a valid memory region of at least `length` bytes
/// - `data` is not written to concurrently /// - `data` is not written to concurrently
/// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`. /// by explicitly calling `sp_packet_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_try_load( pub unsafe extern "C" fn sp_packet_try_load(
data: *const u8, data: *const u8,
length: usize, length: usize,
) -> *mut SPPacket { ) -> *mut SPPacket {
assert!(!data.is_null());
let data = std::slice::from_raw_parts(data, length); let data = std::slice::from_raw_parts(data, length);
match servicepoint::packet::Packet::try_from(data) { match servicepoint::packet::Packet::try_from(data) {
Err(_) => null_mut(), Err(_) => null_mut(),
@ -55,34 +65,45 @@ pub unsafe extern "C" fn sp_packet_try_load(
} }
} }
/// Clones a `SPPacket`. /// Clones a [SPPacket].
/// ///
/// Will never return NULL. /// Will never return NULL.
/// ///
/// # Panics
///
/// - when `packet` is NULL
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `packet` points to a valid `SPPacket` /// - `packet` points to a valid [SPPacket]
/// - `packet` is not written to concurrently /// - `packet` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_packet_free`. /// by explicitly calling `sp_packet_free`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_clone( pub unsafe extern "C" fn sp_packet_clone(
packet: *const SPPacket, packet: *const SPPacket,
) -> *mut SPPacket { ) -> NonNull<SPPacket> {
Box::into_raw(Box::new(SPPacket((*packet).0.clone()))) assert!(!packet.is_null());
let result = Box::new(SPPacket((*packet).0.clone()));
NonNull::from(Box::leak(result))
} }
/// Deallocates a `SPPacket`. /// Deallocates a [SPPacket].
///
/// # Panics
///
/// - when `sp_packet_free` is NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `packet` points to a valid `SPPacket` /// - `packet` points to a valid [SPPacket]
/// - `packet` is not used concurrently or after this call /// - `packet` is not used concurrently or after this call
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) {
assert!(!packet.is_null());
_ = Box::from_raw(packet) _ = Box::from_raw(packet)
} }

View file

@ -1,248 +0,0 @@
//! C functions for interacting with `SPPixelGrid`s
//!
//! prefix `sp_pixel_grid_`
use servicepoint::{DataRef, Grid};
use crate::byte_slice::SPByteSlice;
/// A grid of pixels.
///
/// # Examples
///
/// ```C
/// Cp437Grid grid = sp_pixel_grid_new(8, 3);
/// sp_pixel_grid_fill(grid, true);
/// sp_pixel_grid_set(grid, 0, 0, false);
/// sp_pixel_grid_free(grid);
/// ```
pub struct SPPixelGrid(pub(crate) servicepoint::PixelGrid);
/// Creates a new `SPPixelGrid` with the specified dimensions.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: `SPPixelGrid` initialized to all pixels off. Will never return NULL.
///
/// # Panics
///
/// - when the width is not dividable by 8
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_pixel_grid_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_new(
width: usize,
height: usize,
) -> *mut SPPixelGrid {
Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::new(
width, height,
))))
}
/// Loads a `SPPixelGrid` with the specified dimensions from the provided data.
///
/// # Arguments
///
/// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction
///
/// returns: `SPPixelGrid` that contains a copy of the provided data. Will never return NULL.
///
/// # Panics
///
/// - when the dimensions and data size do not match exactly.
/// - when the width is not dividable by 8
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `data` points to a valid memory location of at least `data_length` bytes in size.
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_pixel_grid_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut SPPixelGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::load(
width, height, data,
))))
}
/// Clones a `SPPixelGrid`.
///
/// Will never return NULL.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - `pixel_grid` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_pixel_grid_free`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_clone(
pixel_grid: *const SPPixelGrid,
) -> *mut SPPixelGrid {
Box::into_raw(Box::new(SPPixelGrid((*pixel_grid).0.clone())))
}
/// Deallocates a `SPPixelGrid`.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - `pixel_grid` is not used concurrently or after pixel_grid call
/// - `pixel_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_free(pixel_grid: *mut SPPixelGrid) {
_ = Box::from_raw(pixel_grid);
}
/// Gets the current value at the specified position in the `SPPixelGrid`.
///
/// # Arguments
///
/// - `pixel_grid`: instance to read from
/// - `x` and `y`: position of the cell to read
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - `pixel_grid` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_get(
pixel_grid: *const SPPixelGrid,
x: usize,
y: usize,
) -> bool {
(*pixel_grid).0.get(x, y)
}
/// Sets the value of the specified position in the `SPPixelGrid`.
///
/// # Arguments
///
/// - `pixel_grid`: instance to write to
/// - `x` and `y`: position of the cell
/// - `value`: the value to write to the cell
///
/// returns: old value of the cell
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - `pixel_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_set(
pixel_grid: *mut SPPixelGrid,
x: usize,
y: usize,
value: bool,
) {
(*pixel_grid).0.set(x, y, value);
}
/// Sets the state of all pixels in the `SPPixelGrid`.
///
/// # Arguments
///
/// - `pixel_grid`: instance to write to
/// - `value`: the value to set all pixels to
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - `pixel_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_fill(
pixel_grid: *mut SPPixelGrid,
value: bool,
) {
(*pixel_grid).0.fill(value);
}
/// Gets the width in pixels of the `SPPixelGrid` instance.
///
/// # Arguments
///
/// - `pixel_grid`: instance to read from
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_width(
pixel_grid: *const SPPixelGrid,
) -> usize {
(*pixel_grid).0.width()
}
/// Gets the height in pixels of the `SPPixelGrid` instance.
///
/// # Arguments
///
/// - `pixel_grid`: instance to read from
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_height(
pixel_grid: *const SPPixelGrid,
) -> usize {
(*pixel_grid).0.height()
}
/// Gets an unsafe reference to the data of the `SPPixelGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure that:
///
/// - `pixel_grid` points to a valid `SPPixelGrid`
/// - the returned memory range is never accessed after the passed `SPPixelGrid` has been freed
/// - the returned memory range is never accessed concurrently, either via the `SPPixelGrid` or directly
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_unsafe_data_ref(
pixel_grid: *mut SPPixelGrid,
) -> SPByteSlice {
let data = (*pixel_grid).0.data_ref_mut();
SPByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}

View file

@ -13,8 +13,8 @@ test = false
csbindgen = "1.9.3" csbindgen = "1.9.3"
[dependencies] [dependencies]
servicepoint_binding_c = { version = "0.9.1", path = "../servicepoint_binding_c" } servicepoint_binding_c = { version = "0.10.0", path = "../servicepoint_binding_c" }
servicepoint = { version = "0.9.1", path = "../servicepoint" } servicepoint = { version = "0.10.0", path = "../servicepoint" }
[lints] [lints]
workspace = true workspace = true

View file

@ -11,7 +11,7 @@ using ServicePoint;
// using statement calls Dispose() on scope exit, which frees unmanaged instances // using statement calls Dispose() on scope exit, which frees unmanaged instances
using var connection = Connection.Open("127.0.0.1:2342"); using var connection = Connection.Open("127.0.0.1:2342");
using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight); using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight);
while (true) while (true)
{ {

View file

@ -8,7 +8,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return new BitVec(NativeMethods.sp_bit_vec_new((nuint)size)); return new BitVec(NativeMethods.sp_bitvec_new((nuint)size));
} }
} }
@ -18,7 +18,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new BitVec(NativeMethods.sp_bit_vec_load(bytesPtr, (nuint)bytes.Length)); return new BitVec(NativeMethods.sp_bitvec_load(bytesPtr, (nuint)bytes.Length));
} }
} }
} }
@ -27,7 +27,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return new BitVec(NativeMethods.sp_bit_vec_clone(Instance)); return new BitVec(NativeMethods.sp_bitvec_clone(Instance));
} }
} }
@ -37,14 +37,14 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return NativeMethods.sp_bit_vec_get(Instance, (nuint)index); return NativeMethods.sp_bitvec_get(Instance, (nuint)index);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp_bit_vec_set(Instance, (nuint)index, value); NativeMethods.sp_bitvec_set(Instance, (nuint)index, value);
} }
} }
} }
@ -53,7 +53,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
NativeMethods.sp_bit_vec_fill(Instance, value); NativeMethods.sp_bitvec_fill(Instance, value);
} }
} }
@ -63,7 +63,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp_bit_vec_len(Instance); return (int)NativeMethods.sp_bitvec_len(Instance);
} }
} }
} }
@ -74,7 +74,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp_bit_vec_unsafe_data_ref(Instance); var slice = NativeMethods.sp_bitvec_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
@ -84,5 +84,5 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
} }
private protected override unsafe void Free() => NativeMethods.sp_bit_vec_free(Instance); private protected override unsafe void Free() => NativeMethods.sp_bitvec_free(Instance);
} }

View file

@ -2,33 +2,33 @@ using ServicePoint.BindGen;
namespace ServicePoint; namespace ServicePoint;
public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> public sealed class Bitmap : SpNativeInstance<BindGen.Bitmap>
{ {
public static PixelGrid New(int width, int height) public static Bitmap New(int width, int height)
{ {
unsafe unsafe
{ {
return new PixelGrid(NativeMethods.sp_pixel_grid_new((nuint)width, (nuint)height)); return new Bitmap(NativeMethods.sp_bitmap_new((nuint)width, (nuint)height));
} }
} }
public static PixelGrid Load(int width, int height, Span<byte> bytes) public static Bitmap Load(int width, int height, Span<byte> bytes)
{ {
unsafe unsafe
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new PixelGrid(NativeMethods.sp_pixel_grid_load((nuint)width, (nuint)height, bytesPtr, return new Bitmap(NativeMethods.sp_bitmap_load((nuint)width, (nuint)height, bytesPtr,
(nuint)bytes.Length)); (nuint)bytes.Length));
} }
} }
} }
public PixelGrid Clone() public Bitmap Clone()
{ {
unsafe unsafe
{ {
return new PixelGrid(NativeMethods.sp_pixel_grid_clone(Instance)); return new Bitmap(NativeMethods.sp_bitmap_clone(Instance));
} }
} }
@ -38,14 +38,14 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return NativeMethods.sp_pixel_grid_get(Instance, (nuint)x, (nuint)y); return NativeMethods.sp_bitmap_get(Instance, (nuint)x, (nuint)y);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp_pixel_grid_set(Instance, (nuint)x, (nuint)y, value); NativeMethods.sp_bitmap_set(Instance, (nuint)x, (nuint)y, value);
} }
} }
} }
@ -54,7 +54,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
NativeMethods.sp_pixel_grid_fill(Instance, value); NativeMethods.sp_bitmap_fill(Instance, value);
} }
} }
@ -64,7 +64,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp_pixel_grid_width(Instance); return (int)NativeMethods.sp_bitmap_width(Instance);
} }
} }
} }
@ -75,7 +75,7 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp_pixel_grid_height(Instance); return (int)NativeMethods.sp_bitmap_height(Instance);
} }
} }
} }
@ -86,15 +86,15 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp_pixel_grid_unsafe_data_ref(Instance); var slice = NativeMethods.sp_bitmap_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
} }
private unsafe PixelGrid(BindGen.PixelGrid* instance) : base(instance) private unsafe Bitmap(BindGen.Bitmap* instance) : base(instance)
{ {
} }
private protected override unsafe void Free() => NativeMethods.sp_pixel_grid_free(Instance); private protected override unsafe void Free() => NativeMethods.sp_bitmap_free(Instance);
} }

View file

@ -105,11 +105,11 @@ public sealed class Command : SpNativeInstance<BindGen.Command>
} }
} }
public static Command BitmapLinearWin(int x, int y, PixelGrid pixelGrid, CompressionCode compression) public static Command BitmapLinearWin(int x, int y, Bitmap bitmap, CompressionCode compression)
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, pixelGrid.Into(), compression)); return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, bitmap.Into(), compression));
} }
} }

View file

@ -1,22 +1,24 @@
using ServicePoint.BindGen;
namespace ServicePoint; namespace ServicePoint;
public static class Constants public static class Constants
{ {
/// size of a single tile in one dimension /// size of a single tile in one dimension
public const int TileSize = 8; public const nuint TileSize = NativeMethods.SP_TILE_SIZE;
/// tile count in the x-direction /// tile count in the x-direction
public const int TileWidth = 56; public const nuint TileWidth = NativeMethods.SP_TILE_WIDTH;
/// tile count in the y-direction /// tile count in the y-direction
public const int TileHeight = 20; public const nuint TileHeight = NativeMethods.SP_TILE_SIZE;
/// screen width in pixels /// screen width in pixels
public const int PixelWidth = TileWidth * TileSize; public const nuint PixelWidth = TileWidth * TileSize;
/// screen height in pixels /// screen height in pixels
public const int PixelHeight = TileHeight * TileSize; public const nuint PixelHeight = TileHeight * TileSize;
/// pixel count on whole screen /// pixel count on whole screen
public const int PixelCount = PixelWidth * PixelHeight; public const nuint PixelCount = PixelWidth * PixelHeight;
} }

View file

@ -11,7 +11,7 @@
<PropertyGroup> <PropertyGroup>
<PackageId>ServicePoint</PackageId> <PackageId>ServicePoint</PackageId>
<Version>0.9.1</Version> <Version>0.10.0</Version>
<Authors>Repository Authors</Authors> <Authors>Repository Authors</Authors>
<Company>None</Company> <Company>None</Company>
<Product>ServicePoint</Product> <Product>ServicePoint</Product>

View file

@ -8,8 +8,12 @@ fn main() {
let mut builder = csbindgen::Builder::default(); let mut builder = csbindgen::Builder::default();
for source in fs::read_dir("../servicepoint_binding_c/src").unwrap() { let mut paths = fs::read_dir("../servicepoint_binding_c/src").unwrap()
let path = source.unwrap().path(); .map(|x| x.unwrap().path())
.collect::<Vec<_>>();
paths.sort();
for path in paths {
println!("cargo:rerun-if-changed={}", path.display()); println!("cargo:rerun-if-changed={}", path.display());
builder = builder.input_extern_file(path); builder = builder.input_extern_file(path);
} }

View file

@ -6,7 +6,7 @@ using var connection = Connection.Open("127.0.0.1:2342");
connection.Send(Command.Clear().IntoPacket()); connection.Send(Command.Clear().IntoPacket());
connection.Send(Command.Brightness(128).IntoPacket()); connection.Send(Command.Brightness(128).IntoPacket());
using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight); using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight);
for (var offset = 0; offset < int.MaxValue; offset++) for (var offset = 0; offset < int.MaxValue; offset++)
{ {