Merge branch 'fix-brightness'

This commit is contained in:
Vinzenz Schroeter 2024-06-27 19:44:24 +02:00
commit a484e6a976
35 changed files with 1594 additions and 553 deletions

View file

@ -16,9 +16,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build crates
- name: build default features
run: cargo build --all --verbose run: cargo build --all --verbose
- name: Build - name: build default features -- examples
run: cargo build --examples --verbose run: cargo build --examples --verbose
- name: Run tests - name: test default features
run: cargo test --all --verbose run: cargo test --all --verbose
- name: build all features
run: cargo build --all-features --verbose
- name: build all features -- examples
run: cargo build --all-features --examples --verbose
- name: test all features
run: cargo test --all --all-features --verbose

View file

@ -1,14 +1,12 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"crates/servicepoint", "crates/*",
"crates/servicepoint_binding_c",
"crates/servicepoint_binding_cs",
"crates/servicepoint_binding_c/examples/lang_c" "crates/servicepoint_binding_c/examples/lang_c"
] ]
[workspace.package] [workspace.package]
version = "0.6.0" version = "0.7.0"
[workspace.lints.rust] [workspace.lints.rust]
missing-docs = "warn" missing-docs = "warn"

View file

@ -19,6 +19,7 @@ flate2 = { version = "1.0", optional = true }
bzip2 = { version = "0.4", optional = true } bzip2 = { version = "0.4", optional = true }
zstd = { version = "0.13", optional = true } zstd = { version = "0.13", optional = true }
rust-lzma = { version = "0.6.0", optional = true } rust-lzma = { version = "0.6.0", optional = true }
rand = { version = "0.8", optional = true }
[features] [features]
default = ["compression_lzma"] default = ["compression_lzma"]
@ -27,6 +28,11 @@ compression_bzip2 = ["dep:bzip2"]
compression_lzma = ["dep:rust-lzma"] compression_lzma = ["dep:rust-lzma"]
compression_zstd = ["dep:zstd"] compression_zstd = ["dep:zstd"]
all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"]
rand = ["dep:rand"]
[[example]]
name = "random_brightness"
required-features = ["rand"]
[dev-dependencies] [dev-dependencies]
# for examples # for examples
@ -34,4 +40,4 @@ clap = { version = "4.5", features = ["derive"] }
rand = "0.8" rand = "0.8"
[lints] [lints]
workspace = true workspace = true

View file

@ -46,7 +46,7 @@ In the likely case you only need one of them, you can include that one specifica
```toml ```toml
[dependencies] [dependencies]
servicepoint = { version = "0.6.0", default-features = false, features = ["compression-bz"] } servicepoint = { version = "0.7.0", default-features = false, features = ["compression-bz"] }
``` ```
## Everything else ## Everything else

View file

@ -2,7 +2,7 @@
use clap::Parser; use clap::Parser;
use servicepoint::{ByteGrid, Command, Connection, Grid, Origin}; use servicepoint::{Command, Connection, Cp437Grid, Grid, Origin};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -22,14 +22,18 @@ fn main() {
cli.text.push("Hello, CCCB!".to_string()); cli.text.push("Hello, CCCB!".to_string());
} }
let connection = Connection::open(&cli.destination).unwrap(); let connection = Connection::open(&cli.destination)
.expect("could not connect to display");
if cli.clear { if cli.clear {
connection.send(Command::Clear).unwrap(); connection
.send(Command::Clear)
.expect("sending clear failed");
} }
let max_width = cli.text.iter().map(|t| t.len()).max().unwrap(); let max_width = cli.text.iter().map(|t| t.len()).max().unwrap();
let mut chars = ByteGrid::new(max_width, cli.text.len()); let mut chars = Cp437Grid::new(max_width, cli.text.len());
for y in 0..cli.text.len() { for y in 0..cli.text.len() {
let row = &cli.text[y]; let row = &cli.text[y];
@ -40,6 +44,6 @@ fn main() {
} }
connection connection
.send(Command::Cp437Data(Origin(0, 0), chars)) .send(Command::Cp437Data(Origin::new(0, 0), chars))
.unwrap(); .expect("sending text failed");
} }

View file

@ -2,7 +2,6 @@
use clap::Parser; use clap::Parser;
use servicepoint::Command::BitmapLinearWin;
use servicepoint::*; use servicepoint::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -13,25 +12,26 @@ struct Cli {
fn main() { fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
let connection = Connection::open(cli.destination).unwrap(); let connection = Connection::open(cli.destination)
.expect("could not connect to display");
let mut pixels = PixelGrid::max_sized(); let mut pixels = PixelGrid::max_sized();
pixels.fill(true); pixels.fill(true);
connection let command = Command::BitmapLinearWin(
.send(BitmapLinearWin( Origin::new(0, 0),
Origin(0, 0), pixels,
pixels, CompressionCode::Uncompressed,
CompressionCode::Uncompressed, );
)) connection.send(command).expect("send failed");
.expect("send failed");
let mut brightnesses = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT); let max_brightness = usize::from(u8::from(Brightness::MAX));
let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT);
for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() {
*byte = (index % u8::MAX as usize) as u8; *byte = Brightness::try_from((index % max_brightness) as u8).unwrap();
} }
connection connection
.send(Command::CharBrightness(Origin(0, 0), brightnesses)) .send(Command::CharBrightness(Origin::new(0, 0), brightnesses))
.expect("send failed"); .expect("send failed");
} }

View file

@ -18,17 +18,17 @@ struct Cli {
fn main() { fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
let connection = Connection::open(&cli.destination).unwrap(); let connection = Connection::open(&cli.destination)
.expect("could not connect to display");
let mut field = make_random_field(cli.probability); let mut field = make_random_field(cli.probability);
loop { loop {
connection let command = Command::BitmapLinearWin(
.send(Command::BitmapLinearWin( Origin::new(0, 0),
Origin(0, 0), field.clone(),
field.clone(), CompressionCode::Lzma,
CompressionCode::Lzma, );
)) connection.send(command).expect("could not send");
.expect("could not send");
thread::sleep(FRAME_PACING); thread::sleep(FRAME_PACING);
field = iteration(field); field = iteration(field);
} }

View file

@ -13,7 +13,8 @@ struct Cli {
} }
fn main() { fn main() {
let connection = Connection::open(Cli::parse().destination).unwrap(); let connection = Connection::open(Cli::parse().destination)
.expect("could not connect to display");
let mut pixels = PixelGrid::max_sized(); let mut pixels = PixelGrid::max_sized();
for x_offset in 0..usize::MAX { for x_offset in 0..usize::MAX {
@ -22,13 +23,13 @@ fn main() {
for y in 0..PIXEL_HEIGHT { for y in 0..PIXEL_HEIGHT {
pixels.set((y + x_offset) % PIXEL_WIDTH, y, true); pixels.set((y + x_offset) % PIXEL_WIDTH, y, true);
} }
connection
.send(Command::BitmapLinearWin( let command = Command::BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
pixels.clone(), pixels.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
)) );
.unwrap(); connection.send(command).expect("send failed");
thread::sleep(FRAME_PACING); thread::sleep(FRAME_PACING);
} }
} }

View file

@ -22,7 +22,8 @@ struct Cli {
fn main() { fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
let connection = Connection::open(cli.destination).unwrap(); let connection = Connection::open(cli.destination)
.expect("could not connect to display");
let wait_duration = Duration::from_millis(cli.wait_ms); let wait_duration = Duration::from_millis(cli.wait_ms);
// put all pixels in on state // put all pixels in on state
@ -30,8 +31,11 @@ fn main() {
let mut filled_grid = PixelGrid::max_sized(); let mut filled_grid = PixelGrid::max_sized();
filled_grid.fill(true); filled_grid.fill(true);
let command = let command = BitmapLinearWin(
BitmapLinearWin(Origin(0, 0), filled_grid, CompressionCode::Lzma); Origin::new(0, 0),
filled_grid,
CompressionCode::Lzma,
);
connection.send(command).expect("send failed"); connection.send(command).expect("send failed");
} }
@ -48,8 +52,8 @@ fn main() {
let w = rng.gen_range(min_size..=TILE_WIDTH - x); let w = rng.gen_range(min_size..=TILE_WIDTH - x);
let h = rng.gen_range(min_size..=TILE_HEIGHT - y); let h = rng.gen_range(min_size..=TILE_HEIGHT - y);
let origin = Origin(x, y); let origin = Origin::new(x, y);
let mut luma = ByteGrid::new(w, h); let mut luma = BrightnessGrid::new(w, h);
for y in 0..h { for y in 0..h {
for x in 0..w { for x in 0..w {

View file

@ -22,7 +22,8 @@ fn main() {
Duration::from_millis(cli.time / PIXEL_WIDTH as u64), Duration::from_millis(cli.time / PIXEL_WIDTH as u64),
); );
let connection = Connection::open(cli.destination).unwrap(); let connection = Connection::open(cli.destination)
.expect("could not connect to display");
let mut enabled_pixels = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT); let mut enabled_pixels = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
enabled_pixels.fill(true); enabled_pixels.fill(true);
@ -38,7 +39,7 @@ fn main() {
connection connection
.send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma)) .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma))
.unwrap(); .expect("could not send command to display");
thread::sleep(sleep_duration); thread::sleep(sleep_duration);
} }
} }

View file

@ -0,0 +1,111 @@
use crate::{Grid, PrimitiveGrid};
#[cfg(feature = "rand")]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
/// A display brightness value, checked for correct value range
///
/// # Examples
///
/// ```
/// # use servicepoint::{Brightness, Command, Connection};
/// let b = Brightness::MAX;
/// let val: u8 = b.into();
///
/// let b = Brightness::try_from(7).unwrap();
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// let result = connection.send(Command::Brightness(b));
/// ```
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Brightness(u8);
/// A grid containing brightness values.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
/// let mut grid = BrightnessGrid::new(2,2);
/// grid.set(0, 0, Brightness::MIN);
/// grid.set(1, 1, Brightness::MIN);
///
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
/// ```
pub type BrightnessGrid = PrimitiveGrid<Brightness>;
impl From<Brightness> for u8 {
fn from(brightness: Brightness) -> Self {
brightness.0
}
}
impl TryFrom<u8> for Brightness {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
if value > Self::MAX.0 {
Err(value)
} else {
Ok(Brightness(value))
}
}
}
impl Brightness {
/// highest possible brightness value, 11
pub const MAX: Brightness = Brightness(11);
/// lowest possible brightness value, 0
pub const MIN: Brightness = Brightness(0);
}
impl Default for Brightness {
fn default() -> Self {
Self::MAX
}
}
impl From<BrightnessGrid> for Vec<u8> {
fn from(value: PrimitiveGrid<Brightness>) -> Self {
value
.iter()
.map(|brightness| (*brightness).into())
.collect()
}
}
impl From<BrightnessGrid> for PrimitiveGrid<u8> {
fn from(value: PrimitiveGrid<Brightness>) -> Self {
let u8s = value
.iter()
.map(|brightness| (*brightness).into())
.collect::<Vec<u8>>();
PrimitiveGrid::load(value.width(), value.height(), &u8s)
}
}
impl TryFrom<PrimitiveGrid<u8>> for BrightnessGrid {
type Error = u8;
fn try_from(value: PrimitiveGrid<u8>) -> Result<Self, Self::Error> {
let brightnesses = value
.iter()
.map(|b| Brightness::try_from(*b))
.collect::<Result<Vec<Brightness>, _>>()?;
Ok(BrightnessGrid::load(
value.width(),
value.height(),
&brightnesses,
))
}
}
#[cfg(feature = "rand")]
impl Distribution<Brightness> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Brightness {
Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0))
}
}

View file

@ -1,183 +1,168 @@
use bitvec::prelude::BitVec; use bitvec::prelude::BitVec;
use crate::command_code::CommandCode;
use crate::compression::{into_compressed, into_decompressed};
use crate::{ use crate::{
ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, SpBitVec, command_code::CommandCode, compression::into_decompressed, Brightness,
TILE_SIZE, BrightnessGrid, CompressionCode, Header, Origin, Packet, PixelGrid, Pixels,
PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE,
}; };
/// An origin marks the top left position of a window sent to the display.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Origin(pub usize, pub usize);
impl std::ops::Add<Origin> for Origin {
type Output = Origin;
fn add(self, rhs: Origin) -> Self::Output {
let Origin(x1, y1) = self;
let Origin(x2, y2) = rhs;
Origin(x1 + x2, y1 + y2)
}
}
/// Type alias for documenting the meaning of the u16 in enum values /// Type alias for documenting the meaning of the u16 in enum values
pub type Offset = usize; pub type Offset = usize;
/// Type alias for documenting the meaning of the u16 in enum values /// A grid containing codepage 437 characters.
pub type Brightness = u8; ///
/// The encoding is currently not enforced.
pub type Cp437Grid = PrimitiveGrid<u8>;
/// A command to send to the display. /// A low-level display command.
///
/// This struct and associated functions implement the UDP protocol for the display.
///
/// To send a `Command`, use a `Connection`.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Brightness, Command, Connection, Packet};
///
/// // create command
/// let command = Command::Brightness(Brightness::MAX);
///
/// // turn command into Packet
/// let packet: Packet = command.clone().into();
///
/// // read command from packet
/// let round_tripped = Command::try_from(packet).unwrap();
///
/// // round tripping produces exact copy
/// assert_eq!(command, round_tripped);
///
/// // send command
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// connection.send(command).unwrap();
/// ```
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Command { pub enum Command {
/// Set all pixels to the off state /// Set all pixels to the off state. Does not affect brightness.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Command, Connection};
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// connection.send(Command::Clear).unwrap();
/// ```
Clear, Clear,
/// Kills the udp daemon, usually results in a reboot of the display.
HardReset, /// Show text on the screen.
/// Slowly decrease brightness until off? Untested. ///
FadeOut, /// <div class="warning">
/// Set the brightness of tiles /// The library does not currently convert between UTF-8 and CP-437.
CharBrightness(Origin, ByteGrid), /// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
/// Set the brightness of all tiles /// </div>
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Command, Connection, Cp437Grid, Origin};
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'].map(move |c| c as u8);
/// let grid = Cp437Grid::load(5, 2, &chars);
/// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
/// ```
Cp437Data(Origin<Tiles>, Cp437Grid),
/// Sets a window of pixels to the specified values
BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode),
/// Set the brightness of all tiles to the same value.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Brightness, Command, Connection};
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// let command = Command::Brightness(Brightness::MAX);
/// connection.send(command).unwrap();
/// ```
Brightness(Brightness), Brightness(Brightness),
#[deprecated]
/// Legacy command code, gets ignored by the real display. /// Set the brightness of individual tiles in a rectangular area of the display.
BitmapLegacy, CharBrightness(Origin<Tiles>, BrightnessGrid),
/// Set pixel data starting at the offset.
/// Set pixel data starting at the pixel offset on screen.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained `BitVec` is always uncompressed. /// The contained `BitVec` is always uncompressed.
BitmapLinear(Offset, SpBitVec, CompressionCode), BitmapLinear(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an and-mask starting at the offset. /// Set pixel data according to an and-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained `BitVec` is always uncompressed. /// The contained `BitVec` is always uncompressed.
BitmapLinearAnd(Offset, SpBitVec, CompressionCode), BitmapLinearAnd(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an or-mask starting at the offset. /// Set pixel data according to an or-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained `BitVec` is always uncompressed. /// The contained `BitVec` is always uncompressed.
BitmapLinearOr(Offset, SpBitVec, CompressionCode), BitmapLinearOr(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to a xor-mask starting at the offset. /// Set pixel data according to a xor-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained `BitVec` is always uncompressed. /// The contained `BitVec` is always uncompressed.
BitmapLinearXor(Offset, SpBitVec, CompressionCode), BitmapLinearXor(Offset, SpBitVec, CompressionCode),
/// Show text on the screen. Note that the byte data has to be CP437 encoded.
Cp437Data(Origin, ByteGrid),
/// Sets a window of pixels to the specified values
BitmapLinearWin(Origin, PixelGrid, CompressionCode),
}
impl From<Command> for Packet { /// Kills the udp daemon on the display, which usually results in a restart.
/// Move the `Command` into a `Packet` instance for sending. ///
#[allow(clippy::cast_possible_truncation)] /// Please do not send this in your normal program flow.
fn from(value: Command) -> Self { ///
match value { /// # Examples
Command::Clear => Command::command_code_only(CommandCode::Clear), ///
Command::FadeOut => { /// ```rust
Command::command_code_only(CommandCode::FadeOut) /// # use servicepoint::{Command, Connection};
} /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
Command::HardReset => { /// connection.send(Command::HardReset).unwrap();
Command::command_code_only(CommandCode::HardReset) /// ```
} HardReset,
#[allow(deprecated)]
Command::BitmapLegacy => {
Command::command_code_only(CommandCode::BitmapLegacy)
}
Command::CharBrightness(Origin(x, y), grid) => Packet(
Header(
CommandCode::CharBrightness.into(),
x as u16,
y as u16,
grid.width() as u16,
grid.height() as u16,
),
grid.into(),
),
Command::Brightness(brightness) => Packet(
Header(
CommandCode::Brightness.into(),
0x00000,
0x0000,
0x0000,
0x0000,
),
vec![brightness],
),
Command::BitmapLinearWin(origin, pixels, compression) => {
bitmap_win_into_packet(origin, pixels, compression)
}
Command::BitmapLinear(offset, bits, compression) => {
Command::bitmap_linear_into_packet(
CommandCode::BitmapLinear,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearAnd(offset, bits, compression) => {
Command::bitmap_linear_into_packet(
CommandCode::BitmapLinearAnd,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearOr(offset, bits, compression) => {
Command::bitmap_linear_into_packet(
CommandCode::BitmapLinearOr,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearXor(offset, bits, compression) => {
Command::bitmap_linear_into_packet(
CommandCode::BitmapLinearXor,
offset,
compression,
bits.into(),
)
}
Command::Cp437Data(Origin(x, y), grid) => Packet(
Header(
CommandCode::Cp437Data.into(),
x as u16,
y as u16,
grid.width() as u16,
grid.height() as u16,
),
grid.into(),
),
}
}
}
#[allow(clippy::cast_possible_truncation)] /// <div class="warning">Untested</div>
fn bitmap_win_into_packet( ///
origin: Origin, /// Slowly decrease brightness until off or something like that?
pixels: PixelGrid, ///
compression: CompressionCode, /// # Examples
) -> Packet { ///
let Origin(pixel_x, pixel_y) = origin; /// ```rust
debug_assert_eq!(pixel_x % 8, 0); /// # use servicepoint::{Command, Connection};
debug_assert_eq!(pixels.width() % 8, 0); /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// connection.send(Command::FadeOut).unwrap();
/// ```
FadeOut,
let tile_x = (pixel_x / TILE_SIZE) as u16; /// Legacy command code, gets ignored by the real display.
let tile_w = (pixels.width() / TILE_SIZE) as u16; ///
let pixel_h = pixels.height() as u16; /// Might be useful as a noop package.
let payload = into_compressed(compression, pixels.into()); ///
let command = match compression { /// # Examples
CompressionCode::Uncompressed => { ///
CommandCode::BitmapLinearWinUncompressed /// ```rust
} /// # use servicepoint::{Command, Connection};
#[cfg(feature = "compression_zlib")] /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
CompressionCode::Zlib => CommandCode::BitmapLinearWinZlib, /// // this sends a packet that does nothing
#[cfg(feature = "compression_bzip2")] /// # #[allow(deprecated)]
CompressionCode::Bzip2 => CommandCode::BitmapLinearWinBzip2, /// connection.send(Command::BitmapLegacy).unwrap();
#[cfg(feature = "compression_lzma")] /// ```
CompressionCode::Lzma => CommandCode::BitmapLinearWinLzma, #[deprecated]
#[cfg(feature = "compression_zstd")] BitmapLegacy,
CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd,
};
Packet(
Header(command.into(), tile_x, pixel_y as u16, tile_w, pixel_h),
payload,
)
} }
#[derive(Debug)] #[derive(Debug)]
@ -196,6 +181,8 @@ pub enum TryFromPacketError {
InvalidCompressionCode(u16), InvalidCompressionCode(u16),
/// Decompression of the payload failed. This can be caused by corrupted packets. /// Decompression of the payload failed. This can be caused by corrupted packets.
DecompressionFailed, DecompressionFailed,
/// The given brightness value is out of bounds
InvalidBrightness(u8),
} }
impl TryFrom<Packet> for Command { impl TryFrom<Packet> for Command {
@ -203,7 +190,7 @@ impl TryFrom<Packet> for Command {
/// Try to interpret the `Packet` as one containing a `Command` /// Try to interpret the `Packet` as one containing a `Command`
fn try_from(packet: Packet) -> Result<Self, Self::Error> { fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let Packet(Header(command_u16, a, b, c, d), _) = packet; let Packet(Header(command_u16, a, _, _, _), _) = packet;
let command_code = match CommandCode::try_from(command_u16) { let command_code = match CommandCode::try_from(command_u16) {
Err(()) => { Err(()) => {
return Err(TryFromPacketError::InvalidCommand(command_u16)); return Err(TryFromPacketError::InvalidCommand(command_u16));
@ -212,47 +199,19 @@ impl TryFrom<Packet> for Command {
}; };
match command_code { match command_code {
CommandCode::Clear => match Self::check_command_only(packet) { CommandCode::Clear => {
Some(err) => Err(err), Self::packet_into_command_only(packet, Command::Clear)
None => Ok(Command::Clear),
},
CommandCode::Brightness => {
let Packet(header, payload) = packet;
if payload.len() != 1 {
return Err(TryFromPacketError::UnexpectedPayloadSize(
1,
payload.len(),
));
}
let Header(_, a, b, c, d) = header;
if a != 0 || b != 0 || c != 0 || d != 0 {
Err(TryFromPacketError::ExtraneousHeaderValues)
} else {
Ok(Command::Brightness(payload[0]))
}
} }
CommandCode::HardReset => match Self::check_command_only(packet) { CommandCode::Brightness => Self::packet_into_brightness(&packet),
Some(err) => Err(err), CommandCode::HardReset => {
None => Ok(Command::HardReset), Self::packet_into_command_only(packet, Command::HardReset)
},
CommandCode::FadeOut => match Self::check_command_only(packet) {
Some(err) => Err(err),
None => Ok(Command::FadeOut),
},
CommandCode::Cp437Data => {
let Packet(_, payload) = packet;
Ok(Command::Cp437Data(
Origin(a as usize, b as usize),
ByteGrid::load(c as usize, d as usize, &payload),
))
} }
CommandCode::FadeOut => {
Self::packet_into_command_only(packet, Command::FadeOut)
}
CommandCode::Cp437Data => Self::packet_into_cp437(&packet),
CommandCode::CharBrightness => { CommandCode::CharBrightness => {
let Packet(_, payload) = packet; Self::packet_into_char_brightness(&packet)
Ok(Command::CharBrightness(
Origin(a as usize, b as usize),
ByteGrid::load(c as usize, d as usize, &payload),
))
} }
#[allow(deprecated)] #[allow(deprecated)]
CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy), CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy),
@ -316,7 +275,7 @@ impl Command {
}; };
Ok(Command::BitmapLinearWin( Ok(Command::BitmapLinearWin(
Origin(tiles_x as usize * TILE_SIZE, pixels_y as usize), Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize),
PixelGrid::load( PixelGrid::load(
tile_w as usize * TILE_SIZE, tile_w as usize * TILE_SIZE,
pixel_h as usize, pixel_h as usize,
@ -326,42 +285,18 @@ impl Command {
)) ))
} }
/// Helper method for `BitMapLinear*`-Commands into `Packet`
#[allow(clippy::cast_possible_truncation)]
fn bitmap_linear_into_packet(
command: CommandCode,
offset: Offset,
compression: CompressionCode,
payload: Vec<u8>,
) -> Packet {
let length = payload.len() as u16;
let payload = into_compressed(compression, payload);
Packet(
Header(
command.into(),
offset as u16,
length,
compression.into(),
0,
),
payload,
)
}
/// Helper method for creating empty packets only containing the command code
fn command_code_only(code: CommandCode) -> Packet {
Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![])
}
/// Helper method for checking that a packet is empty and only contains a command code /// Helper method for checking that a packet is empty and only contains a command code
fn check_command_only(packet: Packet) -> Option<TryFromPacketError> { fn packet_into_command_only(
packet: Packet,
command: Command,
) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet; let Packet(Header(_, a, b, c, d), payload) = packet;
if !payload.is_empty() { if !payload.is_empty() {
Some(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len()))
} else if a != 0 || b != 0 || c != 0 || d != 0 { } else if a != 0 || b != 0 || c != 0 || d != 0 {
Some(TryFromPacketError::ExtraneousHeaderValues) Err(TryFromPacketError::ExtraneousHeaderValues)
} else { } else {
None Ok(command)
} }
} }
@ -391,16 +326,63 @@ impl Command {
} }
Ok((BitVec::from_vec(payload), sub)) Ok((BitVec::from_vec(payload), sub))
} }
fn packet_into_char_brightness(
packet: &Packet,
) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, x, y, width, height), payload) = packet;
let grid =
PrimitiveGrid::load(*width as usize, *height as usize, payload);
let grid = match BrightnessGrid::try_from(grid) {
Ok(grid) => grid,
Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)),
};
Ok(Command::CharBrightness(
Origin::new(*x as usize, *y as usize),
grid,
))
}
fn packet_into_brightness(
packet: &Packet,
) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet;
if payload.len() != 1 {
return Err(TryFromPacketError::UnexpectedPayloadSize(
1,
payload.len(),
));
}
if *a != 0 || *b != 0 || *c != 0 || *d != 0 {
return Err(TryFromPacketError::ExtraneousHeaderValues);
}
match Brightness::try_from(payload[0]) {
Ok(b) => Ok(Command::Brightness(b)),
Err(_) => Err(TryFromPacketError::InvalidBrightness(payload[0])),
}
}
fn packet_into_cp437(
packet: &Packet,
) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet;
Ok(Command::Cp437Data(
Origin::new(*a as usize, *b as usize),
Cp437Grid::load(*c as usize, *d as usize, payload),
))
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bitvec::prelude::BitVec;
use crate::command::TryFromPacketError;
use crate::command_code::CommandCode;
use crate::{ use crate::{
ByteGrid, Command, CompressionCode, Header, Origin, Packet, PixelGrid, bitvec::prelude::BitVec, command::TryFromPacketError,
command_code::CommandCode, origin::Pixels, Brightness, Command,
CompressionCode, Header, Origin, Packet, PixelGrid, PrimitiveGrid,
}; };
fn round_trip(original: Command) { fn round_trip(original: Command) {
@ -443,7 +425,7 @@ mod tests {
#[test] #[test]
fn round_trip_brightness() { fn round_trip_brightness() {
round_trip(Command::Brightness(6)); round_trip(Command::Brightness(Brightness::try_from(6).unwrap()));
} }
#[test] #[test]
@ -454,12 +436,18 @@ mod tests {
#[test] #[test]
fn round_trip_char_brightness() { fn round_trip_char_brightness() {
round_trip(Command::CharBrightness(Origin(5, 2), ByteGrid::new(7, 5))); round_trip(Command::CharBrightness(
Origin::new(5, 2),
PrimitiveGrid::new(7, 5),
));
} }
#[test] #[test]
fn round_trip_cp437_data() { fn round_trip_cp437_data() {
round_trip(Command::Cp437Data(Origin(5, 2), ByteGrid::new(7, 5))); round_trip(Command::Cp437Data(
Origin::new(5, 2),
PrimitiveGrid::new(7, 5),
));
} }
#[test] #[test]
@ -486,7 +474,7 @@ mod tests {
compression, compression,
)); ));
round_trip(Command::BitmapLinearWin( round_trip(Command::BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
PixelGrid::max_sized(), PixelGrid::max_sized(),
compression, compression,
)); ));
@ -572,7 +560,7 @@ mod tests {
fn error_decompression_failed_win() { fn error_decompression_failed_win() {
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(16, 8), Origin::new(16, 8),
PixelGrid::new(8, 8), PixelGrid::new(8, 8),
compression, compression,
) )
@ -696,6 +684,9 @@ mod tests {
#[test] #[test]
fn origin_add() { fn origin_add() {
assert_eq!(Origin(4, 2), Origin(1, 0) + Origin(3, 2)); assert_eq!(
Origin::<Pixels>::new(4, 2),
Origin::new(1, 0) + Origin::new(3, 2)
);
} }
} }

View file

@ -1,4 +1,17 @@
/// Specifies the kind of compression to use. Availability depends on features. /// Specifies the kind of compression to use. Availability depends on features.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid};
/// // create command without payload compression
/// # let pixels = PixelGrid::max_sized();
/// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed);
///
/// // create command with payload compressed with lzma and appropriate header flags
/// # let pixels = PixelGrid::max_sized();
/// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Lzma);
/// ```
#[repr(u16)] #[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum CompressionCode { pub enum CompressionCode {

View file

@ -6,6 +6,15 @@ use log::{debug, info};
use crate::Packet; use crate::Packet;
/// A connection to the display. /// A connection to the display.
///
/// # Examples
/// ```rust
/// # use servicepoint::Command;
/// let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// .expect("connection failed");
/// connection.send(Command::Clear)
/// .expect("send failed");
/// ```
pub struct Connection { pub struct Connection {
socket: UdpSocket, socket: UdpSocket,
} }
@ -46,21 +55,12 @@ impl Connection {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
/// 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");
/// /// // turn off all pixels on display
/// // turn off all pixels
/// connection.send(Command::Clear) /// connection.send(Command::Clear)
/// .expect("send failed"); /// .expect("send failed");
///
/// // turn on all pixels
/// let mut pixels = PixelGrid::max_sized();
/// pixels.fill(true);
///
/// // send pixels to display
/// connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed))
/// .expect("send failed");
/// ``` /// ```
pub fn send( pub fn send(
&self, &self,

View file

@ -2,10 +2,13 @@
/// ///
/// The expectation is that you can create an equal instance with this data given the additional /// The expectation is that you can create an equal instance with this data given the additional
/// metadata needed. /// metadata needed.
pub trait DataRef { pub trait DataRef<T> {
/// Get the underlying bytes writable. /// Get the underlying bytes writable.
fn data_ref_mut(&mut self) -> &mut [u8]; ///
/// Note that depending on the struct this is implemented on, writing invalid values here might
/// lead to panics later in the lifetime of the program or on the receiving side.
fn data_ref_mut(&mut self) -> &mut [T];
/// Get the underlying bytes read-only. /// Get the underlying bytes read-only.
fn data_ref(&self) -> &[u8]; fn data_ref(&self) -> &[T];
} }

View file

@ -1,22 +1,55 @@
//! Abstractions for the UDP protocol of the CCCB servicepoint display. //! Abstractions for the UDP protocol of the CCCB servicepoint display.
//!
//! # Examples
//!
//! ```rust
//! use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
//!
//! let connection = servicepoint::Connection::open("127.0.0.1:2342")
//! .expect("connection failed");
//!
//! // turn off all pixels on display
//! connection.send(Command::Clear)
//! .expect("send failed");
//! ```
//!
//! ```rust
//! # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
//! // turn on all pixels in a grid
//! let mut pixels = PixelGrid::max_sized();
//! pixels.fill(true);
//!
//! // create command to send pixels
//! let command = Command::BitmapLinearWin(
//! servicepoint::Origin::new(0, 0),
//! pixels,
//! CompressionCode::Uncompressed
//! );
//!
//! // send command to display
//! connection.send(command).expect("send failed");
//! ```
use std::time::Duration; use std::time::Duration;
pub use bitvec; pub use bitvec;
use bitvec::prelude::{BitVec, Msb0}; use bitvec::prelude::{BitVec, Msb0};
pub use crate::byte_grid::ByteGrid; pub use crate::brightness::{Brightness, BrightnessGrid};
pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::command::{Command, Cp437Grid, Offset};
pub use crate::compression_code::CompressionCode; pub use crate::compression_code::CompressionCode;
pub use crate::connection::Connection; pub use crate::connection::Connection;
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::packet::{Header, Packet, Payload}; pub use crate::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid; pub use crate::pixel_grid::PixelGrid;
pub use crate::primitive_grid::PrimitiveGrid;
type SpBitVec = BitVec<u8, Msb0>; type SpBitVec = BitVec<u8, Msb0>;
mod byte_grid; mod brightness;
mod command; mod command;
mod command_code; mod command_code;
mod compression; mod compression;
@ -24,28 +57,84 @@ mod compression_code;
mod connection; mod connection;
mod data_ref; mod data_ref;
mod grid; mod grid;
mod origin;
mod packet; mod packet;
mod pixel_grid; mod pixel_grid;
mod primitive_grid;
/// size of a single tile in one dimension /// size of a single tile in one dimension
pub const TILE_SIZE: usize = 8; pub const TILE_SIZE: usize = 8;
/// tile count in the x-direction /// Display tile count in the x-direction
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
/// ```
pub const TILE_WIDTH: usize = 56; pub const TILE_WIDTH: usize = 56;
/// tile count in the y-direction /// Display tile count in the y-direction
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
/// ```
pub const TILE_HEIGHT: usize = 20; pub const TILE_HEIGHT: usize = 20;
/// screen width in pixels /// Display width in pixels
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
/// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
/// ```
pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE;
/// screen height in pixels /// Display height in pixels
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid};
/// let grid = PixelGrid::new(PIXEL_WIDTH, PIXEL_HEIGHT);
/// ```
pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE;
/// pixel count on whole screen /// pixel count on whole screen
pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT;
/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
///
/// # Examples
///
/// ```rust
/// # use std::time::Instant;
/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, PixelGrid};
/// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// # .expect("connection failed");
/// # let pixels = PixelGrid::max_sized();
/// loop {
/// let start = Instant::now();
///
/// // Change pixels here
///
/// connection.send(Command::BitmapLinearWin(
/// Origin::new(0,0),
/// pixels,
/// CompressionCode::Lzma
/// ))
/// .expect("send failed");
///
/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby
/// std::thread::sleep(FRAME_PACING - start.elapsed());
/// # break; // prevent doctest from hanging
/// }
/// ```
pub const FRAME_PACING: Duration = Duration::from_millis(30); pub const FRAME_PACING: Duration = Duration::from_millis(30);
// include README.md in doctest // include README.md in doctest

View file

@ -0,0 +1,48 @@
use std::marker::PhantomData;
/// An origin marks the top left position of a window sent to the display.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Origin<Unit: DisplayUnit> {
/// position in the width direction
pub x: usize,
/// position in the height direction
pub y: usize,
phantom_data: PhantomData<Unit>,
}
impl<Unit: DisplayUnit> Origin<Unit> {
/// Create a new `Origin` instance for the provided position.
pub fn new(x: usize, y: usize) -> Self {
Self {
x,
y,
phantom_data: PhantomData,
}
}
}
impl<T: DisplayUnit> std::ops::Add<Origin<T>> for Origin<T> {
type Output = Origin<T>;
fn add(self, rhs: Origin<T>) -> Self::Output {
Origin {
x: self.x + rhs.x,
y: self.y + rhs.y,
phantom_data: PhantomData,
}
}
}
pub trait DisplayUnit {}
/// Marks something to be measured in number of pixels.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Pixels();
/// Marks something to be measured in number of iles.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Tiles();
impl DisplayUnit for Pixels {}
impl DisplayUnit for Tiles {}

View file

