Merge branch 'uniffi-prerequisites' into 'main'

see https://github.com/cccb/servicepoint/pull/19/
This commit is contained in:
Vinzenz Schroeter 2024-11-13 18:57:08 +01:00
commit 11ec30ca74
25 changed files with 496 additions and 276 deletions

103
Cargo.lock generated
View file

@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.15"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@ -34,36 +34,36 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -101,9 +101,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.2"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
[[package]]
name = "bzip2"
@ -147,9 +147,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.30"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [
"jobserver",
"libc",
@ -204,15 +204,15 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colorchoice"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "cpufeatures"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
dependencies = [
"libc",
]
@ -280,9 +280,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
[[package]]
name = "flate2"
@ -329,9 +329,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
[[package]]
name = "heck"
@ -403,9 +403,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.159"
version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
name = "linux-raw-sys"
@ -457,9 +457,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.87"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@ -511,9 +511,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@ -523,9 +523,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@ -550,9 +550,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.37"
version = "0.38.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
dependencies = [
"bitflags",
"errno",
@ -569,18 +569,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
@ -589,9 +589,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.128"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"itoa",
"memchr",
@ -610,7 +610,7 @@ dependencies = [
[[package]]
name = "servicepoint"
version = "0.10.0"
version = "0.11.0"
dependencies = [
"bitvec",
"bzip2",
@ -620,13 +620,14 @@ dependencies = [
"once_cell",
"rand",
"rust-lzma",
"thiserror",
"tungstenite",
"zstd",
]
[[package]]
name = "servicepoint_binding_c"
version = "0.10.0"
version = "0.11.0"
dependencies = [
"cbindgen",
"servicepoint",
@ -634,7 +635,7 @@ dependencies = [
[[package]]
name = "servicepoint_binding_cs"
version = "0.10.0"
version = "0.11.0"
dependencies = [
"csbindgen",
"servicepoint",
@ -666,9 +667,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.79"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
@ -683,9 +684,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.13.0"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
dependencies = [
"cfg-if",
"fastrand",
@ -696,18 +697,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",

View file

@ -8,7 +8,10 @@ members = [
]
[workspace.package]
version = "0.10.0"
version = "0.11.0"
[workspace.lints.rust]
missing-docs = "warn"
[workspace.dependencies]
thiserror = "1.0.69"

View file

@ -22,6 +22,7 @@ rust-lzma = { version = "0.6.0", optional = true }
rand = { version = "0.8", optional = true }
tungstenite = { version = "0.24.0", optional = true }
once_cell = { version = "1.20.2", optional = true }
thiserror.workspace = true
[features]
default = ["compression_lzma", "protocol_udp", "cp437"]

View file

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

View file

@ -42,10 +42,10 @@ fn main() {
}
let text = cli.text.join("\n");
let grid = CharGrid::from(&*text);
let cp437_grid = Cp437Grid::from(&grid);
let grid = CharGrid::from(text);
let grid = Cp437Grid::from(grid);
connection
.send(Command::Cp437Data(Origin::ZERO, cp437_grid))
.send(Command::Cp437Data(Origin::ZERO, grid))
.expect("sending text failed");
}

View file

@ -5,20 +5,17 @@ use servicepoint::{
};
fn main() {
// make connection mut
let mut connection =
let connection =
Connection::open_websocket("ws://localhost:8080".parse().unwrap())
.unwrap();
// use send_mut instead of send
connection.send_mut(Command::Clear).unwrap();
connection.send(Command::Clear).unwrap();
let mut pixels = Bitmap::max_sized();
pixels.fill(true);
// use send_mut instead of send
connection
.send_mut(Command::BitmapLinearWin(
.send(Command::BitmapLinearWin(
Origin::ZERO,
pixels,
CompressionCode::Lzma,

View file

@ -4,7 +4,7 @@ use std::time::Duration;
use clap::Parser;
use servicepoint::{bitvec::prelude::BitVec, *};
use servicepoint::*;
#[derive(Parser, Debug)]
struct Cli {
@ -33,12 +33,8 @@ fn main() {
enabled_pixels.set(x_offset % PIXEL_WIDTH, y, false);
}
// this works because the pixel grid has max size
let pixel_data: Vec<u8> = enabled_pixels.clone().into();
let bit_vec = BitVec::from_vec(pixel_data);
connection
.send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma))
.send(Command::BitmapLinearWin(Origin::ZERO, enabled_pixels.clone(), CompressionCode::Lzma))
.expect("could not send command to display");
thread::sleep(sleep_duration);
}

View file

@ -2,14 +2,14 @@ use bitvec::order::Msb0;
use bitvec::prelude::BitSlice;
use bitvec::slice::IterMut;
use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH};
use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH};
/// A grid of pixels stored in packed bytes.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bitmap {
width: usize,
height: usize,
bit_vec: SpBitVec,
bit_vec: BitVec,
}
impl Bitmap {
@ -175,6 +175,12 @@ impl From<Bitmap> for Vec<u8> {
}
}
impl From<Bitmap> for BitVec {
fn from(value: Bitmap) -> Self {
value.bit_vec
}
}
pub struct IterRows<'t> {
bitmap: &'t Bitmap,
row: usize,

View file

@ -19,7 +19,7 @@ use rand::{
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
/// let result = connection.send(Command::Brightness(b));
/// ```
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub struct Brightness(u8);
/// A grid containing brightness values.
@ -37,8 +37,21 @@ pub struct Brightness(u8);
/// ```
pub type BrightnessGrid = PrimitiveGrid<Brightness>;
impl BrightnessGrid {
/// Like [Self::load], but ignoring any out-of-range brightness values
pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self {
PrimitiveGrid::load(width, height, data).map(Brightness::saturating_from)
}
}
impl From<Brightness> for u8 {
fn from(brightness: Brightness) -> Self {
Self::from(&brightness)
}
}
impl From<&Brightness> for u8 {
fn from(brightness: &Brightness) -> Self {
brightness.0
}
}
@ -105,7 +118,7 @@ impl TryFrom<PrimitiveGrid<u8>> for BrightnessGrid {
let brightnesses = value
.iter()
.map(|b| Brightness::try_from(*b))
.collect::<Result<Vec<Brightness>, _>>()?;
.collect::<Result<Vec<_>, _>>()?;
Ok(BrightnessGrid::load(
value.width(),
value.height(),
@ -129,7 +142,7 @@ mod tests {
#[test]
fn brightness_from_u8() {
assert_eq!(Err(100), Brightness::try_from(100));
assert_eq!(Ok(Brightness(1)), Brightness::try_from(1))
assert_eq!(Ok(Brightness(1)), Brightness::try_from(1));
}
#[test]
@ -155,4 +168,10 @@ mod tests {
assert_eq!(Brightness::MAX, Brightness::saturating_from(100));
assert_eq!(Brightness(5), Brightness::saturating_from(5));
}
#[test]
fn saturating_load() {
assert_eq!(BrightnessGrid::load(2,2, &[Brightness::MAX, Brightness::MAX, Brightness::MIN, Brightness::MAX]),
BrightnessGrid::saturating_load(2,2, &[255u8, 23, 0, 42]));
}
}

View file

@ -0,0 +1,129 @@
use crate::primitive_grid::SeriesError;
use crate::{Grid, PrimitiveGrid};
/// A grid containing UTF-8 characters.
pub type CharGrid = PrimitiveGrid<char>;
impl CharGrid {
/// Copies a column from the grid as a String.
///
/// Returns [None] if x is out of bounds.
pub fn get_col_str(&self, x: usize) -> Option<String> {
Some(String::from_iter(self.get_col(x)?))
}
/// Copies a row from the grid as a String.
///
/// Returns [None] if y is out of bounds.
pub fn get_row_str(&self, y: usize) -> Option<String> {
Some(String::from_iter(self.get_row(y)?))
}
/// Overwrites a row in the grid with a str.
///
/// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
pub fn set_row_str(
&mut self,
y: usize,
value: &str,
) -> Result<(), SeriesError> {
self.set_row(y, value.chars().collect::<Vec<_>>().as_ref())
}
/// Overwrites a column in the grid with a str.
///
/// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
pub fn set_col_str(
&mut self,
x: usize,
value: &str,
) -> Result<(), SeriesError> {
self.set_col(x, value.chars().collect::<Vec<_>>().as_ref())
}
}
impl From<&str> for CharGrid {
fn from(value: &str) -> Self {
let value = value.replace("\r\n", "\n");
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() {
grid.set(x, y, char);
}
}
grid
}
}
impl From<String> for CharGrid {
fn from(value: String) -> Self {
CharGrid::from(&*value)
}
}
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")
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::Grid;
#[test]
fn col_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_col_str(2), None);
assert_eq!(grid.get_col_str(1), Some(String::from("\0\0\0")));
assert_eq!(grid.set_col_str(1, "abc"), Ok(()));
assert_eq!(grid.get_col_str(1), Some(String::from("abc")));
}
#[test]
fn row_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_row_str(3), None);
assert_eq!(grid.get_row_str(1), Some(String::from("\0\0")));
assert_eq!(
grid.set_row_str(1, "abc"),
Err(SeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
assert_eq!(grid.set_row_str(1, "ab"), Ok(()));
assert_eq!(grid.get_row_str(1), Some(String::from("ab")));
}
#[test]
fn str_to_char_grid() {
let original = "Hello\r\nWorld!\n...\n";
let grid = CharGrid::from(original);
assert_eq!(3, grid.height());
let actual = String::from(&grid);
assert_eq!("Hello\nWorld!\n...", actual);
}
}

View file

@ -1,11 +1,9 @@
use bitvec::prelude::BitVec;
use crate::{
command_code::CommandCode,
compression::into_decompressed,
packet::{Header, Packet},
Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin,
Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE,
Pixels, PrimitiveGrid, BitVec, Tiles, TILE_SIZE,
};
/// Type alias for documenting the meaning of the u16 in enum values
@ -144,7 +142,7 @@ pub enum Command {
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
BitmapLinear(Offset, SpBitVec, CompressionCode),
BitmapLinear(Offset, BitVec, CompressionCode),
/// Set pixel data according to an and-mask starting at the offset.
///
@ -152,7 +150,7 @@ pub enum Command {
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
BitmapLinearAnd(Offset, SpBitVec, CompressionCode),
BitmapLinearAnd(Offset, BitVec, CompressionCode),
/// Set pixel data according to an or-mask starting at the offset.
///
@ -160,7 +158,7 @@ pub enum Command {
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
BitmapLinearOr(Offset, SpBitVec, CompressionCode),
BitmapLinearOr(Offset, BitVec, CompressionCode),
/// Set pixel data according to a xor-mask starting at the offset.
///
@ -168,7 +166,7 @@ pub enum Command {
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
BitmapLinearXor(Offset, SpBitVec, CompressionCode),
BitmapLinearXor(Offset, BitVec, CompressionCode),
/// Kills the udp daemon on the display, which usually results in a restart.
///
@ -214,21 +212,27 @@ pub enum Command {
}
/// Err values for [Command::try_from].
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum TryFromPacketError {
/// the contained command code does not correspond to a known command
#[error("The command code {0:?} does not correspond to a known command")]
InvalidCommand(u16),
/// the expected payload size was n, but size m was found
#[error("the expected payload size was {0}, but size {1} was found")]
UnexpectedPayloadSize(usize, usize),
/// Header fields not needed for the command have been used.
///
/// Note that these commands would usually still work on the actual display.
#[error("Header fields not needed for the command have been used")]
ExtraneousHeaderValues,
/// The contained compression code is not known. This could be of disabled features.
#[error("The compression code {0:?} does not correspond to a known compression algorithm.")]
InvalidCompressionCode(u16),
/// Decompression of the payload failed. This can be caused by corrupted packets.
#[error("The decompression of the payload failed")]
DecompressionFailed,
/// The given brightness value is out of bounds
#[error("The given brightness value {0} is out of bounds.")]
InvalidBrightness(u8),
}
@ -374,7 +378,7 @@ impl Command {
/// Helper method for Packets into `BitmapLinear*`-Commands
fn packet_into_linear_bitmap(
packet: Packet,
) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> {
) -> Result<(BitVec, CompressionCode), TryFromPacketError> {
let Packet {
header:
Header {

View file

@ -1,6 +1,5 @@
use std::fmt::Debug;
use crate::packet::Packet;
use std::fmt::Debug;
/// A connection to the display.
///
@ -35,27 +34,24 @@ pub enum Connection {
/// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay
#[cfg(feature = "protocol_websocket")]
WebSocket(
tungstenite::WebSocket<
tungstenite::stream::MaybeTlsStream<std::net::TcpStream>,
std::sync::Mutex<
tungstenite::WebSocket<
tungstenite::stream::MaybeTlsStream<std::net::TcpStream>,
>,
>,
),
/// A fake connection for testing that does not actually send anything.
///
/// This variant allows immutable send.
Fake,
/// A fake connection for testing that does not actually send anything.
///
/// This variant does not allow immutable send.
FakeMutableSend,
}
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum SendError {
IoError(std::io::Error),
#[error("IO error occurred while sending")]
IoError(#[from] std::io::Error),
#[cfg(feature = "protocol_websocket")]
WebsocketError(tungstenite::Error),
#[error("WebSocket error occurred while sending")]
WebsocketError(#[from] tungstenite::Error),
}
impl Connection {
@ -96,7 +92,7 @@ impl Connection {
/// let uri = "ws://localhost:8080".parse().unwrap();
/// let mut connection = Connection::open_websocket(uri)
/// .expect("could not connect");
/// connection.send_mut(Command::Clear)
/// connection.send(Command::Clear)
/// .expect("send failed");
/// ```
#[cfg(feature = "protocol_websocket")]
@ -111,25 +107,19 @@ impl Connection {
let request = ClientRequestBuilder::new(uri).into_client_request()?;
let (sock, _) = connect(request)?;
Ok(Self::WebSocket(sock))
Ok(Self::WebSocket(std::sync::Mutex::new(
sock,
)))
}
/// Send something packet-like to the display. Usually this is in the form of a Command.
///
/// This variant can only be used for connections that support immutable send, e.g. [Connection::Udp].
///
/// If you want to be able to switch the protocol, you should use [Self::send_mut] instead.
///
/// # Arguments
///
/// - `packet`: the packet-like to send
///
/// returns: true if packet was sent, otherwise false
///
/// # Panics
///
/// If the connection does not support immutable send, e.g. for [Connection::WebSocket].
///
/// # Examples
///
/// ```rust
@ -150,59 +140,17 @@ impl Connection {
.map_err(SendError::IoError)
.map(move |_| ()) // ignore Ok value
}
Connection::Fake => {
let _ = data;
Ok(())
}
#[allow(unreachable_patterns)] // depends on features
_ => {
panic!("Connection {:?} does not support immutable send", self)
}
}
}
/// Send something packet-like to the display. Usually this is in the form of a Command.
///
/// This variant has to be used for connections that do not support immutable send, e.g. [Connection::WebSocket].
///
/// If you want to be able to switch the protocol, you should use this variant.
///
/// # Arguments
///
/// - `packet`: the packet-like to send
///
/// returns: true if packet was sent, otherwise false
///
/// # Examples
///
/// ```rust
/// let mut connection = servicepoint::Connection::FakeMutableSend;
/// // turn off all pixels on display
/// connection.send_mut(servicepoint::Command::Clear)
/// .expect("send failed");
/// ```
pub fn send_mut(
&mut self,
packet: impl Into<Packet>,
) -> Result<(), SendError> {
match self {
#[cfg(feature = "protocol_websocket")]
Connection::WebSocket(socket) => {
let packet = packet.into();
log::debug!("sending {packet:?}");
let data: Vec<u8> = packet.into();
let mut socket = socket.lock().unwrap();
socket
.send(tungstenite::Message::Binary(data))
.map_err(SendError::WebsocketError)
}
Connection::FakeMutableSend => {
let packet = packet.into();
log::debug!("sending {packet:?}");
let data: Vec<u8> = packet.into();
Connection::Fake => {
let _ = data;
Ok(())
}
_ => self.send(packet),
}
}
}
@ -211,7 +159,9 @@ impl Drop for Connection {
fn drop(&mut self) {
#[cfg(feature = "protocol_websocket")]
if let Connection::WebSocket(sock) = self {
_ = sock.close(None);
_ = sock
.try_lock()
.map(move |mut sock| sock.close(None));
}
}
}
@ -227,19 +177,4 @@ mod tests {
let packet = Packet::try_from(data).unwrap();
Connection::Fake.send(packet).unwrap()
}
#[test]
fn send_fake_mutable() {
let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let packet = Packet::try_from(data).unwrap();
Connection::FakeMutableSend.send_mut(packet).unwrap()
}
#[test]
#[should_panic]
fn send_fake_mutable_panic() {
let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let packet = Packet::try_from(data).unwrap();
Connection::FakeMutableSend.send(packet).unwrap()
}
}

View file

@ -10,19 +10,14 @@ use std::collections::HashMap;
/// The encoding is currently not enforced.
pub type Cp437Grid = PrimitiveGrid<u8>;
/// A grid containing UTF-8 characters.
pub type CharGrid = PrimitiveGrid<char>;
/// Errors that can occur when loading CP-437.
#[derive(Debug, PartialEq)]
pub enum Cp437LoadError {
/// Invalid character in input prevented loading
InvalidChar {
/// invalid character is at this position in input
index: usize,
/// the invalid character
char: char,
},
/// The error occurring when loading an invalid character
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("The character {char:?} at position {index} is not a valid CP437 character")]
pub struct InvalidCharError {
/// invalid character is at this position in input
index: usize,
/// the invalid character
char: char,
}
impl Cp437Grid {
@ -36,7 +31,7 @@ impl Cp437Grid {
value: &str,
width: usize,
wrap: bool,
) -> Result<Self, Cp437LoadError> {
) -> Result<Self, InvalidCharError> {
assert!(width > 0);
assert!(!value.is_empty());
@ -46,7 +41,7 @@ impl Cp437Grid {
for (index, char) in value.chars().enumerate() {
if !char.is_ascii() {
return Err(Cp437LoadError::InvalidChar { index, char });
return Err(InvalidCharError { index, char });
}
let is_lf = char == '\n';
@ -92,6 +87,7 @@ pub use feature_cp437::*;
#[cfg(feature = "cp437")]
mod feature_cp437 {
use super::*;
use crate::CharGrid;
/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
///
@ -99,7 +95,7 @@ mod feature_cp437 {
///
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
///
/// Mostly 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]
pub const CP437_TO_UTF8: [char; 256] = [
/* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼',
@ -143,44 +139,9 @@ mod feature_cp437 {
}
}
impl From<&str> for CharGrid {
fn from(value: &str) -> Self {
let value = value.replace("\r\n", "\n");
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() {
grid.set(x, y, char);
}
}
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")
impl From<CharGrid> for Cp437Grid {
fn from(value: CharGrid) -> Self {
Cp437Grid::from(&value)
}
}
@ -236,7 +197,7 @@ mod tests {
#[test]
fn load_ascii_invalid() {
assert_eq!(
Err(Cp437LoadError::InvalidChar {
Err(InvalidCharError {
char: '🥶',
index: 2
}),
@ -249,6 +210,7 @@ mod tests {
#[cfg(feature = "cp437")]
mod tests_feature_cp437 {
use super::*;
use crate::CharGrid;
#[test]
fn round_trip_cp437() {
@ -292,13 +254,4 @@ mod tests_feature_cp437 {
fn convert_invalid() {
assert_eq!(cp437_to_char(char_to_cp437('😜')), '?');
}
#[test]
fn str_to_char_grid() {
let original = "Hello\r\nWorld!\n...\n";
let grid = CharGrid::from(original);
assert_eq!(3, grid.height());
let actual = String::from(&grid);
assert_eq!("Hello\nWorld!\n...", actual);
}
}

View file

@ -38,23 +38,25 @@
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::char_grid::CharGrid;
pub use crate::command::{Command, Offset};
pub use crate::compression_code::CompressionCode;
pub use crate::connection::Connection;
pub use crate::cp437::{CharGrid, Cp437Grid};
pub use crate::cp437::Cp437Grid;
pub use crate::data_ref::DataRef;
pub use crate::grid::Grid;
pub use crate::origin::{Origin, Pixels, Tiles};
pub use crate::primitive_grid::PrimitiveGrid;
pub use crate::primitive_grid::{PrimitiveGrid, SeriesError};
type SpBitVec = BitVec<u8, Msb0>;
/// An alias for the specific type of [bitvec::prelude::BitVec] used.
pub type BitVec = bitvec::prelude::BitVec<u8, bitvec::prelude::Msb0>;
mod bitmap;
mod brightness;
mod char_grid;
mod command;
mod command_code;
mod compression;

View file

@ -13,6 +13,27 @@ pub struct PrimitiveGrid<T: PrimitiveGridType> {
data: Vec<T>,
}
/// Error type for methods that change a whole column or row at once
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum SeriesError {
#[error("The index {index} was out of bounds for size {size}")]
/// The index {index} was out of bounds for size {size}
OutOfBounds {
/// the index where access was tried
index: usize,
/// the size in that direction
size: usize,
},
#[error("The provided series was expected to have a length of {expected}, but was {actual}")]
/// The provided series was expected to have a length of {expected}, but was {actual}
InvalidLength {
/// actual size of the provided series
actual: usize,
/// expected size
expected: usize,
},
}
impl<T: PrimitiveGridType> PrimitiveGrid<T> {
/// Creates a new [PrimitiveGrid] with the specified dimensions.
///
@ -126,6 +147,8 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
/// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
/// let command = Command::CharBrightness(Origin::ZERO, grid);
/// ```
/// [Brightness]: [crate::Brightness]
/// [Command]: [crate::Command]
pub fn map<TConverted, F>(&self, f: F) -> PrimitiveGrid<TConverted>
where
TConverted: PrimitiveGridType,
@ -138,6 +161,81 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
.collect::<Vec<_>>();
PrimitiveGrid::load(self.width(), self.height(), &data)
}
/// Copies a row from the grid.
///
/// Returns [None] if y is out of bounds.
pub fn get_row(&self, y: usize) -> Option<Vec<T>> {
self.data
.chunks_exact(self.width())
.nth(y)
.map(|row| row.to_vec())
}
/// Copies a column from the grid.
///
/// Returns [None] if x is out of bounds.
pub fn get_col(&self, x: usize) -> Option<Vec<T>> {
self.data
.chunks_exact(self.width())
.map(|row| row.get(x).copied())
.collect()
}
/// Overwrites a column in the grid.
///
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
pub fn set_col(&mut self, x: usize, col: &[T]) -> Result<(), SeriesError> {
if col.len() != self.height() {
return Err(SeriesError::InvalidLength {
expected: self.height(),
actual: col.len(),
});
}
let width = self.width();
if self
.data
.chunks_exact_mut(width)
.zip(col.iter())
.map(|(row, column_value)| {
row.get_mut(x).map(move |cell| *cell = *column_value)
})
.all(|cell| cell.is_some())
{
Ok(())
} else {
Err(SeriesError::OutOfBounds {
index: x,
size: width,
})
}
}
/// Overwrites a row in the grid.
///
/// Returns [Err] if y is out of bounds or `row` is not of the correct size.
pub fn set_row(&mut self, y: usize, row: &[T]) -> Result<(), SeriesError> {
let width = self.width();
if row.len() != width {
return Err(SeriesError::InvalidLength {
expected: width,
actual: row.len(),
});
}
let chunk = match self.data.chunks_exact_mut(width).nth(y) {
Some(row) => row,
None => {
return Err(SeriesError::OutOfBounds {
size: self.height(),
index: y,
})
}
};
chunk.copy_from_slice(row);
Ok(())
}
}
impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> {
@ -225,7 +323,7 @@ impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> {
#[cfg(test)]
mod tests {
use crate::{DataRef, Grid, PrimitiveGrid};
use crate::{DataRef, Grid, PrimitiveGrid, SeriesError};
#[test]
fn fill() {
@ -347,4 +445,46 @@ mod tests {
assert_eq!(grid.get_optional(0, 0), Some(5));
assert_eq!(grid.get_optional(0, 8), None);
}
#[test]
fn col() {
let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
assert_eq!(grid.get_col(0), Some(vec![0, 2, 4]));
assert_eq!(grid.get_col(1), Some(vec![1, 3, 5]));
assert_eq!(grid.get_col(2), None);
assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(()));
assert_eq!(
grid.set_col(2, &[5, 7, 9]),
Err(SeriesError::OutOfBounds { size: 2, index: 2 })
);
assert_eq!(
grid.set_col(0, &[5, 7]),
Err(SeriesError::InvalidLength {
expected: 3,
actual: 2
})
);
assert_eq!(grid.get_col(0), Some(vec![5, 7, 9]));
}
#[test]
fn row() {
let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
assert_eq!(grid.get_row(0), Some(vec![0, 1]));
assert_eq!(grid.get_row(2), Some(vec![4, 5]));
assert_eq!(grid.get_row(3), None);
assert_eq!(grid.set_row(0, &[5, 7]), Ok(()));
assert_eq!(grid.get_row(0), Some(vec![5, 7]));
assert_eq!(
grid.set_row(3, &[5, 7]),
Err(SeriesError::OutOfBounds { size: 3, index: 3 })
);
assert_eq!(
grid.set_row(2, &[5, 7, 3]),
Err(SeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
}
}

View file

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

View file

@ -144,6 +144,8 @@ typedef struct SPBrightnessGrid SPBrightnessGrid;
* sp_connection_send_command(connection, sp_command_clear());
* sp_connection_send_command(connection, sp_command_brightness(5));
* ```
*
* [SPConnection]: [crate::SPConnection]
*/
typedef struct SPCommand SPCommand;
@ -266,6 +268,8 @@ void sp_bitmap_fill(SPBitmap *bitmap, bool value);
* - `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]
*
* [SPCommand]: [crate::SPCommand]
*/
void sp_bitmap_free(SPBitmap *bitmap);
@ -479,6 +483,8 @@ void sp_bitvec_fill(SPBitVec *bit_vec, bool value);
* - `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]
*
* [SPCommand]: [crate::SPCommand]
*/
void sp_bitvec_free(SPBitVec *bit_vec);
@ -695,6 +701,8 @@ void sp_brightness_grid_fill(SPBrightnessGrid *brightness_grid, uint8_t value);
* - `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]
*
* [SPCommand]: [crate::SPCommand]
*/
void sp_brightness_grid_free(SPBrightnessGrid *brightness_grid);
@ -805,7 +813,7 @@ SPBrightnessGrid *sp_brightness_grid_new(size_t width,
*
* The caller has to make sure that:
*
* - `brightness_grid` points to a valid [SPBitVec]
* - `brightness_grid` points to a valid [SPBrightnessGrid]
* - `brightness_grid` is not written to or read from concurrently
*/
void sp_brightness_grid_set(SPBrightnessGrid *brightness_grid,
@ -867,7 +875,7 @@ size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid);
*
* The passed [SPBitVec] gets consumed.
*
* Returns: a new [Command::BitmapLinear] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL.
*
* # Panics
*
@ -898,7 +906,7 @@ SPCommand *sp_command_bitmap_linear(size_t offset,
*
* The passed [SPBitVec] gets consumed.
*
* Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL.
*
* # Panics
*
@ -929,7 +937,7 @@ SPCommand *sp_command_bitmap_linear_and(size_t offset,
*
* The passed [SPBitVec] gets consumed.
*
* Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL.
*
* # Panics
*
@ -955,7 +963,7 @@ SPCommand *sp_command_bitmap_linear_or(size_t offset,
*
* The passed [SPBitmap] gets consumed.
*
* Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL.
*
* # Panics
*
@ -987,7 +995,7 @@ SPCommand *sp_command_bitmap_linear_win(size_t x,
*
* The passed [SPBitVec] gets consumed.
*
* Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL.
*
* # Panics
*
@ -1011,7 +1019,7 @@ SPCommand *sp_command_bitmap_linear_xor(size_t offset,
/**
* Set the brightness of all tiles to the same value.
*
* Returns: a new [Command::Brightness] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL.
*
* # Panics
*
@ -1031,7 +1039,7 @@ SPCommand *sp_command_brightness(uint8_t brightness);
*
* The passed [SPBrightnessGrid] gets consumed.
*
* Returns: a new [Command::CharBrightness] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL.
*
* # Panics
*
@ -1055,7 +1063,7 @@ SPCommand *sp_command_char_brightness(size_t x,
*
* Does not affect brightness.
*
* Returns: a new [Command::Clear] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL.
*
* # Examples
*
@ -1097,7 +1105,7 @@ SPCommand *sp_command_clone(const SPCommand *command);
*
* The passed [SPCp437Grid] gets consumed.
*
* Returns: a new [Command::Cp437Data] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL.
*
* # Panics
*
@ -1119,7 +1127,7 @@ SPCommand *sp_command_cp437_data(size_t x,
/**
* A yet-to-be-tested command.
*
* Returns: a new `Command::FadeOut` instance. Will never return NULL.
* Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL.
*
* # Safety
*
@ -1159,7 +1167,7 @@ void sp_command_free(SPCommand *command);
*
* Please do not send this in your normal program flow.
*
* Returns: a new [Command::HardReset] instance. Will never return NULL.
* Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL.
*
* # Safety
*
@ -1328,6 +1336,8 @@ void sp_cp437_grid_fill(SPCp437Grid *cp437_grid, uint8_t value);
* - `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]
*
* [SPCommand]: [crate::SPCommand]
*/
void sp_cp437_grid_free(SPCp437Grid *cp437_grid);
@ -1433,6 +1443,8 @@ SPCp437Grid *sp_cp437_grid_new(size_t width,
*
* - `cp437_grid` points to a valid [SPBitVec]
* - `cp437_grid` is not written to or read from concurrently
*
* [SPBitVec]: [crate::SPBitVec]
*/
void sp_cp437_grid_set(SPCp437Grid *cp437_grid,
size_t x,

View file

@ -124,6 +124,8 @@ pub unsafe extern "C" fn sp_bitmap_clone(
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) {
assert!(!bitmap.is_null());

View file

@ -123,6 +123,8 @@ pub unsafe extern "C" fn sp_bitvec_clone(
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_bitvec_free(bit_vec: *mut SPBitVec) {
assert!(!bit_vec.is_null());

View file

@ -133,6 +133,8 @@ pub unsafe extern "C" fn sp_brightness_grid_clone(
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_free(
brightness_grid: *mut SPBrightnessGrid,
@ -191,7 +193,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 [SPBrightnessGrid]
/// - `brightness_grid` is not written to or read from concurrently
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_set(

View file

@ -23,6 +23,8 @@ use crate::{
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_connection_send_command(connection, sp_command_brightness(5));
/// ```
///
/// [SPConnection]: [crate::SPConnection]
pub struct SPCommand(pub(crate) servicepoint::Command);
impl Clone for SPCommand {
@ -90,7 +92,7 @@ pub unsafe extern "C" fn sp_command_clone(
///
/// Does not affect brightness.
///
/// Returns: a new [Command::Clear] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL.
///
/// # Examples
///
@ -114,7 +116,7 @@ pub unsafe extern "C" fn sp_command_clear() -> NonNull<SPCommand> {
///
/// Please do not send this in your normal program flow.
///
/// Returns: a new [Command::HardReset] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL.
///
/// # Safety
///
@ -130,7 +132,7 @@ pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<SPCommand> {
/// A yet-to-be-tested command.
///
/// Returns: a new `Command::FadeOut` instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL.
///
/// # Safety
///
@ -146,7 +148,7 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> {
/// Set the brightness of all tiles to the same value.
///
/// Returns: a new [Command::Brightness] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL.
///
/// # Panics
///
@ -174,7 +176,7 @@ pub unsafe extern "C" fn sp_command_brightness(
///
/// The passed [SPBrightnessGrid] gets consumed.
///
/// Returns: a new [Command::CharBrightness] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL.
///
/// # Panics
///
@ -211,7 +213,7 @@ pub unsafe extern "C" fn sp_command_char_brightness(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinear] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL.
///
/// # Panics
///
@ -254,7 +256,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL.
///
/// # Panics
///
@ -297,7 +299,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL.
///
/// # Panics
///
@ -340,7 +342,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL.
///
/// # Panics
///
@ -378,7 +380,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
///
/// The passed [SPCp437Grid] gets consumed.
///
/// Returns: a new [Command::Cp437Data] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL.
///
/// # Panics
///
@ -410,7 +412,7 @@ pub unsafe extern "C" fn sp_command_cp437_data(
///
/// The passed [SPBitmap] gets consumed.
///
/// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL.
///
/// # Panics
///

View file

@ -117,6 +117,8 @@ pub unsafe extern "C" fn sp_cp437_grid_clone(
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) {
assert!(!cp437_grid.is_null());
@ -172,6 +174,8 @@ pub unsafe extern "C" fn sp_cp437_grid_get(
///
/// - `cp437_grid` points to a valid [SPBitVec]
/// - `cp437_grid` is not written to or read from concurrently
///
/// [SPBitVec]: [crate::SPBitVec]
#[no_mangle]
pub unsafe extern "C" fn sp_cp437_grid_set(
cp437_grid: *mut SPCp437Grid,

View file

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

View file

@ -108,6 +108,8 @@ namespace ServicePoint.BindGen
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_bitmap_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_bitmap_free(Bitmap* bitmap);
@ -321,6 +323,8 @@ namespace ServicePoint.BindGen
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_bitvec_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_bitvec_free(BitVec* bit_vec);
@ -540,6 +544,8 @@ namespace ServicePoint.BindGen
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_brightness_grid_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_brightness_grid_free(BrightnessGrid* brightness_grid);
@ -590,7 +596,7 @@ namespace ServicePoint.BindGen
///
/// The caller has to make sure that:
///
/// - `brightness_grid` points to a valid [SPBitVec]
/// - `brightness_grid` points to a valid [SPBrightnessGrid]
/// - `brightness_grid` is not written to or read from concurrently
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_brightness_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
@ -737,7 +743,7 @@ namespace ServicePoint.BindGen
///
/// Does not affect brightness.
///
/// Returns: a new [Command::Clear] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL.
///
/// # Examples
///
@ -760,7 +766,7 @@ namespace ServicePoint.BindGen
///
/// Please do not send this in your normal program flow.
///
/// Returns: a new [Command::HardReset] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL.
///
/// # Safety
///
@ -775,7 +781,7 @@ namespace ServicePoint.BindGen
/// <summary>
/// A yet-to-be-tested command.
///
/// Returns: a new `Command::FadeOut` instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL.
///
/// # Safety
///
@ -790,7 +796,7 @@ namespace ServicePoint.BindGen
/// <summary>
/// Set the brightness of all tiles to the same value.
///
/// Returns: a new [Command::Brightness] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL.
///
/// # Panics
///
@ -811,7 +817,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBrightnessGrid] gets consumed.
///
/// Returns: a new [Command::CharBrightness] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL.
///
/// # Panics
///
@ -839,7 +845,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinear] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL.
///
/// # Panics
///
@ -869,7 +875,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearAnd] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL.
///
/// # Panics
///
@ -899,7 +905,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearOr] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL.
///
/// # Panics
///
@ -929,7 +935,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBitVec] gets consumed.
///
/// Returns: a new [Command::BitmapLinearXor] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL.
///
/// # Panics
///
@ -954,7 +960,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPCp437Grid] gets consumed.
///
/// Returns: a new [Command::Cp437Data] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL.
///
/// # Panics
///
@ -977,7 +983,7 @@ namespace ServicePoint.BindGen
///
/// The passed [SPBitmap] gets consumed.
///
/// Returns: a new [Command::BitmapLinearWin] instance. Will never return NULL.
/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL.
///
/// # Panics
///
@ -1178,6 +1184,8 @@ namespace ServicePoint.BindGen
/// - `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]
///
/// [SPCommand]: [crate::SPCommand]
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_cp437_grid_free(Cp437Grid* cp437_grid);
@ -1227,6 +1235,8 @@ namespace ServicePoint.BindGen
///
/// - `cp437_grid` points to a valid [SPBitVec]
/// - `cp437_grid` is not written to or read from concurrently
///
/// [SPBitVec]: [crate::SPBitVec]
/// </summary>
[DllImport(__DllName, EntryPoint = "sp_cp437_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp_cp437_grid_set(Cp437Grid* cp437_grid, nuint x, nuint y, byte value);

View file

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