Merge pull request #13 from cccb/fonts

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

10
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@ fn main() {
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 = Bitmap::max_sized();
enabled_pixels.fill(true);
for x_offset in 0..PIXEL_WIDTH {

View file

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

View file

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

View file

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

View file

@ -3,14 +3,14 @@
/// # Examples
///
/// ```rust
/// # use servicepoint::{Command, CompressionCode, Origin, PixelGrid};
/// # use servicepoint::{Command, CompressionCode, Origin, Bitmap};
/// // create command without payload compression
/// # let pixels = PixelGrid::max_sized();
/// _ = Command::BitmapLinearWin(Origin::new(0, 0), pixels, CompressionCode::Uncompressed);
/// # let pixels = Bitmap::max_sized();
/// _ = Command::BitmapLinearWin(Origin::ZERO, 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);
/// # let pixels = Bitmap::max_sized();
/// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Lzma);
/// ```
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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