@ -1,5 +1,12 @@
use std::mem::size_of; use std::mem::size_of;
use crate::command_code::CommandCode;
use crate::compression::into_compressed;
use crate::{
Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels,
TILE_SIZE,
};
/// A raw header. Should probably not be used directly. /// A raw header. Should probably not be used directly.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16); pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16);
@ -58,6 +65,151 @@ impl TryFrom<&[u8]> for Packet {
} }
} }
impl From<Command> for Packet {
/// Move the `Command` into a `Packet` instance for sending.
#[allow(clippy::cast_possible_truncation)]
fn from(value: Command) -> Self {
match value {
Command::Clear => Self::command_code_only(CommandCode::Clear),
Command::FadeOut => Self::command_code_only(CommandCode::FadeOut),
Command::HardReset => {
Self::command_code_only(CommandCode::HardReset)
}
#[allow(deprecated)]
Command::BitmapLegacy => {
Self::command_code_only(CommandCode::BitmapLegacy)
}
Command::CharBrightness(origin, grid) => Packet(
Header(
CommandCode::CharBrightness.into(),
origin.x as u16,
origin.y as u16,
grid.width() as u16,
grid.height() as u16,
),
grid.into(),
),
Command::Brightness(brightness) => Packet(
Header(
CommandCode::Brightness.into(),
0x00000,
0x0000,
0x0000,
0x0000,
),
vec![brightness.into()],
),
Command::BitmapLinearWin(origin, pixels, compression) => {
Self::bitmap_win_into_packet(origin, pixels, compression)
}
Command::BitmapLinear(offset, bits, compression) => {
Self::bitmap_linear_into_packet(
CommandCode::BitmapLinear,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearAnd(offset, bits, compression) => {
Self::bitmap_linear_into_packet(
CommandCode::BitmapLinearAnd,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearOr(offset, bits, compression) => {
Self::bitmap_linear_into_packet(
CommandCode::BitmapLinearOr,
offset,
compression,
bits.into(),
)
}
Command::BitmapLinearXor(offset, bits, compression) => {
Self::bitmap_linear_into_packet(
CommandCode::BitmapLinearXor,
offset,
compression,
bits.into(),
)
}
Command::Cp437Data(origin, grid) => Packet(
Header(
CommandCode::Cp437Data.into(),
origin.x as u16,
origin.y as u16,
grid.width() as u16,
grid.height() as u16,
),
grid.into(),
),
}
}
}
impl Packet {
/// Helper method for `BitMapLinear*`-Commands into `Packet`
#[allow(clippy::cast_possible_truncation)]
fn bitmap_linear_into_packet(
command: CommandCode,
offset: Offset,
compression: CompressionCode,
payload: Vec<u8>,
) -> Packet {
let length = payload.len() as u16;
let payload = into_compressed(compression, payload);
Packet(
Header(
command.into(),
offset as u16,
length,
compression.into(),
0,
),
payload,
)
}
#[allow(clippy::cast_possible_truncation)]
fn bitmap_win_into_packet(
origin: Origin<Pixels>,
pixels: PixelGrid,
compression: CompressionCode,
) -> Packet {
debug_assert_eq!(origin.x % 8, 0);
debug_assert_eq!(pixels.width() % 8, 0);
let tile_x = (origin.x / TILE_SIZE) as u16;
let tile_w = (pixels.width() / TILE_SIZE) as u16;
let pixel_h = pixels.height() as u16;
let payload = into_compressed(compression, pixels.into());
let command = match compression {
CompressionCode::Uncompressed => {
CommandCode::BitmapLinearWinUncompressed
}
#[cfg(feature = "compression_zlib")]
CompressionCode::Zlib => CommandCode::BitmapLinearWinZlib,
#[cfg(feature = "compression_bzip2")]
CompressionCode::Bzip2 => CommandCode::BitmapLinearWinBzip2,
#[cfg(feature = "compression_lzma")]
CompressionCode::Lzma => CommandCode::BitmapLinearWinLzma,
#[cfg(feature = "compression_zstd")]
CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd,
};
Packet(
Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h),
payload,
)
}
/// Helper method for creating empty packets only containing the command code
fn command_code_only(code: CommandCode) -> Packet {
Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![])
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{Header, Packet}; use crate::{Header, Packet};

View file

@ -158,7 +158,7 @@ impl Grid<bool> for PixelGrid {
} }
} }
impl DataRef for PixelGrid { impl DataRef<u8> for PixelGrid {
fn data_ref_mut(&mut self) -> &mut [u8] { fn data_ref_mut(&mut self) -> &mut [u8] {
self.bit_vec.as_raw_mut_slice() self.bit_vec.as_raw_mut_slice()
} }

View file

@ -2,40 +2,43 @@ use std::slice::{Iter, IterMut};
use crate::{DataRef, Grid}; use crate::{DataRef, Grid};
pub trait PrimitiveGridType: Sized + Default + Copy + Clone {}
impl<T: Sized + Default + Copy + Clone> PrimitiveGridType for T {}
/// A 2D grid of bytes /// A 2D grid of bytes
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ByteGrid { pub struct PrimitiveGrid<T: PrimitiveGridType> {
width: usize, width: usize,
height: usize, height: usize,
data: Vec<u8>, data: Vec<T>,
} }
impl ByteGrid { impl<T: PrimitiveGridType> PrimitiveGrid<T> {
/// Creates a new `ByteGrid` with the specified dimensions. /// Creates a new `PrimitiveGrid` with the specified dimensions.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - width: size in x-direction /// - width: size in x-direction
/// - height: size in y-direction /// - height: size in y-direction
/// ///
/// returns: `ByteGrid` initialized to 0. /// returns: `PrimitiveGrid` initialized to default value.
pub fn new(width: usize, height: usize) -> Self { pub fn new(width: usize, height: usize) -> Self {
Self { Self {
data: vec![0; width * height], data: vec![Default::default(); width * height],
width, width,
height, height,
} }
} }
/// Loads a `ByteGrid` with the specified dimensions from the provided data. /// Loads a `PrimitiveGrid` with the specified dimensions from the provided data.
/// ///
/// returns: `ByteGrid` that contains a copy of the provided data /// returns: `PrimitiveGrid` that contains a copy of the provided data
/// ///
/// # Panics /// # Panics
/// ///
/// - when the dimensions and data size do not match exactly. /// - when the dimensions and data size do not match exactly.
#[must_use] #[must_use]
pub fn load(width: usize, height: usize, data: &[u8]) -> Self { pub fn load(width: usize, height: usize, data: &[T]) -> Self {
assert_eq!(width * height, data.len()); assert_eq!(width * height, data.len());
Self { Self {
data: Vec::from(data), data: Vec::from(data),
@ -44,24 +47,24 @@ impl ByteGrid {
} }
} }
/// Iterate over all cells in `ByteGrid`. /// Iterate over all cells in `PrimitiveGrid`.
/// ///
/// Order is equivalent to the following loop: /// Order is equivalent to the following loop:
/// ``` /// ```
/// # use servicepoint::{ByteGrid, Grid}; /// # use servicepoint::{PrimitiveGrid, Grid};
/// # let grid = ByteGrid::new(2,2); /// # let grid = PrimitiveGrid::<u8>::new(2,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);
/// } /// }
/// } /// }
/// ``` /// ```
pub fn iter(&self) -> Iter<u8> { pub fn iter(&self) -> Iter<T> {
self.data.iter() self.data.iter()
} }
/// Iterate over all rows in `ByteGrid` top to bottom. /// Iterate over all rows in `PrimitiveGrid` top to bottom.
pub fn iter_rows(&self) -> IterRows { pub fn iter_rows(&self) -> IterRows<T> {
IterRows { IterRows {
byte_grid: self, byte_grid: self,
row: 0, row: 0,
@ -71,7 +74,7 @@ impl ByteGrid {
/// Returns an iterator that allows modifying each value. /// Returns an iterator that allows modifying each value.
/// ///
/// The iterator yields all cells from top left to bottom right. /// The iterator yields all cells from top left to bottom right.
pub fn iter_mut(&mut self) -> IterMut<u8> { pub fn iter_mut(&mut self) -> IterMut<T> {
self.data.iter_mut() self.data.iter_mut()
} }
@ -84,7 +87,7 @@ impl ByteGrid {
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// When accessing `x` or `y` out of bounds.
pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 { pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut T {
self.assert_in_bounds(x, y); self.assert_in_bounds(x, y);
&mut self.data[x + y * self.width] &mut self.data[x + y * self.width]
} }
@ -100,7 +103,7 @@ impl ByteGrid {
&mut self, &mut self,
x: isize, x: isize,
y: isize, y: isize,
) -> Option<&mut u8> { ) -> Option<&mut T> {
if self.is_in_bounds(x, y) { if self.is_in_bounds(x, y) {
Some(&mut self.data[x as usize + y as usize * self.width]) Some(&mut self.data[x as usize + y as usize * self.width])
} else { } else {
@ -109,8 +112,8 @@ impl ByteGrid {
} }
} }
impl Grid<u8> for ByteGrid { impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> {
/// Sets the value of the cell at the specified position in the `ByteGrid. /// Sets the value of the cell at the specified position in the `PrimitiveGrid.
/// ///
/// # Arguments /// # Arguments
/// ///
@ -120,7 +123,7 @@ impl Grid<u8> for ByteGrid {
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// When accessing `x` or `y` out of bounds.
fn set(&mut self, x: usize, y: usize, value: u8) { fn set(&mut self, x: usize, y: usize, value: T) {
self.assert_in_bounds(x, y); self.assert_in_bounds(x, y);
self.data[x + y * self.width] = value; self.data[x + y * self.width] = value;
} }
@ -134,12 +137,12 @@ impl Grid<u8> for ByteGrid {
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// When accessing `x` or `y` out of bounds.
fn get(&self, x: usize, y: usize) -> u8 { fn get(&self, x: usize, y: usize) -> T {
self.assert_in_bounds(x, y); self.assert_in_bounds(x, y);
self.data[x + y * self.width] self.data[x + y * self.width]
} }
fn fill(&mut self, value: u8) { fn fill(&mut self, value: T) {
self.data.fill(value); self.data.fill(value);
} }
@ -152,32 +155,32 @@ impl Grid<u8> for ByteGrid {
} }
} }
impl DataRef for ByteGrid { impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> {
/// Get the underlying byte rows mutable /// Get the underlying byte rows mutable
fn data_ref_mut(&mut self) -> &mut [u8] { fn data_ref_mut(&mut self) -> &mut [T] {
self.data.as_mut_slice() self.data.as_mut_slice()
} }
/// Get the underlying byte rows read only /// Get the underlying byte rows read only
fn data_ref(&self) -> &[u8] { fn data_ref(&self) -> &[T] {
self.data.as_slice() self.data.as_slice()
} }
} }
impl From<ByteGrid> for Vec<u8> { impl<T: PrimitiveGridType> From<PrimitiveGrid<T>> for Vec<T> {
/// Turn into the underlying `Vec<u8>` containing the rows of bytes. /// Turn into the underlying `Vec<u8>` containing the rows of bytes.
fn from(value: ByteGrid) -> Self { fn from(value: PrimitiveGrid<T>) -> Self {
value.data value.data
} }
} }
pub struct IterRows<'t> { pub struct IterRows<'t, T: PrimitiveGridType> {
byte_grid: &'t ByteGrid, byte_grid: &'t PrimitiveGrid<T>,
row: usize, row: usize,
} }
impl<'t> Iterator for IterRows<'t> { impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> {
type Item = Iter<'t, u8>; type Item = Iter<'t, T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.byte_grid.height { if self.row >= self.byte_grid.height {
@ -194,11 +197,11 @@ impl<'t> Iterator for IterRows<'t> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ByteGrid, DataRef, Grid}; use crate::{DataRef, Grid, PrimitiveGrid};
#[test] #[test]
fn fill() { fn fill() {
let mut grid = ByteGrid::new(2, 2); let mut grid = PrimitiveGrid::<usize>::new(2, 2);
assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]); assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]);
grid.fill(42); grid.fill(42);
@ -207,7 +210,7 @@ mod tests {
#[test] #[test]
fn get_set() { fn get_set() {
let mut grid = ByteGrid::new(2, 2); let mut grid = PrimitiveGrid::new(2, 2);
assert_eq!(grid.get(0, 0), 0); assert_eq!(grid.get(0, 0), 0);
assert_eq!(grid.get(1, 1), 0); assert_eq!(grid.get(1, 1), 0);
@ -222,7 +225,7 @@ mod tests {
#[test] #[test]
fn load() { fn load() {
let mut grid = ByteGrid::new(2, 3); let mut grid = PrimitiveGrid::new(2, 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) as u8); grid.set(x, y, (x + y) as u8);
@ -233,13 +236,13 @@ mod tests {
let data: Vec<u8> = grid.into(); let data: Vec<u8> = grid.into();
let grid = ByteGrid::load(2, 3, &data); let grid = PrimitiveGrid::load(2, 3, &data);
assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]);
} }
#[test] #[test]
fn mut_data_ref() { fn mut_data_ref() {
let mut vec = ByteGrid::new(2, 2); let mut vec = PrimitiveGrid::new(2, 2);
let data_ref = vec.data_ref_mut(); let data_ref = vec.data_ref_mut();
data_ref.copy_from_slice(&[1, 2, 3, 4]); data_ref.copy_from_slice(&[1, 2, 3, 4]);
@ -250,7 +253,7 @@ mod tests {
#[test] #[test]
fn iter() { fn iter() {
let mut vec = ByteGrid::new(2, 2); let mut vec = PrimitiveGrid::new(2, 2);
vec.set(1, 1, 5); vec.set(1, 1, 5);
let mut iter = vec.iter(); let mut iter = vec.iter();
@ -262,7 +265,7 @@ mod tests {
#[test] #[test]
fn iter_mut() { fn iter_mut() {
let mut vec = ByteGrid::new(2, 3); let mut vec = PrimitiveGrid::new(2, 3);
for (index, cell) in vec.iter_mut().enumerate() { for (index, cell) in vec.iter_mut().enumerate() {
*cell = index as u8; *cell = index as u8;
} }
@ -272,7 +275,7 @@ mod tests {
#[test] #[test]
fn iter_rows() { fn iter_rows() {
let vec = ByteGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); let vec = PrimitiveGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]);
for (y, row) in vec.iter_rows().enumerate() { for (y, row) in vec.iter_rows().enumerate() {
for (x, val) in row.enumerate() { for (x, val) in row.enumerate() {
assert_eq!(*val, (x + y) as u8); assert_eq!(*val, (x + y) as u8);
@ -283,20 +286,20 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn out_of_bounds_x() { fn out_of_bounds_x() {
let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
vec.set(2, 1, 5); vec.set(2, 1, 5);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn out_of_bounds_y() { fn out_of_bounds_y() {
let vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); let vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
vec.get(1, 2); vec.get(1, 2);
} }
#[test] #[test]
fn ref_mut() { fn ref_mut() {
let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]); let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
let top_left = vec.get_ref_mut(0, 0); let top_left = vec.get_ref_mut(0, 0);
*top_left += 5; *top_left += 5;
@ -307,7 +310,7 @@ mod tests {
#[test] #[test]
fn optional() { fn optional() {
let mut grid = ByteGrid::load(2, 2, &[0, 1, 2, 3]); let mut grid = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
grid.set_optional(0, 0, 5); grid.set_optional(0, 0, 5);
grid.set_optional(-1, 0, 8); grid.set_optional(-1, 0, 8);
grid.set_optional(0, 8, 42); grid.set_optional(0, 8, 42);

View file

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

View file

@ -33,4 +33,4 @@ include = ["servicepoint"]
extra_bindings = ["servicepoint"] extra_bindings = ["servicepoint"]
[parse.expand] [parse.expand]
#all_features = true all_features = true

View file

@ -70,14 +70,18 @@ typedef uint16_t sp_CompressionCode;
#endif // __cplusplus #endif // __cplusplus
/** /**
* A fixed-size vector of bits * A display brightness value, checked for correct value range
*/ */
typedef struct sp_BitVec sp_BitVec; typedef struct sp_Brightness sp_Brightness;
/** /**
* A 2D grid of bytes * Opaque struct needed for correct code generation.
*/ */
typedef struct sp_ByteGrid sp_ByteGrid; typedef struct sp_CBitVec sp_CBitVec;
typedef struct sp_CBrightnessGrid sp_CBrightnessGrid;
typedef struct sp_CCp437Grid sp_CCp437Grid;
/** /**
* A command to send to the display. * A command to send to the display.
@ -99,6 +103,16 @@ typedef struct sp_Packet sp_Packet;
*/ */
typedef struct sp_PixelGrid sp_PixelGrid; typedef struct sp_PixelGrid sp_PixelGrid;
/**
* A 2D grid of bytes
*/
typedef struct sp_PrimitiveGrid_Brightness sp_PrimitiveGrid_Brightness;
/**
* A 2D grid of bytes
*/
typedef struct sp_PrimitiveGrid_u8 sp_PrimitiveGrid_u8;
/** /**
* Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. * Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
* *
@ -127,9 +141,20 @@ typedef struct sp_CByteSlice {
typedef size_t sp_Offset; typedef size_t sp_Offset;
/** /**
* Type alias for documenting the meaning of the u16 in enum values * A grid containing brightness values.
*/ */
typedef uint8_t sp_Brightness; typedef struct sp_PrimitiveGrid_Brightness sp_BrightnessGrid;
/**
* A grid containing codepage 437 characters.
*
* The encoding is currently not enforced.
*/
typedef struct sp_PrimitiveGrid_u8 sp_Cp437Grid;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -147,7 +172,7 @@ extern "C" {
* - 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_dealloc`. * by explicitly calling `sp_bit_vec_dealloc`.
*/ */
struct sp_BitVec *sp_bit_vec_clone(const struct sp_BitVec *this_); struct sp_CBitVec *sp_bit_vec_clone(const struct sp_CBitVec *this_);
/** /**
* Deallocates a `BitVec`. * Deallocates a `BitVec`.
@ -160,7 +185,7 @@ struct sp_BitVec *sp_bit_vec_clone(const struct sp_BitVec *this_);
* - `this` is not used concurrently or after this call * - `this` is not used concurrently or after this call
* - `this` was not passed to another consuming function, e.g. to create a `Command` * - `this` was not passed to another consuming function, e.g. to create a `Command`
*/ */
void sp_bit_vec_dealloc(struct sp_BitVec *this_); void sp_bit_vec_dealloc(struct sp_CBitVec *this_);
/** /**
* Sets the value of all bits in the `BitVec`. * Sets the value of all bits in the `BitVec`.
@ -176,7 +201,7 @@ void sp_bit_vec_dealloc(struct sp_BitVec *this_);
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
* - `this` is not written to or read from concurrently * - `this` is not written to or read from concurrently
*/ */
void sp_bit_vec_fill(struct sp_BitVec *this_, bool value); void sp_bit_vec_fill(struct sp_CBitVec *this_, bool value);
/** /**
* Gets the value of a bit from the `BitVec`. * Gets the value of a bit from the `BitVec`.
@ -199,7 +224,7 @@ void sp_bit_vec_fill(struct sp_BitVec *this_, bool value);
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
* - `this` is not written to concurrently * - `this` is not written to concurrently
*/ */
bool sp_bit_vec_get(const struct sp_BitVec *this_, size_t index); bool sp_bit_vec_get(const struct sp_CBitVec *this_, size_t index);
/** /**
* Returns true if length is 0. * Returns true if length is 0.
@ -210,7 +235,7 @@ bool sp_bit_vec_get(const struct sp_BitVec *this_, size_t index);
* *
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
*/ */
bool sp_bit_vec_is_empty(const struct sp_BitVec *this_); bool sp_bit_vec_is_empty(const struct sp_CBitVec *this_);
/** /**
* Gets the length of the `BitVec` in bits. * Gets the length of the `BitVec` in bits.
@ -221,7 +246,7 @@ bool sp_bit_vec_is_empty(const struct sp_BitVec *this_);
* *
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
*/ */
size_t sp_bit_vec_len(const struct sp_BitVec *this_); size_t sp_bit_vec_len(const struct sp_CBitVec *this_);
/** /**
* Interpret the data as a series of bits and load then into a new `BitVec` instance. * Interpret the data as a series of bits and load then into a new `BitVec` instance.
@ -235,8 +260,8 @@ size_t sp_bit_vec_len(const struct sp_BitVec *this_);
* - the returned instance is freed in some way, either by using a consuming function or * - the returned instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_bit_vec_dealloc`. * by explicitly calling `sp_bit_vec_dealloc`.
*/ */
struct sp_BitVec *sp_bit_vec_load(const uint8_t *data, struct sp_CBitVec *sp_bit_vec_load(const uint8_t *data,
size_t data_length); size_t data_length);
/** /**
* Creates a new `BitVec` instance. * Creates a new `BitVec` instance.
@ -258,7 +283,7 @@ struct sp_BitVec *sp_bit_vec_load(const uint8_t *data,
* - the returned instance is freed in some way, either by using a consuming function or * - the returned instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_bit_vec_dealloc`. * by explicitly calling `sp_bit_vec_dealloc`.
*/ */
struct sp_BitVec *sp_bit_vec_new(size_t size); struct sp_CBitVec *sp_bit_vec_new(size_t size);
/** /**
* Sets the value of a bit in the `BitVec`. * Sets the value of a bit in the `BitVec`.
@ -282,7 +307,7 @@ struct sp_BitVec *sp_bit_vec_new(size_t size);
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
* - `this` is not written to or read from concurrently * - `this` is not written to or read from concurrently
*/ */
bool sp_bit_vec_set(struct sp_BitVec *this_, size_t index, bool value); void sp_bit_vec_set(struct sp_CBitVec *this_, size_t index, bool value);
/** /**
* Gets an unsafe reference to the data of the `BitVec` instance. * Gets an unsafe reference to the data of the `BitVec` instance.
@ -295,37 +320,37 @@ bool sp_bit_vec_set(struct sp_BitVec *this_, size_t index, bool value);
* - the returned memory range is never accessed after the passed `BitVec` has been freed * - the returned memory range is never accessed after the passed `BitVec` has been freed
* - the returned memory range is never accessed concurrently, either via the `BitVec` or directly * - the returned memory range is never accessed concurrently, either via the `BitVec` or directly
*/ */
struct sp_CByteSlice sp_bit_vec_unsafe_data_ref(struct sp_BitVec *this_); struct sp_CByteSlice sp_bit_vec_unsafe_data_ref(struct sp_CBitVec *this_);
/** /**
* Clones a `ByteGrid`. * Clones a `BrightnessGrid`.
* *
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
* - `this` is not written to concurrently * - `this` 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_byte_grid_dealloc`. * by explicitly calling `sp_brightness_grid_dealloc`.
*/ */
struct sp_ByteGrid *sp_byte_grid_clone(const struct sp_ByteGrid *this_); struct sp_CBrightnessGrid *sp_brightness_grid_clone(const struct sp_CBrightnessGrid *this_);
/** /**
* Deallocates a `ByteGrid`. * Deallocates a `BrightnessGrid`.
* *
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
* - `this` is not used concurrently or after this call * - `this` is not used concurrently or after this call
* - `this` was not passed to another consuming function, e.g. to create a `Command` * - `this` was not passed to another consuming function, e.g. to create a `Command`
*/ */
void sp_byte_grid_dealloc(struct sp_ByteGrid *this_); void sp_brightness_grid_dealloc(struct sp_CBrightnessGrid *this_);
/** /**
* Sets the value of all cells in the `ByteGrid`. * Sets the value of all cells in the `BrightnessGrid`.
* *
* # Arguments * # Arguments
* *
@ -336,10 +361,10 @@ void sp_byte_grid_dealloc(struct sp_ByteGrid *this_);
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
* - `this` is not written to or read from concurrently * - `this` is not written to or read from concurrently
*/ */
void sp_byte_grid_fill(struct sp_ByteGrid *this_, uint8_t value); void sp_brightness_grid_fill(struct sp_CBrightnessGrid *this_, uint8_t value);
/** /**
* Gets the current value at the specified position. * Gets the current value at the specified position.
@ -357,13 +382,15 @@ void sp_byte_grid_fill(struct sp_ByteGrid *this_, uint8_t value);
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
* - `this` is not written to concurrently * - `this` is not written to concurrently
*/ */
uint8_t sp_byte_grid_get(const struct sp_ByteGrid *this_, size_t x, size_t y); uint8_t sp_brightness_grid_get(const struct sp_CBrightnessGrid *this_,
size_t x,
size_t y);
/** /**
* Gets the height of the `ByteGrid` instance. * Gets the height of the `BrightnessGrid` instance.
* *
* # Arguments * # Arguments
* *
@ -373,12 +400,12 @@ uint8_t sp_byte_grid_get(const struct sp_ByteGrid *this_, size_t x, size_t y);
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
*/ */
size_t sp_byte_grid_height(const struct sp_ByteGrid *this_); size_t sp_brightness_grid_height(const struct sp_CBrightnessGrid *this_);
/** /**
* Loads a `ByteGrid` with the specified dimensions from the provided data. * Loads a `BrightnessGrid` with the specified dimensions from the provided data.
* *
* # Panics * # Panics
* *
@ -391,30 +418,30 @@ size_t sp_byte_grid_height(const struct sp_ByteGrid *this_);
* - `data` points to a valid memory location of at least `data_length` * - `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_byte_grid_dealloc`. * by explicitly calling `sp_brightness_grid_dealloc`.
*/ */
struct sp_ByteGrid *sp_byte_grid_load(size_t width, struct sp_CBrightnessGrid *sp_brightness_grid_load(size_t width,
size_t height, size_t height,
const uint8_t *data, const uint8_t *data,
size_t data_length); size_t data_length);
/** /**
* Creates a new `ByteGrid` with the specified dimensions. * Creates a new `BrightnessGrid` with the specified dimensions.
* *
* returns: `ByteGrid` initialized to 0. * returns: `BrightnessGrid` initialized to 0.
* *
* # Safety * # 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_byte_grid_dealloc`. * by explicitly calling `sp_brightness_grid_dealloc`.
*/ */
struct sp_ByteGrid *sp_byte_grid_new(size_t width, struct sp_CBrightnessGrid *sp_brightness_grid_new(size_t width,
size_t height); size_t height);
/** /**
* Sets the value of the specified position in the `ByteGrid`. * Sets the value of the specified position in the `BrightnessGrid`.
* *
* # Arguments * # Arguments
* *
@ -435,26 +462,26 @@ struct sp_ByteGrid *sp_byte_grid_new(size_t width,
* - `this` points to a valid `BitVec` * - `this` points to a valid `BitVec`
* - `this` is not written to or read from concurrently * - `this` is not written to or read from concurrently
*/ */
void sp_byte_grid_set(struct sp_ByteGrid *this_, void sp_brightness_grid_set(struct sp_CBrightnessGrid *this_,
size_t x, size_t x,
size_t y, size_t y,
uint8_t value); uint8_t value);
/** /**
* Gets an unsafe reference to the data of the `ByteGrid` instance. * Gets an unsafe reference to the data of the `BrightnessGrid` instance.
* *
* ## Safety * ## Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
* - the returned memory range is never accessed after the passed `ByteGrid` has been freed * - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed
* - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly * - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly
*/ */
struct sp_CByteSlice sp_byte_grid_unsafe_data_ref(struct sp_ByteGrid *this_); struct sp_CByteSlice sp_brightness_grid_unsafe_data_ref(struct sp_CBrightnessGrid *this_);
/** /**
* Gets the width of the `ByteGrid` instance. * Gets the width of the `BrightnessGrid` instance.
* *
* # Arguments * # Arguments
* *
@ -464,9 +491,9 @@ struct sp_CByteSlice sp_byte_grid_unsafe_data_ref(struct sp_ByteGrid *this_);
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `this` points to a valid `ByteGrid` * - `this` points to a valid `BrightnessGrid`
*/ */
size_t sp_byte_grid_width(const struct sp_ByteGrid *this_); size_t sp_brightness_grid_width(const struct sp_CBrightnessGrid *this_);
/** /**
* Allocates a new `Command::BitmapLinear` instance. * Allocates a new `Command::BitmapLinear` instance.
@ -483,7 +510,7 @@ size_t sp_byte_grid_width(const struct sp_ByteGrid *this_);
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct sp_Command *sp_command_bitmap_linear(sp_Offset offset, struct sp_Command *sp_command_bitmap_linear(sp_Offset offset,
struct sp_BitVec *bit_vec, struct sp_CBitVec *bit_vec,
sp_CompressionCode compression); sp_CompressionCode compression);
/** /**
@ -501,7 +528,7 @@ struct sp_Command *sp_command_bitmap_linear(sp_Offset offset,
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset, struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset,
struct sp_BitVec *bit_vec, struct sp_CBitVec *bit_vec,
sp_CompressionCode compression); sp_CompressionCode compression);
/** /**
@ -519,7 +546,7 @@ struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset,
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct sp_Command *sp_command_bitmap_linear_or(sp_Offset offset, struct sp_Command *sp_command_bitmap_linear_or(sp_Offset offset,
struct sp_BitVec *bit_vec, struct sp_CBitVec *bit_vec,
sp_CompressionCode compression); sp_CompressionCode compression);
/** /**
@ -556,11 +583,16 @@ struct sp_Command *sp_command_bitmap_linear_win(size_t x,
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset, struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset,
struct sp_BitVec *bit_vec, struct sp_CBitVec *bit_vec,
sp_CompressionCode compression); sp_CompressionCode compression);
/** /**
* Allocates a new `Command::Brightness` instance. * Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the
* same value.
*
* # Panics
*
* - When the provided brightness value is out of range (0-11).
* *
* # Safety * # Safety
* *
@ -569,7 +601,7 @@ struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset,
* - the returned `Command` instance is freed in some way, either by using a consuming function or * - the returned `Command` instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct sp_Command *sp_command_brightness(sp_Brightness brightness); struct sp_Command *sp_command_brightness(uint8_t brightness);
/** /**
* Allocates a new `Command::CharBrightness` instance. * Allocates a new `Command::CharBrightness` instance.
@ -586,7 +618,7 @@ struct sp_Command *sp_command_brightness(sp_Brightness brightness);
*/ */
struct sp_Command *sp_command_char_brightness(size_t x, struct sp_Command *sp_command_char_brightness(size_t x,
size_t y, size_t y,
struct sp_ByteGrid *byte_grid); sp_BrightnessGrid *byte_grid);
/** /**
* Allocates a new `Command::Clear` instance. * Allocates a new `Command::Clear` instance.
@ -629,7 +661,7 @@ struct sp_Command *sp_command_clone(const struct sp_Command *original);
*/ */
struct sp_Command *sp_command_cp437_data(size_t x, struct sp_Command *sp_command_cp437_data(size_t x,
size_t y, size_t y,
struct sp_ByteGrid *byte_grid); sp_Cp437Grid *byte_grid);
/** /**
* Deallocates a `Command`. * Deallocates a `Command`.
@ -732,6 +764,179 @@ struct sp_Connection *sp_connection_open(const char *host);
bool sp_connection_send(const struct sp_Connection *connection, bool sp_connection_send(const struct sp_Connection *connection,
struct sp_Packet *packet); struct sp_Packet *packet);
/**
* Clones a `Cp437Grid`.
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
* - `this` is not written to concurrently
* - the returned instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_cp437_grid_dealloc`.
*/
struct sp_CCp437Grid *sp_cp437_grid_clone(const struct sp_CCp437Grid *this_);
/**
* Deallocates a `Cp437Grid`.
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
* - `this` is not used concurrently or after this call
* - `this` was not passed to another consuming function, e.g. to create a `Command`
*/
void sp_cp437_grid_dealloc(struct sp_CCp437Grid *this_);
/**
* Sets the value of all cells in the `Cp437Grid`.
*
* # Arguments
*
* * `this`: instance to write to
* * `value`: the value to set all cells to
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
* - `this` is not written to or read from concurrently
*/
void sp_cp437_grid_fill(struct sp_CCp437Grid *this_, uint8_t value);
/**
* Gets the current value at the specified position.
*
* # Arguments
*
* * `this`: instance to read from
* * `x` and `y`: position of the cell to read
*
* # Panics
*
* When accessing `x` or `y` out of bounds.
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
* - `this` is not written to concurrently
*/
uint8_t sp_cp437_grid_get(const struct sp_CCp437Grid *this_,
size_t x,
size_t y);
/**
* Gets the height of the `Cp437Grid` instance.
*
* # Arguments
*
* * `this`: instance to read from
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
*/
size_t sp_cp437_grid_height(const struct sp_CCp437Grid *this_);
/**
* Loads a `Cp437Grid` with the specified dimensions from the provided data.
*
* # Panics
*
* When the provided `data_length` is not sufficient for the `height` and `width`
*
* # Safety
*
* The caller has to make sure that:
*
* - `data` points to a valid memory location of at least `data_length`
* bytes in size.
* - the returned instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_cp437_grid_dealloc`.
*/
struct sp_CCp437Grid *sp_cp437_grid_load(size_t width,
size_t height,
const uint8_t *data,
size_t data_length);
/**
* Creates a new `Cp437Grid` with the specified dimensions.
*
* returns: `Cp437Grid` initialized to 0.
*
* # Safety
*
* The caller has to make sure that:
*
* - the returned instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_cp437_grid_dealloc`.
*/
struct sp_CCp437Grid *sp_cp437_grid_new(size_t width,
size_t height);
/**
* Sets the value of the specified position in the `Cp437Grid`.
*
* # Arguments
*
* * `this`: instance to write to
* * `x` and `y`: position of the cell
* * `value`: the value to write to the cell
*
* returns: old value of the cell
*
* # Panics
*
* When accessing `x` or `y` out of bounds.
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `BitVec`
* - `this` is not written to or read from concurrently
*/
void sp_cp437_grid_set(struct sp_CCp437Grid *this_,
size_t x,
size_t y,
uint8_t value);
/**
* Gets an unsafe reference to the data of the `Cp437Grid` instance.
*
* ## Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
* - the returned memory range is never accessed after the passed `Cp437Grid` has been freed
* - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly
*/
struct sp_CByteSlice sp_cp437_grid_unsafe_data_ref(struct sp_CCp437Grid *this_);
/**
* Gets the width of the `Cp437Grid` instance.
*
* # Arguments
*
* * `this`: instance to read from
*
* # Safety
*
* The caller has to make sure that:
*
* - `this` points to a valid `Cp437Grid`
*/
size_t sp_cp437_grid_width(const struct sp_CCp437Grid *this_);
/** /**
* Deallocates a `Packet`. * Deallocates a `Packet`.
* *

View file

@ -0,0 +1,233 @@
//! C functions for interacting with `BrightnessGrid`s
//!
//! prefix `sp_brightness_grid_`
use servicepoint::{Brightness, BrightnessGrid, DataRef, Grid, PrimitiveGrid};
use std::intrinsics::transmute;
use crate::c_slice::CByteSlice;
/// C-wrapper for grid containing brightness values.
#[derive(Clone)]
pub struct CBrightnessGrid(pub(crate) BrightnessGrid);
/// Creates a new `BrightnessGrid` with the specified dimensions.
///
/// returns: `BrightnessGrid` initialized to 0.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_new(
width: usize,
height: usize,
) -> *mut CBrightnessGrid {
Box::into_raw(Box::new(CBrightnessGrid(BrightnessGrid::new(
width, height,
))))
}
/// Loads a `BrightnessGrid` with the specified dimensions from the provided data.
///
/// # Panics
///
/// When the provided `data_length` is not sufficient for the `height` and `width`
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `data` points to a valid memory location of at least `data_length`
/// bytes in size.
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut CBrightnessGrid {
let data = std::slice::from_raw_parts(data, data_length);
let grid = PrimitiveGrid::load(width, height, data);
let grid =
BrightnessGrid::try_from(grid).expect("invalid brightness value");
Box::into_raw(Box::new(CBrightnessGrid(grid)))
}
/// Clones a `BrightnessGrid`.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
/// - `this` is not written to concurrently
/// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_brightness_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_clone(
this: *const CBrightnessGrid,
) -> *mut CBrightnessGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `BrightnessGrid`.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
/// - `this` is not used concurrently or after this call
/// - `this` was not passed to another consuming function, e.g. to create a `Command`
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_dealloc(
this: *mut CBrightnessGrid,
) {
_ = Box::from_raw(this);
}
/// Gets the current value at the specified position.
///
/// # Arguments
///
/// * `this`: instance to read from
/// * `x` and `y`: position of the cell to read
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
/// - `this` is not written to concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_get(
this: *const CBrightnessGrid,
x: usize,
y: usize,
) -> u8 {
(*this).0.get(x, y).into()
}
/// Sets the value of the specified position in the `BrightnessGrid`.
///
/// # Arguments
///
/// * `this`: instance to write to
/// * `x` and `y`: position of the cell
/// * `value`: the value to write to the cell
///
/// returns: old value of the cell
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BitVec`
/// - `this` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_set(
this: *mut CBrightnessGrid,
x: usize,
y: usize,
value: u8,
) {
let brightness =
Brightness::try_from(value).expect("invalid brightness value");
(*this).0.set(x, y, brightness);
}
/// Sets the value of all cells in the `BrightnessGrid`.
///
/// # Arguments
///
/// * `this`: instance to write to
/// * `value`: the value to set all cells to
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
/// - `this` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_fill(
this: *mut CBrightnessGrid,
value: u8,
) {
let brightness =
Brightness::try_from(value).expect("invalid brightness value");
(*this).0.fill(brightness);
}
/// Gets the width of the `BrightnessGrid` instance.
///
/// # Arguments
///
/// * `this`: instance to read from
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_width(
this: *const CBrightnessGrid,
) -> usize {
(*this).0.width()
}
/// Gets the height of the `BrightnessGrid` instance.
///
/// # Arguments
///
/// * `this`: instance to read from
///
/// # Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_height(
this: *const CBrightnessGrid,
) -> usize {
(*this).0.height()
}
/// Gets an unsafe reference to the data of the `BrightnessGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure that:
///
/// - `this` points to a valid `BrightnessGrid`
/// - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed
/// - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref(
this: *mut CBrightnessGrid,
) -> CByteSlice {
assert_eq!(std::mem::size_of::<Brightness>(), 1);
let data = (*this).0.data_ref_mut();
let data: &mut [u8] = transmute(data);
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}

View file

@ -5,11 +5,12 @@
use std::ptr::null_mut; use std::ptr::null_mut;
use servicepoint::{ use servicepoint::{
Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet, Brightness, Command, CompressionCode, Offset, Origin, Packet, PixelGrid,
PixelGrid,
}; };
use crate::bit_vec::CBitVec; use crate::bit_vec::CBitVec;
use crate::brightness_grid::CBrightnessGrid;
use crate::cp437_grid::CCp437Grid;
/// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. /// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.
/// ///
@ -91,7 +92,12 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command {
Box::into_raw(Box::new(Command::FadeOut)) Box::into_raw(Box::new(Command::FadeOut))
} }
/// Allocates a new `Command::Brightness` instance. /// Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the
/// same value.
///
/// # Panics
///
/// - When the provided brightness value is out of range (0-11).
/// ///
/// # Safety /// # Safety
/// ///
@ -100,9 +106,9 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command {
/// - the returned `Command` instance is freed in some way, either by using a consuming function or /// - the returned `Command` instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_dealloc`. /// by explicitly calling `sp_command_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_brightness( pub unsafe extern "C" fn sp_command_brightness(brightness: u8) -> *mut Command {
brightness: Brightness, let brightness =
) -> *mut Command { Brightness::try_from(brightness).expect("invalid brightness");
Box::into_raw(Box::new(Command::Brightness(brightness))) Box::into_raw(Box::new(Command::Brightness(brightness)))
} }
@ -121,10 +127,13 @@ pub unsafe extern "C" fn sp_command_brightness(
pub unsafe extern "C" fn sp_command_char_brightness( pub unsafe extern "C" fn sp_command_char_brightness(
x: usize, x: usize,
y: usize, y: usize,
byte_grid: *mut ByteGrid, byte_grid: *mut CBrightnessGrid,
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid); let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::CharBrightness(Origin(x, y), byte_grid))) Box::into_raw(Box::new(Command::CharBrightness(
Origin::new(x, y),
byte_grid.0,
)))
} }
/// Allocates a new `Command::BitmapLinear` instance. /// Allocates a new `Command::BitmapLinear` instance.
@ -246,10 +255,10 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
pub unsafe extern "C" fn sp_command_cp437_data( pub unsafe extern "C" fn sp_command_cp437_data(
x: usize, x: usize,
y: usize, y: usize,
byte_grid: *mut ByteGrid, byte_grid: *mut CCp437Grid,
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid); let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid))) Box::into_raw(Box::new(Command::Cp437Data(Origin::new(x, y), byte_grid.0)))
} }
/// Allocates a new `Command::BitmapLinearWin` instance. /// Allocates a new `Command::BitmapLinearWin` instance.
@ -273,7 +282,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win(
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(pixel_grid); let byte_grid = *Box::from_raw(pixel_grid);
Box::into_raw(Box::new(Command::BitmapLinearWin( Box::into_raw(Box::new(Command::BitmapLinearWin(
Origin(x, y), Origin::new(x, y),
byte_grid, byte_grid,
compression_code, compression_code,
))) )))

View file

@ -1,30 +1,36 @@
//! C functions for interacting with `ByteGrid`s //! C functions for interacting with `Cp437Grid`s
//! //!
//! prefix `sp_byte_grid_` //! prefix `sp_cp437_grid_`
use servicepoint::{ByteGrid, DataRef, Grid}; use servicepoint::{Cp437Grid, DataRef, Grid};
use crate::c_slice::CByteSlice; use crate::c_slice::CByteSlice;
/// Creates a new `ByteGrid` with the specified dimensions. /// A C-wrapper for grid containing codepage 437 characters.
/// ///
/// returns: `ByteGrid` initialized to 0. /// The encoding is currently not enforced.
#[derive(Clone)]
pub struct CCp437Grid(pub(crate) Cp437Grid);
/// Creates a new `Cp437Grid` with the specified dimensions.
///
/// returns: `Cp437Grid` initialized to 0.
/// ///
/// # Safety /// # 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_byte_grid_dealloc`. /// by explicitly calling `sp_cp437_grid_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_new( pub unsafe extern "C" fn sp_cp437_grid_new(
width: usize, width: usize,
height: usize, height: usize,
) -> *mut ByteGrid { ) -> *mut CCp437Grid {
Box::into_raw(Box::new(ByteGrid::new(width, height))) Box::into_raw(Box::new(CCp437Grid(Cp437Grid::new(width, height))))
} }
/// Loads a `ByteGrid` with the specified dimensions from the provided data. /// Loads a `Cp437Grid` with the specified dimensions from the provided data.
/// ///
/// # Panics /// # Panics
/// ///
@ -37,46 +43,46 @@ pub unsafe extern "C" fn sp_byte_grid_new(
/// - `data` points to a valid memory location of at least `data_length` /// - `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_byte_grid_dealloc`. /// by explicitly calling `sp_cp437_grid_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_load( pub unsafe extern "C" fn sp_cp437_grid_load(
width: usize, width: usize,
height: usize, height: usize,
data: *const u8, data: *const u8,
data_length: usize, data_length: usize,
) -> *mut ByteGrid { ) -> *mut CCp437Grid {
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(ByteGrid::load(width, height, data))) Box::into_raw(Box::new(CCp437Grid(Cp437Grid::load(width, height, data))))
} }
/// Clones a `ByteGrid`. /// Clones a `Cp437Grid`.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
/// - `this` is not written to concurrently /// - `this` 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_byte_grid_dealloc`. /// by explicitly calling `sp_cp437_grid_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_clone( pub unsafe extern "C" fn sp_cp437_grid_clone(
this: *const ByteGrid, this: *const CCp437Grid,
) -> *mut ByteGrid { ) -> *mut CCp437Grid {
Box::into_raw(Box::new((*this).clone())) Box::into_raw(Box::new((*this).clone()))
} }
/// Deallocates a `ByteGrid`. /// Deallocates a `Cp437Grid`.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
/// - `this` is not used concurrently or after this call /// - `this` is not used concurrently or after this call
/// - `this` was not passed to another consuming function, e.g. to create a `Command` /// - `this` was not passed to another consuming function, e.g. to create a `Command`
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_dealloc(this: *mut ByteGrid) { pub unsafe extern "C" fn sp_cp437_grid_dealloc(this: *mut CCp437Grid) {
_ = Box::from_raw(this); _ = Box::from_raw(this);
} }
@ -95,18 +101,18 @@ pub unsafe extern "C" fn sp_byte_grid_dealloc(this: *mut ByteGrid) {
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
/// - `this` is not written to concurrently /// - `this` is not written to concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_get( pub unsafe extern "C" fn sp_cp437_grid_get(
this: *const ByteGrid, this: *const CCp437Grid,
x: usize, x: usize,
y: usize, y: usize,
) -> u8 { ) -> u8 {
(*this).get(x, y) (*this).0.get(x, y)
} }
/// Sets the value of the specified position in the `ByteGrid`. /// Sets the value of the specified position in the `Cp437Grid`.
/// ///
/// # Arguments /// # Arguments
/// ///
@ -127,16 +133,16 @@ pub unsafe extern "C" fn sp_byte_grid_get(
/// - `this` points to a valid `BitVec` /// - `this` points to a valid `BitVec`
/// - `this` is not written to or read from concurrently /// - `this` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_set( pub unsafe extern "C" fn sp_cp437_grid_set(
this: *mut ByteGrid, this: *mut CCp437Grid,
x: usize, x: usize,
y: usize, y: usize,
value: u8, value: u8,
) { ) {
(*this).set(x, y, value); (*this).0.set(x, y, value);
} }
/// Sets the value of all cells in the `ByteGrid`. /// Sets the value of all cells in the `Cp437Grid`.
/// ///
/// # Arguments /// # Arguments
/// ///
@ -147,14 +153,14 @@ pub unsafe extern "C" fn sp_byte_grid_set(
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
/// - `this` is not written to or read from concurrently /// - `this` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) { pub unsafe extern "C" fn sp_cp437_grid_fill(this: *mut CCp437Grid, value: u8) {
(*this).fill(value); (*this).0.fill(value);
} }
/// Gets the width of the `ByteGrid` instance. /// Gets the width of the `Cp437Grid` instance.
/// ///
/// # Arguments /// # Arguments
/// ///
@ -164,13 +170,13 @@ pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) {
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize { pub unsafe extern "C" fn sp_cp437_grid_width(this: *const CCp437Grid) -> usize {
(*this).width() (*this).0.width()
} }
/// Gets the height of the `ByteGrid` instance. /// Gets the height of the `Cp437Grid` instance.
/// ///
/// # Arguments /// # Arguments
/// ///
@ -180,26 +186,28 @@ pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize {
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_height(this: *const ByteGrid) -> usize { pub unsafe extern "C" fn sp_cp437_grid_height(
(*this).height() this: *const CCp437Grid,
) -> usize {
(*this).0.height()
} }
/// Gets an unsafe reference to the data of the `ByteGrid` instance. /// Gets an unsafe reference to the data of the `Cp437Grid` instance.
/// ///
/// ## Safety /// ## Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `this` points to a valid `ByteGrid` /// - `this` points to a valid `Cp437Grid`
/// - the returned memory range is never accessed after the passed `ByteGrid` has been freed /// - the returned memory range is never accessed after the passed `Cp437Grid` has been freed
/// - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly /// - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_unsafe_data_ref( pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref(
this: *mut ByteGrid, this: *mut CCp437Grid,
) -> CByteSlice { ) -> CByteSlice {
let data = (*this).data_ref_mut(); let data = (*this).0.data_ref_mut();
CByteSlice { CByteSlice {
start: data.as_mut_ptr_range().start, start: data.as_mut_ptr_range().start,
length: data.len(), length: data.len(),

View file

@ -9,7 +9,7 @@ pub use crate::c_slice::CByteSlice;
pub mod bit_vec; pub mod bit_vec;
pub mod byte_grid; pub mod brightness_grid;
pub mod command; pub mod command;
@ -19,7 +19,9 @@ pub mod packet;
pub mod pixel_grid; pub mod pixel_grid;
pub mod c_slice;
pub mod cp437_grid;
/// The minimum time needed for the display to refresh the screen in ms. /// The minimum time needed for the display to refresh the screen in ms.
pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32; pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32;
pub mod c_slice;

View file

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

View file

@ -61,45 +61,85 @@ namespace ServicePoint.BindGen
[DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp_bit_vec_unsafe_data_ref(CBitVec* @this); public static extern CByteSlice sp_bit_vec_unsafe_data_ref(CBitVec* @this);
/// <summary>Creates a new `ByteGrid` with the specified dimensions. returns: `ByteGrid` initialized to 0. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`.</summary> /// <summary>Creates a new `BrightnessGrid` with the specified dimensions. returns: `BrightnessGrid` initialized to 0. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_brightness_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp_byte_grid_new(nuint width, nuint height); public static extern CBrightnessGrid* sp_brightness_grid_new(nuint width, nuint height);
/// <summary>Loads a `ByteGrid` with the specified dimensions from the provided data. # Panics When the provided `data_length` is not sufficient for the `height` and `width` # Safety The caller has to make sure that: - `data` points to a valid memory location of at least `data_length` bytes in size. - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`.</summary> /// <summary>Loads a `BrightnessGrid` with the specified dimensions from the provided data. # Panics When the provided `data_length` is not sufficient for the `height` and `width` # Safety The caller has to make sure that: - `data` points to a valid memory location of at least `data_length` bytes in size. - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_brightness_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp_byte_grid_load(nuint width, nuint height, byte* data, nuint data_length); public static extern CBrightnessGrid* sp_brightness_grid_load(nuint width, nuint height, byte* data, nuint data_length);
/// <summary>Clones a `ByteGrid`. # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`.</summary> /// <summary>Clones a `BrightnessGrid`. # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_brightness_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp_byte_grid_clone(ByteGrid* @this); public static extern CBrightnessGrid* sp_brightness_grid_clone(CBrightnessGrid* @this);
/// <summary>Deallocates a `ByteGrid`. # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary> /// <summary>Deallocates a `BrightnessGrid`. # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_byte_grid_dealloc(ByteGrid* @this); public static extern void sp_brightness_grid_dealloc(CBrightnessGrid* @this);
/// <summary>Gets the current value at the specified position. # Arguments * `this`: instance to read from * `x` and `y`: position of the cell to read # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid` - `this` is not written to concurrently</summary> /// <summary>Gets the current value at the specified position. # Arguments * `this`: instance to read from * `x` and `y`: position of the cell to read # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid` - `this` is not written to concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern byte sp_byte_grid_get(ByteGrid* @this, nuint x, nuint y); public static extern byte sp_brightness_grid_get(CBrightnessGrid* @this, nuint x, nuint y);
/// <summary>Sets the value of the specified position in the `ByteGrid`. # Arguments * `this`: instance to write to * `x` and `y`: position of the cell * `value`: the value to write to the cell returns: old value of the cell # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary> /// <summary>Sets the value of the specified position in the `BrightnessGrid`. # Arguments * `this`: instance to write to * `x` and `y`: position of the cell * `value`: the value to write to the cell returns: old value of the cell # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_byte_grid_set(ByteGrid* @this, nuint x, nuint y, byte value); public static extern void sp_brightness_grid_set(CBrightnessGrid* @this, nuint x, nuint y, byte value);
/// <summary>Sets the value of all cells in the `ByteGrid`. # Arguments * `this`: instance to write to * `value`: the value to set all cells to # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid` - `this` is not written to or read from concurrently</summary> /// <summary>Sets the value of all cells in the `BrightnessGrid`. # Arguments * `this`: instance to write to * `value`: the value to set all cells to # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid` - `this` is not written to or read from concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_byte_grid_fill(ByteGrid* @this, byte value); public static extern void sp_brightness_grid_fill(CBrightnessGrid* @this, byte value);
/// <summary>Gets the width of the `ByteGrid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid`</summary> /// <summary>Gets the width of the `BrightnessGrid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid`</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp_byte_grid_width(ByteGrid* @this); public static extern nuint sp_brightness_grid_width(CBrightnessGrid* @this);
/// <summary>Gets the height of the `ByteGrid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `ByteGrid`</summary> /// <summary>Gets the height of the `BrightnessGrid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid`</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp_byte_grid_height(ByteGrid* @this); public static extern nuint sp_brightness_grid_height(CBrightnessGrid* @this);
/// <summary>Gets an unsafe reference to the data of the `ByteGrid` instance. ## Safety The caller has to make sure that: - `this` points to a valid `ByteGrid` - the returned memory range is never accessed after the passed `ByteGrid` has been freed - the returned memory range is never accessed concurrently, either via the `ByteGrid` or directly</summary> /// <summary>Gets an unsafe reference to the data of the `BrightnessGrid` instance. ## Safety The caller has to make sure that: - `this` points to a valid `BrightnessGrid` - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly</summary>
[DllImport(__DllName, EntryPoint = "sp_byte_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_brightness_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp_byte_grid_unsafe_data_ref(ByteGrid* @this); public static extern CByteSlice sp_brightness_grid_unsafe_data_ref(CBrightnessGrid* @this);
/// <summary>Creates a new `Cp437Grid` with the specified dimensions. returns: `Cp437Grid` initialized to 0. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_cp437_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CCp437Grid* sp_cp437_grid_new(nuint width, nuint height);
/// <summary>Loads a `Cp437Grid` with the specified dimensions from the provided data. # Panics When the provided `data_length` is not sufficient for the `height` and `width` # Safety The caller has to make sure that: - `data` points to a valid memory location of at least `data_length` bytes in size. - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_cp437_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CCp437Grid* sp_cp437_grid_load(nuint width, nuint height, byte* data, nuint data_length);
/// <summary>Clones a `Cp437Grid`. # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_cp437_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CCp437Grid* sp_cp437_grid_clone(CCp437Grid* @this);
/// <summary>Deallocates a `Cp437Grid`. # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_cp437_grid_dealloc(CCp437Grid* @this);
/// <summary>Gets the current value at the specified position. # Arguments * `this`: instance to read from * `x` and `y`: position of the cell to read # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid` - `this` is not written to concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern byte sp_cp437_grid_get(CCp437Grid* @this, nuint x, nuint y);
/// <summary>Sets the value of the specified position in the `Cp437Grid`. # Arguments * `this`: instance to write to * `x` and `y`: position of the cell * `value`: the value to write to the cell returns: old value of the cell # Panics When accessing `x` or `y` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_cp437_grid_set(CCp437Grid* @this, nuint x, nuint y, byte value);
/// <summary>Sets the value of all cells in the `Cp437Grid`. # Arguments * `this`: instance to write to * `value`: the value to set all cells to # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid` - `this` is not written to or read from concurrently</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_cp437_grid_fill(CCp437Grid* @this, byte value);
/// <summary>Gets the width of the `Cp437Grid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid`</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp_cp437_grid_width(CCp437Grid* @this);
/// <summary>Gets the height of the `Cp437Grid` instance. # Arguments * `this`: instance to read from # Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid`</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp_cp437_grid_height(CCp437Grid* @this);
/// <summary>Gets an unsafe reference to the data of the `Cp437Grid` instance. ## Safety The caller has to make sure that: - `this` points to a valid `Cp437Grid` - the returned memory range is never accessed after the passed `Cp437Grid` has been freed - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly</summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp_cp437_grid_unsafe_data_ref(CCp437Grid* @this);
/// <summary>Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. Returns: pointer to new `Command` instance or NULL # Safety The caller has to make sure that: - `packet` points to a valid instance of `Packet` - `packet` is not used concurrently or after this call - the result is checked for NULL - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. Returns: pointer to new `Command` instance or NULL # Safety The caller has to make sure that: - `packet` points to a valid instance of `Packet` - `packet` is not used concurrently or after this call - the result is checked for NULL - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -121,13 +161,13 @@ namespace ServicePoint.BindGen
[DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_fade_out(); public static extern Command* sp_command_fade_out();
/// <summary>Allocates a new `Command::Brightness` instance. # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the same value. # Panics - When the provided brightness value is out of range (0-11). # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_brightness(byte brightness); public static extern Command* sp_command_brightness(byte brightness);
/// <summary>Allocates a new `Command::CharBrightness` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::CharBrightness` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_char_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_char_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_char_brightness(nuint x, nuint y, ByteGrid* byte_grid); public static extern Command* sp_command_char_brightness(nuint x, nuint y, CBrightnessGrid* byte_grid);
/// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -147,7 +187,7 @@ namespace ServicePoint.BindGen
/// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_cp437_data(nuint x, nuint y, ByteGrid* byte_grid); public static extern Command* sp_command_cp437_data(nuint x, nuint y, CCp437Grid* byte_grid);
/// <summary>Allocates a new `Command::BitmapLinearWin` instance. The passed `PixelGrid` gets consumed. # Safety The caller has to make sure that: - `pixel_grid` points to a valid instance of `PixelGrid` - `pixel_grid` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::BitmapLinearWin` instance. The passed `PixelGrid` gets consumed. # Safety The caller has to make sure that: - `pixel_grid` points to a valid instance of `PixelGrid` - `pixel_grid` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_win", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_win", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -231,6 +271,16 @@ namespace ServicePoint.BindGen
{ {
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CBrightnessGrid
{
}
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CCp437Grid
{
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CByteSlice public unsafe partial struct CByteSlice
{ {
@ -238,11 +288,6 @@ namespace ServicePoint.BindGen
public nuint length; public nuint length;
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct ByteGrid
{
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct Connection public unsafe partial struct Connection
{ {
@ -262,17 +307,17 @@ namespace ServicePoint.BindGen
public enum Command public enum Command
{ {
Clear, Clear,
HardReset, Cp437Data,
FadeOut, BitmapLinearWin,
CharBrightness,
Brightness, Brightness,
BitmapLegacy, CharBrightness,
BitmapLinear, BitmapLinear,
BitmapLinearAnd, BitmapLinearAnd,
BitmapLinearOr, BitmapLinearOr,
BitmapLinearXor, BitmapLinearXor,
Cp437Data, HardReset,
BitmapLinearWin, FadeOut,
BitmapLegacy,
} }
public enum CompressionCode : ushort public enum CompressionCode : ushort

View file

@ -0,0 +1,103 @@
using ServicePoint.BindGen;
namespace ServicePoint;
public sealed class BrightnessGrid : SpNativeInstance<BindGen.CBrightnessGrid>
{
public static BrightnessGrid New(int width, int height)
{
unsafe
{
return new BrightnessGrid(NativeMethods.sp_brightness_grid_new((nuint)width, (nuint)height));
}
}
public static BrightnessGrid Load(int width, int height, Span<byte> bytes)
{
unsafe
{
fixed (byte* bytesPtr = bytes)
{
return new BrightnessGrid(NativeMethods.sp_brightness_grid_load((nuint)width, (nuint)height, bytesPtr,
(nuint)bytes.Length));
}
}
}
public BrightnessGrid Clone()
{
unsafe
{
return new BrightnessGrid(NativeMethods.sp_brightness_grid_clone(Instance));
}
}
public byte this[int x, int y]
{
get
{
unsafe
{
return NativeMethods.sp_brightness_grid_get(Instance, (nuint)x, (nuint)y);
}
}
set
{
unsafe
{
NativeMethods.sp_brightness_grid_set(Instance, (nuint)x, (nuint)y, value);
}
}
}
public void Fill(byte value)
{
unsafe
{
NativeMethods.sp_brightness_grid_fill(Instance, value);
}
}
public int Width
{
get
{
unsafe
{
return (int)NativeMethods.sp_brightness_grid_width(Instance);
}
}
}
public int Height
{
get
{
unsafe
{
return (int)NativeMethods.sp_brightness_grid_height(Instance);
}
}
}
public Span<byte> Data
{
get
{
unsafe
{
var slice = NativeMethods.sp_brightness_grid_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length);
}
}
}
private unsafe BrightnessGrid(BindGen.CBrightnessGrid* instance) : base(instance)
{
}
private protected override unsafe void Dealloc()
{
NativeMethods.sp_brightness_grid_dealloc(Instance);
}
}

View file

@ -61,7 +61,7 @@ public sealed class Command : SpNativeInstance<BindGen.Command>
} }
} }
public static Command CharBrightness(int x, int y, ByteGrid grid) public static Command CharBrightness(int x, int y, BrightnessGrid grid)
{ {
unsafe unsafe
{ {
@ -113,7 +113,7 @@ public sealed class Command : SpNativeInstance<BindGen.Command>
} }
} }
public static Command Cp437Data(int x, int y, ByteGrid byteGrid) public static Command Cp437Data(int x, int y, Cp437Grid byteGrid)
{ {
unsafe unsafe
{ {

View file

@ -3,33 +3,33 @@ using ServicePoint.BindGen;
namespace ServicePoint; namespace ServicePoint;
public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid> public sealed class Cp437Grid : SpNativeInstance<BindGen.CCp437Grid>
{ {
public static ByteGrid New(int width, int height) public static Cp437Grid New(int width, int height)
{ {
unsafe unsafe
{ {
return new ByteGrid(NativeMethods.sp_byte_grid_new((nuint)width, (nuint)height)); return new Cp437Grid(NativeMethods.sp_cp437_grid_new((nuint)width, (nuint)height));
} }
} }
public static ByteGrid Load(int width, int height, Span<byte> bytes) public static Cp437Grid Load(int width, int height, Span<byte> bytes)
{ {
unsafe unsafe
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new ByteGrid(NativeMethods.sp_byte_grid_load((nuint)width, (nuint)height, bytesPtr, return new Cp437Grid(NativeMethods.sp_cp437_grid_load((nuint)width, (nuint)height, bytesPtr,
(nuint)bytes.Length)); (nuint)bytes.Length));
} }
} }
} }
public ByteGrid Clone() public Cp437Grid Clone()
{ {
unsafe unsafe
{ {
return new ByteGrid(NativeMethods.sp_byte_grid_clone(Instance)); return new Cp437Grid(NativeMethods.sp_cp437_grid_clone(Instance));
} }
} }
@ -39,14 +39,14 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return NativeMethods.sp_byte_grid_get(Instance, (nuint)x, (nuint)y); return NativeMethods.sp_cp437_grid_get(Instance, (nuint)x, (nuint)y);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp_byte_grid_set(Instance, (nuint)x, (nuint)y, value); NativeMethods.sp_cp437_grid_set(Instance, (nuint)x, (nuint)y, value);
} }
} }
} }
@ -85,7 +85,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
NativeMethods.sp_byte_grid_fill(Instance, value); NativeMethods.sp_cp437_grid_fill(Instance, value);
} }
} }
@ -95,7 +95,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp_byte_grid_width(Instance); return (int)NativeMethods.sp_cp437_grid_width(Instance);
} }
} }
} }
@ -106,7 +106,7 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp_byte_grid_height(Instance); return (int)NativeMethods.sp_cp437_grid_height(Instance);
} }
} }
} }
@ -117,18 +117,18 @@ public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp_byte_grid_unsafe_data_ref(Instance); var slice = NativeMethods.sp_cp437_grid_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
} }
private unsafe ByteGrid(BindGen.ByteGrid* instance) : base(instance) private unsafe Cp437Grid(BindGen.CCp437Grid* instance) : base(instance)
{ {
} }
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp_byte_grid_dealloc(Instance); NativeMethods.sp_cp437_grid_dealloc(Instance);
} }
} }

View file

@ -7,12 +7,11 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck> <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>ServicePoint</PackageId> <PackageId>ServicePoint</PackageId>
<Version>0.6.0</Version> <Version>0.7.0</Version>
<Authors>Repository Authors</Authors> <Authors>Repository Authors</Authors>
<Company>None</Company> <Company>None</Company>
<Product>ServicePoint</Product> <Product>ServicePoint</Product>
@ -35,7 +34,7 @@
<Exec Command="cargo build"/> <Exec Command="cargo build"/>
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/> <Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/>
</Target> </Target>
<!-- include native binary in output --> <!-- include native binary in output -->
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always"> <Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">

View file

@ -5,14 +5,14 @@ fn main() {
println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=build.rs");
csbindgen::Builder::default() csbindgen::Builder::default()
.input_extern_file("../servicepoint_binding_c/src/bit_vec.rs") .input_extern_file("../servicepoint_binding_c/src/bit_vec.rs")
.input_extern_file("../servicepoint_binding_c/src/byte_grid.rs") .input_extern_file("../servicepoint_binding_c/src/brightness_grid.rs")
.input_extern_file("../servicepoint_binding_c/src/cp437_grid.rs")
.input_extern_file("../servicepoint_binding_c/src/command.rs") .input_extern_file("../servicepoint_binding_c/src/command.rs")
.input_extern_file("../servicepoint_binding_c/src/connection.rs") .input_extern_file("../servicepoint_binding_c/src/connection.rs")
.input_extern_file("../servicepoint_binding_c/src/pixel_grid.rs") .input_extern_file("../servicepoint_binding_c/src/pixel_grid.rs")
.input_extern_file("../servicepoint_binding_c/src/lib.rs") .input_extern_file("../servicepoint_binding_c/src/lib.rs")
.input_extern_file("../servicepoint_binding_c/src/c_slice.rs") .input_extern_file("../servicepoint_binding_c/src/c_slice.rs")
.input_extern_file("../servicepoint_binding_c/src/packet.rs") .input_extern_file("../servicepoint_binding_c/src/packet.rs")
.input_extern_file("../servicepoint/src/byte_grid.rs")
.input_extern_file("../servicepoint/src/command.rs") .input_extern_file("../servicepoint/src/command.rs")
.input_extern_file("../servicepoint/src/connection.rs") .input_extern_file("../servicepoint/src/connection.rs")
.input_extern_file("../servicepoint/src/pixel_grid.rs") .input_extern_file("../servicepoint/src/pixel_grid.rs")

View file

@ -1,11 +1,16 @@
{pkgs ? import <nixpkgs> {}}: {pkgs ? import <nixpkgs> {}}:
pkgs.mkShell { pkgs.mkShell {
nativeBuildInputs = with pkgs.buildPackages; [ nativeBuildInputs = with pkgs.buildPackages; [
rustup rustc cargo gcc rustfmt clippy
pkg-config pkg-config
xe xe
lzma lzma
cargo-tarpaulin cargo-tarpaulin
gnumake gnumake
# dotnet-sdk_8
]; ];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
} }