mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 02:00:12 +01:00
Merge pull request #13 from cccb/fonts
More CP437, PrimitiveGrid::map, renamings
This commit is contained in:
commit
30d74ff07d
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -6,7 +6,7 @@ members = [
|
|||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
|
||||
[workspace.lints.rust]
|
||||
missing-docs = "warn"
|
||||
|
|
|
@ -17,7 +17,7 @@ cargo add servicepoint
|
|||
or
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = "0.9.1"
|
||||
servicepoint = "0.10.0"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 là, sur cette île bizarroïde où 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
///
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
|
281
crates/servicepoint_binding_c/src/bitmap.rs
Normal file
281
crates/servicepoint_binding_c/src/bitmap.rs
Normal 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(),
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue