Merge branch 'uniffi-prerequisites' into 'main'
see https://github.com/cccb/servicepoint/pull/19/
This commit is contained in:
		
						commit
						11ec30ca74
					
				
					 25 changed files with 496 additions and 276 deletions
				
			
		
							
								
								
									
										103
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										103
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -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", | ||||
|  |  | |||
|  | @ -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" | ||||
|  | @ -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"] | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ cargo add servicepoint | |||
| or | ||||
| ```toml | ||||
| [dependencies] | ||||
| servicepoint = "0.10.0" | ||||
| servicepoint = "0.11.0" | ||||
| ``` | ||||
| 
 | ||||
| ## Examples | ||||
|  |  | |||
|  | @ -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"); | ||||
| } | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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])); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										129
									
								
								crates/servicepoint/src/char_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								crates/servicepoint/src/char_grid.rs
									
										
									
									
									
										Normal 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); | ||||
|     } | ||||
| } | ||||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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( | ||||
|         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() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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 { | ||||
| /// 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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 | ||||
|             }) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -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( | ||||
|  |  | |||
|  | @ -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
 | ||||
| ///
 | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter