diff --git a/.envrc.default b/.envrc.default deleted file mode 100644 index 3550a30..0000000 --- a/.envrc.default +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/.gitignore b/.gitignore index f48287d..4d893d8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ out .direnv .envrc result -mutants.* \ No newline at end of file +mutants.* +bin +obj diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..766df6a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "uniffi-bindgen-cs/servicepoint-binding-uniffi"] + path = uniffi-bindgen-cs/servicepoint-binding-uniffi + url = https://git.berlin.ccc.de/servicepoint/servicepoint-binding-uniffi +[submodule "servicepoint-binding-uniffi"] + path = servicepoint-binding-uniffi + url = https://git.berlin.ccc.de/servicepoint/servicepoint-binding-uniffi diff --git a/Cargo.lock b/Cargo.lock index 38b99d0..6b420cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,55 +8,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" -dependencies = [ - "anstyle", - "windows-sys", -] - [[package]] name = "anyhow" version = "1.0.95" @@ -140,7 +91,7 @@ dependencies = [ "quote", "serde", "syn 1.0.109", - "toml 0.5.11", + "toml", ] [[package]] @@ -184,12 +135,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" - [[package]] name = "bitvec" version = "1.0.1" @@ -202,21 +147,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.9.0" @@ -225,19 +155,18 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58" +checksum = "75b89e7c29231c673a61a46e722602bcd138298f6b9e81e71119693534585f5c" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.12+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "72ebc2f1a417f01e1da30ef264ee86ae31d2dcd2d603ea283d3c244a883ca2a9" dependencies = [ "cc", "libc", @@ -276,30 +205,11 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "cbindgen" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" -dependencies = [ - "clap 4.5.26", - "heck 0.4.1", - "indexmap 2.7.0", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 2.0.96", - "tempfile", - "toml 0.8.19", -] - [[package]] name = "cc" -version = "1.2.9" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "jobserver", "libc", @@ -319,63 +229,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags 1.3.2", - "clap_derive 3.2.25", - "clap_lex 0.2.4", - "indexmap 1.9.3", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", "textwrap", ] -[[package]] -name = "clap" -version = "4.5.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" -dependencies = [ - "clap_builder", - "clap_derive 4.5.24", -] - -[[package]] -name = "clap_builder" -version = "4.5.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" -dependencies = [ - "anstream", - "anstyle", - "clap_lex 0.7.4", - "strsim 0.11.1", -] - [[package]] name = "clap_derive" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "clap_derive" -version = "4.5.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.96", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -385,27 +261,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "cpufeatures" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.4.2" @@ -415,48 +270,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "extend" version = "1.2.0" @@ -468,12 +281,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "flate2" version = "1.0.35" @@ -484,12 +291,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "fs-err" version = "2.11.0" @@ -505,27 +306,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "glob" version = "0.3.2" @@ -549,24 +329,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -576,23 +344,6 @@ dependencies = [ "libc", ] -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - [[package]] name = "indexmap" version = "1.9.3" @@ -600,25 +351,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", ] -[[package]] -name = "indexmap" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" -dependencies = [ - "equivalent", - "hashbrown 0.15.2", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itoa" version = "1.0.14" @@ -634,26 +369,12 @@ dependencies = [ "libc", ] -[[package]] -name = "lang_c" -version = "0.1.0" -dependencies = [ - "cc", - "servicepoint_binding_c", -] - [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "log" version = "0.4.22" @@ -690,9 +411,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] @@ -743,15 +464,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -800,36 +512,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "rust-lzma" version = "0.6.0" @@ -840,19 +522,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "rustix" -version = "0.38.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" -dependencies = [ - "bitflags 2.7.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "ryu" version = "1.0.18" @@ -920,40 +589,22 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "servicepoint" version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b52049be55a15fe37c13249d7f96aa8a5de56e1a41838e74a822ee8316a0c4" dependencies = [ "bitvec", "bzip2", - "clap 4.5.26", "flate2", "log", "once_cell", - "rand", "rust-lzma", "thiserror 2.0.11", - "tungstenite", "zstd", ] -[[package]] -name = "servicepoint_binding_c" -version = "0.13.1" -dependencies = [ - "cbindgen", - "servicepoint", -] - [[package]] name = "servicepoint_binding_uniffi" version = "0.13.1" @@ -961,21 +612,10 @@ dependencies = [ "servicepoint", "thiserror 2.0.11", "uniffi", - "uniffi-bindgen-cs", + "uniffi-bindgen-cs 0.8.3+v0.25.0", "uniffi-bindgen-go", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1006,12 +646,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "syn" version = "1.0.109" @@ -1040,20 +674,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tempfile" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" -dependencies = [ - "cfg-if", - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -1123,64 +743,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap 2.7.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tungstenite" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror 2.0.11", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicase" version = "2.8.1" @@ -1225,18 +787,25 @@ dependencies = [ "anyhow", "askama 0.11.1", "camino", - "clap 3.2.25", + "clap", "extend", "fs-err", - "heck 0.4.1", + "heck", "paste", "serde", "serde_json", "textwrap", - "toml 0.5.11", + "toml", "uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce)", ] +[[package]] +name = "uniffi-bindgen-cs" +version = "0.13.1" +dependencies = [ + "uniffi-bindgen-cs 0.8.3+v0.25.0", +] + [[package]] name = "uniffi-bindgen-go" version = "0.2.2+v0.25.0" @@ -1246,15 +815,15 @@ dependencies = [ "askama 0.12.1", "camino", "cargo_metadata", - "clap 3.2.25", + "clap", "extend", "fs-err", - "heck 0.4.1", + "heck", "paste", "serde", "serde_json", "textwrap", - "toml 0.5.11", + "toml", "uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", @@ -1272,12 +841,12 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck 0.4.1", + "heck", "once_cell", "paste", "serde", "textwrap", - "toml 0.5.11", + "toml", "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", @@ -1295,12 +864,12 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck 0.4.1", + "heck", "once_cell", "paste", "serde", "textwrap", - "toml 0.5.11", + "toml", "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce)", "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce)", "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce)", @@ -1319,11 +888,11 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck 0.4.1", + "heck", "once_cell", "paste", "serde", - "toml 0.5.11", + "toml", "uniffi_meta 0.25.3", "uniffi_testing 0.25.3", "uniffi_udl 0.25.3", @@ -1398,7 +967,7 @@ dependencies = [ "quote", "serde", "syn 2.0.96", - "toml 0.5.11", + "toml", "uniffi_build", "uniffi_meta 0.25.3", ] @@ -1508,18 +1077,6 @@ dependencies = [ "weedle2 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "vcpkg" version = "0.2.15" @@ -1532,12 +1089,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "weedle2" version = "4.0.0" @@ -1667,15 +1218,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.6.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" -dependencies = [ - "memchr", -] - [[package]] name = "wyz" version = "0.5.1" @@ -1685,27 +1227,6 @@ dependencies = [ "tap", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", -] - [[package]] name = "zstd" version = "0.13.2" diff --git a/Cargo.toml b/Cargo.toml index cef002d..365d2e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,3 @@ [workspace] resolver = "2" -members = [ - "crates/servicepoint", - "crates/servicepoint_binding_c", - "crates/servicepoint_binding_c/examples/lang_c", - "crates/servicepoint_binding_uniffi" -] - -[workspace.package] -version = "0.13.1" - -[workspace.lints.rust] -missing-docs = "warn" - -[workspace.dependencies] -thiserror = "2.0" +members = ["uniffi-bindgen-cs", "servicepoint-binding-uniffi"] \ No newline at end of file diff --git a/README.md b/README.md index 958de0f..27d05ed 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,90 @@ -# servicepoint - -[![crates.io](https://img.shields.io/crates/v/servicepoint.svg)](https://crates.io/crates/servicepoint) -[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint)](https://crates.io/crates/servicepoint) -[![docs.rs](https://img.shields.io/docsrs/servicepoint)](https://docs.rs/servicepoint/latest/servicepoint/) -[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint)](./LICENSE) +# ServicePoint In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point Display" or "Airport Display". -This repository contains a library for parsing, encoding and sending packets to this display via UDP in multiple -programming languages. -This project moved to [git.berlin.ccc.de/servicepoint/servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint). -The [GitHub repository](https://github.com/cccb/servicepoint) remains available as a mirror. +This crate contains bindings for multiple programming languages, enabling non-rust-developers to use the library. -Take a look at the contained crates for language specific information: +Also take a look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for more +information. -| Crate | Languages | Readme | -|-----------------------------|-----------------------------------|-----------------------------------------------------------------------------| -| servicepoint | Rust | [servicepoint](crates/servicepoint/README.md) | -| servicepoint_binding_c | C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) | -| servicepoint_binding_uniffi | C# / Python / Go / Kotlin / Swift | [servicepoint_binding_uniffi](crates/servicepoint_binding_uniffi/README.md) | +## Note on stability -## Projects using the library +This library is still in early development. +You can absolutely use it, and it works, but expect minor breaking changes with every version bump. -- screen simulator (rust): [servicepoint-simulator](https://git.berlin.ccc.de/servicepoint/servicepoint-simulator) -- A bunch of projects (C): [arfst23/ServicePoint](https://github.com/arfst23/ServicePoint), including - - a CLI tool to display image files on the display or use the display as a TTY - - a BSD games robots clone - - a split-flap-display simulator - - animations that play on the display -- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs) -- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life) -- partial typescript implementation inspired by this library and browser stream: [cccb-servicepoint-browser](https://github.com/SamuelScheit/cccb-servicepoint-browser) -- a CLI: [servicepoint-cli](https://git.berlin.ccc.de/servicepoint/servicepoint-cli) +## Notes on differences to rust library -To add yourself to the list, open a pull request. +- Performance will not be as good as the rust version: + - most objects are reference counted. + - objects with mutating methods will also have a MRSW lock +- You will not get rust backtraces in release builds of the native code +- Panic messages will work (PanicException) -You can also check out [awesome-servicepoint](https://github.com/stars/kaesaecracker/lists/awesome-servicepoint) for a bigger collection of projects, including some not related to this library. +## Supported languages -If you have access, there is even more software linked in [the wiki](https://wiki.berlin.ccc.de/LED-Riesendisplay). +| Language | Support level | Notes | +|-----------|---------------|-------------------------------------------------------------------------------------------------| +| .NET (C#) | Full | see dedicated section | +| Ruby | Working | LD_LIBRARY_PATH has to be set, see example project | +| Python | Tested once | Required project file not included. The shared library will be loaded from the script location. | +| Go | untested | | +| Kotlin | untested | | +| Swift | untested | | -## Contributing +## Installation -See [CONTRIBUTING.md](CONTRIBUTING.md). +Including this repository as a submodule and building from source is the recommended way of using the library. -## What happened to servicepoint2? +```bash +git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint.git +git commit -m "add servicepoint submodule" +``` -After `servicepoint2` has been merged into `servicepoint`, `servicepoint2` will not continue to get any updates. +Run `generate-bindings.sh` to regenerate all bindings. This will also build `libservicepoint.so` (or equivalent on your +platform). + +For languages not fully supported, there will be no project file for the library, just the naked source file(s). +If you successfully use a language, please open an issue or PR to add the missing ones. + +## .NET (C#) + +This is the best supported language. + +F# is not tested. If there are usability or functionality problems, please open an issue. + +Currently, the project file is hard-coded for Linux and will need tweaks for other platforms (e.g. `.dylib` instead of `.so`). + +You do not have to compile or copy the rust crate manually, as building `ServicePoint.csproj` also builds it. + +### Example + +```csharp +using System.Threading; +using ServicePoint; + +var connection = new Connection("127.0.0.1:2342"); +connection.Send(Command.Clear()); + +connection.Send(Command.Brightness(5)); + +var pixels = Bitmap.NewMaxSized(); +for (ulong offset = 0; offset < ulong.MaxValue; offset++) +{ + pixels.Fill(false); + + for (ulong y = 0; y < pixels.Height(); y++) + pixels.Set((y + offset) % pixels.Width(), y, true); + + connection.Send(Command.BitmapLinearWin(0, 0, pixels)); + Thread.Sleep(14); +} +``` + +A full example including project files is available as part of this crate. + +### Why is there no NuGet-Package? + +NuGet packages are not a good way to distribute native +binaries ([relevant issue](https://github.com/dotnet/sdk/issues/33845)). +Because of that, there is no NuGet package you can use directly. diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore b/ServicePoint.Example/.gitignore similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore rename to ServicePoint.Example/.gitignore diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs b/ServicePoint.Example/Program.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/Program.cs rename to ServicePoint.Example/Program.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/ServicePoint.Example.csproj b/ServicePoint.Example/ServicePoint.Example.csproj similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/ServicePoint.Example.csproj rename to ServicePoint.Example/ServicePoint.Example.csproj diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/BitmapTests.cs b/ServicePoint.Tests/BitmapTests.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/BitmapTests.cs rename to ServicePoint.Tests/BitmapTests.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CharGridTests.cs b/ServicePoint.Tests/CharGridTests.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CharGridTests.cs rename to ServicePoint.Tests/CharGridTests.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CommandTests.cs b/ServicePoint.Tests/CommandTests.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/CommandTests.cs rename to ServicePoint.Tests/CommandTests.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ConnectionTests.cs b/ServicePoint.Tests/ConnectionTests.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ConnectionTests.cs rename to ServicePoint.Tests/ConnectionTests.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/GlobalUsings.cs b/ServicePoint.Tests/GlobalUsings.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/GlobalUsings.cs rename to ServicePoint.Tests/GlobalUsings.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ServicePoint.Tests.csproj b/ServicePoint.Tests/ServicePoint.Tests.csproj similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Tests/ServicePoint.Tests.csproj rename to ServicePoint.Tests/ServicePoint.Tests.csproj diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/Constants.cs b/ServicePoint/Constants.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/Constants.cs rename to ServicePoint/Constants.cs diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj b/ServicePoint/ServicePoint.csproj similarity index 89% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj rename to ServicePoint/ServicePoint.csproj index 70d12b3..24ca0ef 100644 --- a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/ServicePoint.csproj +++ b/ServicePoint/ServicePoint.csproj @@ -33,12 +33,12 @@ - + libservicepoint_binding_uniffi.so - + libservicepoint_binding_uniffi.so diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs b/ServicePoint/servicepoint_binding_uniffi.cs similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint/servicepoint_binding_uniffi.cs rename to ServicePoint/servicepoint_binding_uniffi.cs diff --git a/crates/servicepoint/Cargo.toml b/crates/servicepoint/Cargo.toml deleted file mode 100644 index 394aec4..0000000 --- a/crates/servicepoint/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "servicepoint" -version.workspace = true -publish = true -edition = "2021" -license = "GPL-3.0-or-later" -description = "A rust library for the CCCB Service Point Display." -homepage = "https://docs.rs/crate/servicepoint" -repository = "https://git.berlin.ccc.de/servicepoint/servicepoint" -readme = "README.md" -keywords = ["cccb", "cccb-servicepoint"] - -[lib] -crate-type = ["rlib"] - -[dependencies] -log = "0.4" -bitvec = "1.0" -flate2 = { version = "1.0", optional = true } -bzip2 = { version = "0.5", optional = true } -zstd = { version = "0.13", optional = true } -rust-lzma = { version = "0.6", optional = true } -rand = { version = "0.8", optional = true } -tungstenite = { version = "0.26", optional = true } -once_cell = { version = "1.20", optional = true } -thiserror.workspace = true - -[features] -default = ["compression_lzma", "protocol_udp", "cp437"] -compression_zlib = ["dep:flate2"] -compression_bzip2 = ["dep:bzip2"] -compression_lzma = ["dep:rust-lzma"] -compression_zstd = ["dep:zstd"] -all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] -rand = ["dep:rand"] -protocol_udp = [] -protocol_websocket = ["dep:tungstenite"] -cp437 = ["dep:once_cell"] - -[[example]] -name = "random_brightness" -required-features = ["rand"] - -[[example]] -name = "game_of_life" -required-features = ["rand"] - -[[example]] -name = "websocket" -required-features = ["protocol_websocket"] - -[dev-dependencies] -# for examples -clap = { version = "4.5", features = ["derive"] } - -[lints] -workspace = true - -[package.metadata.docs.rs] -all-features = true diff --git a/crates/servicepoint/README.md b/crates/servicepoint/README.md deleted file mode 100644 index fe888a3..0000000 --- a/crates/servicepoint/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# servicepoint - -[![crates.io](https://img.shields.io/crates/v/servicepoint.svg)](https://crates.io/crates/servicepoint) -[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint)](https://crates.io/crates/servicepoint) -[![docs.rs](https://img.shields.io/docsrs/servicepoint)](https://docs.rs/servicepoint/latest/servicepoint/) -[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint)](../../LICENSE) - -In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point -Display" or "Airport Display". -This crate contains a library for parsing, encoding and sending packets to this display via UDP. - -## Installation - -```bash -cargo add servicepoint -``` -or -```toml -[dependencies] -servicepoint = "0.13.1" -``` - -## Examples - -```rust no_run -fn main() { - // establish connection - let connection = servicepoint::Connection::open("172.23.42.29:2342") - .expect("connection failed"); - - // clear screen content - connection.send(servicepoint::Command::Clear) - .expect("send failed"); -} -``` - -More examples are available in the crate. -Execute `cargo run --example` for a list of available examples and `cargo run --example ` to run one. - -## Note on stability - -This library can be used for creative project or just to play around with the display. -A decent coverage by unit tests prevents major problems and I also test this with my own projects, which mostly use up-to-date versions. - -That being said, the API is still being worked on. -Expect minor breaking changes with every version bump. -Please specify the full version including patch in your Cargo.toml until 1.0 is released. - -## Features - -This library has multiple optional dependencies. -You can choose to (not) include them by toggling the related features. - -| Name | Default | Description | -|--------------------|---------|--------------------------------------------| -| compression_zlib | false | Enable additional compression algo | -| compression_bzip2 | false | Enable additional compression algo | -| compression_lzma | true | Enable additional compression algo | -| compression_zstd | false | Enable additional compression algo | -| protocol_udp | true | Connection::Udp | -| protocol_websocket | false | Connection::WebSocket | -| rand | false | impl Distribution for Standard | -| cp437 | true | Conversion to and from CP-437 | - -## Everything else - -Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for further information. diff --git a/crates/servicepoint/examples/announce.rs b/crates/servicepoint/examples/announce.rs deleted file mode 100644 index 05d2b19..0000000 --- a/crates/servicepoint/examples/announce.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! An example for how to send text to the display. - -use clap::Parser; -use servicepoint::*; - -#[derive(Parser, Debug)] -struct Cli { - #[arg( - short, - long, - default_value = "localhost:2342", - help = "Address of the display" - )] - destination: String, - #[arg(short, long, num_args = 1.., value_delimiter = '\n', - help = "Text to send - specify multiple times for multiple lines")] - text: Vec, - #[arg( - short, - long, - default_value_t = true, - help = "Clear screen before sending text" - )] - clear: bool, -} - -/// example: `cargo run -- --text "Hallo" --text "CCCB"` -fn main() { - let mut cli = Cli::parse(); - if cli.text.is_empty() { - cli.text.push("Hello, CCCB!".to_string()); - } - - let connection = Connection::open(&cli.destination) - .expect("could not connect to display"); - - if cli.clear { - connection - .send(Command::Clear) - .expect("sending clear failed"); - } - - let text = cli.text.join("\n"); - let grid = CharGrid::wrap_str(TILE_WIDTH, &text); - connection - .send(Command::Utf8Data(Origin::ZERO, grid)) - .expect("sending text failed"); -} diff --git a/crates/servicepoint/examples/brightness_tester.rs b/crates/servicepoint/examples/brightness_tester.rs deleted file mode 100644 index ec78415..0000000 --- a/crates/servicepoint/examples/brightness_tester.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Show a brightness level test pattern on screen - -use clap::Parser; -use servicepoint::*; - -#[derive(Parser, Debug)] -struct Cli { - #[arg(short, long, default_value = "localhost:2342")] - destination: String, -} - -fn main() { - let cli = Cli::parse(); - let connection = Connection::open(cli.destination) - .expect("could not connect to display"); - - let mut pixels = Bitmap::max_sized(); - pixels.fill(true); - - let command = Command::BitmapLinearWin( - Origin::ZERO, - pixels, - CompressionCode::Uncompressed, - ); - connection.send(command).expect("send failed"); - - let max_brightness: u8 = Brightness::MAX.into(); - let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT); - for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() { - let level = index as u8 % max_brightness; - *byte = Brightness::try_from(level).unwrap(); - } - - connection - .send(Command::CharBrightness(Origin::ZERO, brightnesses)) - .expect("send failed"); -} diff --git a/crates/servicepoint/examples/game_of_life.rs b/crates/servicepoint/examples/game_of_life.rs deleted file mode 100644 index ab4f63b..0000000 --- a/crates/servicepoint/examples/game_of_life.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! A simple game of life implementation to show how to render graphics to the display. - -use clap::Parser; -use rand::{distributions, Rng}; -use servicepoint::*; -use std::thread; - -#[derive(Parser, Debug)] -struct Cli { - #[arg(short, long, default_value = "localhost:2342")] - destination: String, - #[arg(short, long, default_value_t = 0.5f64)] - probability: f64, -} - -fn main() { - let cli = Cli::parse(); - - let connection = Connection::open(&cli.destination) - .expect("could not connect to display"); - let mut field = make_random_field(cli.probability); - - loop { - let command = Command::BitmapLinearWin( - Origin::ZERO, - field.clone(), - CompressionCode::Lzma, - ); - connection.send(command).expect("could not send"); - thread::sleep(FRAME_PACING); - field = iteration(field); - } -} - -fn iteration(field: Bitmap) -> Bitmap { - let mut next = field.clone(); - for x in 0..field.width() { - for y in 0..field.height() { - let old_state = field.get(x, y); - let neighbors = count_neighbors(&field, x as i32, y as i32); - - let new_state = matches!( - (old_state, neighbors), - (true, 2) | (true, 3) | (false, 3) - ); - next.set(x, y, new_state); - } - } - next -} - -fn count_neighbors(field: &Bitmap, x: i32, y: i32) -> i32 { - let mut count = 0; - for nx in x - 1..=x + 1 { - for ny in y - 1..=y + 1 { - if nx == x && ny == y { - continue; // the cell itself does not count - } - - if nx < 0 - || ny < 0 - || nx >= field.width() as i32 - || ny >= field.height() as i32 - { - continue; // pixels outside the grid do not count - } - - if !field.get(nx as usize, ny as usize) { - continue; // dead cells do not count - } - - count += 1; - } - } - - count -} - -fn make_random_field(probability: f64) -> Bitmap { - let mut field = Bitmap::max_sized(); - let mut rng = rand::thread_rng(); - let d = distributions::Bernoulli::new(probability).unwrap(); - for x in 0..field.width() { - for y in 0..field.height() { - field.set(x, y, rng.sample(d)); - } - } - field -} diff --git a/crates/servicepoint/examples/moving_line.rs b/crates/servicepoint/examples/moving_line.rs deleted file mode 100644 index 3ebd6b0..0000000 --- a/crates/servicepoint/examples/moving_line.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! A simple example for how to send pixel data to the display. - -use clap::Parser; -use servicepoint::*; -use std::thread; - -#[derive(Parser, Debug)] -struct Cli { - #[arg(short, long, default_value = "localhost:2342")] - destination: String, -} - -fn main() { - let connection = Connection::open(Cli::parse().destination) - .expect("could not connect to display"); - - let mut pixels = Bitmap::max_sized(); - for x_offset in 0..usize::MAX { - pixels.fill(false); - - for y in 0..PIXEL_HEIGHT { - pixels.set((y + x_offset) % PIXEL_WIDTH, y, true); - } - - let command = Command::BitmapLinearWin( - Origin::ZERO, - pixels.clone(), - CompressionCode::Lzma, - ); - connection.send(command).expect("send failed"); - thread::sleep(FRAME_PACING); - } -} diff --git a/crates/servicepoint/examples/random_brightness.rs b/crates/servicepoint/examples/random_brightness.rs deleted file mode 100644 index 0f976c4..0000000 --- a/crates/servicepoint/examples/random_brightness.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! A simple example for how to set brightnesses for tiles on the screen. -//! Continuously changes the tiles in a random window to random brightnesses. - -use clap::Parser; -use rand::Rng; -use servicepoint::*; -use std::time::Duration; - -#[derive(Parser, Debug)] -struct Cli { - #[arg(short, long, default_value = "localhost:2342")] - destination: String, - #[arg(short, long, default_value_t = true)] - enable_all: bool, - #[arg(short, long, default_value_t = 100, allow_negative_numbers = false)] - wait_ms: u64, -} - -fn main() { - let cli = Cli::parse(); - - let connection = Connection::open(cli.destination) - .expect("could not connect to display"); - let wait_duration = Duration::from_millis(cli.wait_ms); - - // put all pixels in on state - if cli.enable_all { - let mut filled_grid = Bitmap::max_sized(); - filled_grid.fill(true); - - let command = Command::BitmapLinearWin( - Origin::ZERO, - filled_grid, - CompressionCode::Lzma, - ); - connection.send(command).expect("send failed"); - } - - // set all pixels to the same random brightness - let mut rng = rand::thread_rng(); - connection.send(Command::Brightness(rng.gen())).unwrap(); - - // continuously update random windows to new random brightness - loop { - let min_size = 1; - let x = rng.gen_range(0..TILE_WIDTH - min_size); - let y = rng.gen_range(0..TILE_HEIGHT - min_size); - - let w = rng.gen_range(min_size..=TILE_WIDTH - x); - let h = rng.gen_range(min_size..=TILE_HEIGHT - y); - - let origin = Origin::new(x, y); - let mut luma = BrightnessGrid::new(w, h); - - for y in 0..h { - for x in 0..w { - luma.set(x, y, rng.gen()); - } - } - - connection - .send(Command::CharBrightness(origin, luma)) - .unwrap(); - std::thread::sleep(wait_duration); - } -} diff --git a/crates/servicepoint/examples/websocket.rs b/crates/servicepoint/examples/websocket.rs deleted file mode 100644 index 207609e..0000000 --- a/crates/servicepoint/examples/websocket.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Example for how to use the WebSocket connection - -use servicepoint::{ - Bitmap, Command, CompressionCode, Connection, Grid, Origin, -}; - -fn main() { - let connection = - Connection::open_websocket("ws://localhost:8080".parse().unwrap()) - .unwrap(); - - connection.send(Command::Clear).unwrap(); - - let mut pixels = Bitmap::max_sized(); - pixels.fill(true); - - connection - .send(Command::BitmapLinearWin( - Origin::ZERO, - pixels, - CompressionCode::Lzma, - )) - .unwrap(); -} diff --git a/crates/servicepoint/examples/wiping_clear.rs b/crates/servicepoint/examples/wiping_clear.rs deleted file mode 100644 index 101a9ae..0000000 --- a/crates/servicepoint/examples/wiping_clear.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! An example on how to modify the image on screen without knowing the current content. -use clap::Parser; -use servicepoint::*; -use std::thread; -use std::time::Duration; - -#[derive(Parser, Debug)] -struct Cli { - #[arg(short, long, default_value = "localhost:2342")] - destination: String, - #[arg(short, long = "duration-ms", default_value_t = 5000)] - time: u64, -} - -fn main() { - let cli = Cli::parse(); - - let sleep_duration = Duration::max( - FRAME_PACING, - Duration::from_millis(cli.time / PIXEL_WIDTH as u64), - ); - - let connection = Connection::open(cli.destination) - .expect("could not connect to display"); - - let mut enabled_pixels = Bitmap::max_sized(); - enabled_pixels.fill(true); - - for x_offset in 0..PIXEL_WIDTH { - for y in 0..PIXEL_HEIGHT { - enabled_pixels.set(x_offset % PIXEL_WIDTH, y, false); - } - - connection - .send(Command::BitmapLinearWin( - Origin::ZERO, - enabled_pixels.clone(), - CompressionCode::Lzma, - )) - .expect("could not send command to display"); - thread::sleep(sleep_duration); - } -} diff --git a/crates/servicepoint/src/bit_vec.rs b/crates/servicepoint/src/bit_vec.rs deleted file mode 100644 index 2ece813..0000000 --- a/crates/servicepoint/src/bit_vec.rs +++ /dev/null @@ -1,10 +0,0 @@ -/// A byte-packed vector of booleans. -/// -/// The implementation is provided by [bitvec]. -/// This is an alias for the specific type of [bitvec::BitVec] used in this crate. -pub type BitVec = bitvec::BitVec; - -pub mod bitvec { - //! Re-export of the used library [mod@bitvec]. - pub use bitvec::prelude::*; -} diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs deleted file mode 100644 index afa42f2..0000000 --- a/crates/servicepoint/src/bitmap.rs +++ /dev/null @@ -1,379 +0,0 @@ -use crate::data_ref::DataRef; -use crate::BitVec; -use crate::*; -use ::bitvec::order::Msb0; -use ::bitvec::prelude::BitSlice; -use ::bitvec::slice::IterMut; - -/// A fixed-size 2D grid of booleans. -/// -/// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels. -/// This means that no conversion is necessary for sending the data to the display. -/// The downside is that the width has to be a multiple of 8. -/// -/// # Examples -/// -/// ```rust -/// use servicepoint::Bitmap; -/// let mut bitmap = Bitmap::new(8, 2); -/// -/// ``` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Bitmap { - width: usize, - height: usize, - bit_vec: BitVec, -} - -impl Bitmap { - /// Creates a new [Bitmap] with the specified dimensions. - /// - /// # Arguments - /// - /// - `width`: size in pixels in x-direction - /// - `height`: size in pixels in y-direction - /// - /// returns: [Bitmap] initialized to all pixels off - /// - /// # Panics - /// - /// - when the width is not dividable by 8 - pub fn new(width: usize, height: usize) -> Self { - assert_eq!( - width % 8, - 0, - "width must be a multiple of 8, but is {width}" - ); - Self { - width, - height, - bit_vec: BitVec::repeat(false, width * height), - } - } - - /// Creates a new pixel grid with the size of the whole screen. - #[must_use] - pub fn max_sized() -> Self { - Self::new(PIXEL_WIDTH, PIXEL_HEIGHT) - } - - /// Loads a [Bitmap] with the specified dimensions from the provided data. - /// - /// # Arguments - /// - /// - `width`: size in pixels in x-direction - /// - `height`: size in pixels in y-direction - /// - /// returns: [Bitmap] that contains a copy of the provided data - /// - /// # Panics - /// - /// - when the dimensions and data size do not match exactly. - /// - when the width is not dividable by 8 - #[must_use] - pub fn load(width: usize, height: usize, data: &[u8]) -> Self { - assert_eq!(width % 8, 0, "width must be a multiple of 8, but is {width}"); - assert_eq!(data.len(), height * width / 8, "data length must match dimensions, with 8 pixels per byte."); - Self { - width, - height, - bit_vec: BitVec::from_slice(data), - } - } - - /// Creates a [Bitmap] with the specified width from the provided [BitVec] without copying it. - /// - /// returns: [Bitmap] that contains the provided data. - /// - /// # Panics - /// - /// - when the bitvec size is not dividable by the provided width - /// - when the width is not dividable by 8 - #[must_use] - pub fn from_bitvec(width: usize, bit_vec: BitVec) -> Self { - assert_eq!(width % 8, 0, "width must be a multiple of 8, but is {width}"); - let len = bit_vec.len(); - let height = len / width; - assert_eq!(0, len % width, "dimension mismatch - len {len} is not dividable by {width}"); - Self { width, height, bit_vec } - } - - /// Iterate over all cells in [Bitmap]. - /// - /// Order is equivalent to the following loop: - /// ``` - /// # use servicepoint::{Bitmap, Grid}; - /// # let grid = Bitmap::new(8,2); - /// for y in 0..grid.height() { - /// for x in 0..grid.width() { - /// grid.get(x, y); - /// } - /// } - /// ``` - pub fn iter(&self) -> impl Iterator { - self.bit_vec.iter().by_refs() - } - - /// Iterate over all cells in [Bitmap] mutably. - /// - /// Order is equivalent to the following loop: - /// ``` - /// # use servicepoint::{Bitmap, Grid}; - /// # let mut grid = Bitmap::new(8,2); - /// # let value = false; - /// for y in 0..grid.height() { - /// for x in 0..grid.width() { - /// grid.set(x, y, value); - /// } - /// } - /// ``` - /// - /// # Example - /// ``` - /// # use servicepoint::{Bitmap, Grid}; - /// # let mut grid = Bitmap::new(8,2); - /// # let value = false; - /// for (index, mut pixel) in grid.iter_mut().enumerate() { - /// pixel.set(index % 2 == 0) - /// } - /// ``` - pub fn iter_mut(&mut self) -> IterMut { - self.bit_vec.iter_mut() - } - - /// Iterate over all rows in [Bitmap] top to bottom. - pub fn iter_rows(&self) -> IterRows { - IterRows { - bitmap: self, - row: 0, - } - } -} - -impl Grid for Bitmap { - /// Sets the value of the specified position in the [Bitmap]. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell - /// - `value`: the value to write to the cell - /// - /// returns: old value of the cell - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: bool) { - self.assert_in_bounds(x, y); - self.bit_vec.set(x + y * self.width, value) - } - - fn get(&self, x: usize, y: usize) -> bool { - self.assert_in_bounds(x, y); - self.bit_vec[x + y * self.width] - } - - /// Sets the state of all pixels in the [Bitmap]. - /// - /// # Arguments - /// - /// - `this`: instance to write to - /// - `value`: the value to set all pixels to - fn fill(&mut self, value: bool) { - self.bit_vec.fill(value); - } - - fn width(&self) -> usize { - self.width - } - - fn height(&self) -> usize { - self.height - } -} - -impl DataRef for Bitmap { - fn data_ref_mut(&mut self) -> &mut [u8] { - self.bit_vec.as_raw_mut_slice() - } - - fn data_ref(&self) -> &[u8] { - self.bit_vec.as_raw_slice() - } -} - -impl From for Vec { - /// Turns a [Bitmap] into the underlying [`Vec`]. - fn from(value: Bitmap) -> Self { - value.bit_vec.into() - } -} - -impl From for BitVec { - /// Turns a [Bitmap] into the underlying [BitVec]. - fn from(value: Bitmap) -> Self { - value.bit_vec - } -} - -impl From<&ValueGrid> for Bitmap { - /// Converts a grid of [bool]s into a [Bitmap]. - /// - /// # Panics - /// - /// - when the width of `value` is not dividable by 8 - fn from(value: &ValueGrid) -> Self { - let mut result = Self::new(value.width(), value.height()); - for (mut to, from) in result.iter_mut().zip(value.iter()) { - *to = *from; - } - result - } -} - -impl From<&Bitmap> for ValueGrid { - /// Converts a [Bitmap] into a grid of [bool]s. - fn from(value: &Bitmap) -> Self { - let mut result = Self::new(value.width(), value.height()); - for (to, from) in result.iter_mut().zip(value.iter()) { - *to = *from; - } - result - } -} - -pub struct IterRows<'t> { - bitmap: &'t Bitmap, - row: usize, -} - -impl<'t> Iterator for IterRows<'t> { - type Item = &'t BitSlice; - - fn next(&mut self) -> Option { - if self.row >= self.bitmap.height { - return None; - } - - let start = self.row * self.bitmap.width; - let end = start + self.bitmap.width; - self.row += 1; - Some(&self.bitmap.bit_vec[start..end]) - } -} - -#[cfg(test)] -mod tests { - use crate::{BitVec, Bitmap, DataRef, Grid, ValueGrid}; - - #[test] - fn fill() { - let mut grid = Bitmap::new(8, 2); - assert_eq!(grid.data_ref(), [0x00, 0x00]); - - grid.fill(true); - assert_eq!(grid.data_ref(), [0xFF, 0xFF]); - - grid.fill(false); - assert_eq!(grid.data_ref(), [0x00, 0x00]); - } - - #[test] - fn get_set() { - let mut grid = Bitmap::new(8, 2); - assert!(!grid.get(0, 0)); - assert!(!grid.get(1, 1)); - - grid.set(5, 0, true); - grid.set(1, 1, true); - assert_eq!(grid.data_ref(), [0x04, 0x40]); - - assert!(grid.get(5, 0)); - assert!(grid.get(1, 1)); - assert!(!grid.get(1, 0)); - } - - #[test] - fn load() { - let mut grid = Bitmap::new(8, 3); - for x in 0..grid.width { - for y in 0..grid.height { - grid.set(x, y, (x + y) % 2 == 0); - } - } - - assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); - - let data: Vec = grid.into(); - - let grid = Bitmap::load(8, 3, &data); - assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); - } - - #[test] - #[should_panic] - fn out_of_bounds_x() { - let vec = Bitmap::new(8, 2); - vec.get(8, 1); - } - - #[test] - #[should_panic] - fn out_of_bounds_y() { - let mut vec = Bitmap::new(8, 2); - vec.set(1, 2, false); - } - - #[test] - fn iter() { - let grid = Bitmap::new(8, 2); - assert_eq!(16, grid.iter().count()) - } - - #[test] - fn iter_rows() { - let grid = Bitmap::load(8, 2, &[0x04, 0x40]); - let mut iter = grid.iter_rows(); - - assert_eq!(iter.next().unwrap().count_ones(), 1); - assert_eq!(iter.next().unwrap().count_ones(), 1); - assert_eq!(None, iter.next()); - } - - #[test] - fn iter_mut() { - let mut grid = Bitmap::new(8, 2); - for (index, mut pixel) in grid.iter_mut().enumerate() { - pixel.set(index % 2 == 0); - } - assert_eq!(grid.data_ref(), [0xAA, 0xAA]); - } - - #[test] - fn data_ref_mut() { - let mut grid = Bitmap::new(8, 2); - let data = grid.data_ref_mut(); - data[1] = 0x0F; - assert!(grid.get(7, 1)); - } - - #[test] - fn to_bitvec() { - let mut grid = Bitmap::new(8, 2); - grid.set(0, 0, true); - let bitvec: BitVec = grid.into(); - assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]); - } - - #[test] - fn from_bool_grid() { - let original = ValueGrid::load( - 8, - 1, - &[true, false, true, false, true, false, true, false], - ); - let converted = Bitmap::from(&original); - let reconverted = ValueGrid::from(&converted); - assert_eq!(original, reconverted); - } -} diff --git a/crates/servicepoint/src/brightness.rs b/crates/servicepoint/src/brightness.rs deleted file mode 100644 index 539a0a2..0000000 --- a/crates/servicepoint/src/brightness.rs +++ /dev/null @@ -1,109 +0,0 @@ -#[cfg(feature = "rand")] -use rand::{ - distributions::{Distribution, Standard}, - Rng, -}; - -/// A display brightness value, checked for correct value range -/// -/// # Examples -/// -/// ``` -/// # use servicepoint::{Brightness, Command, Connection}; -/// let b = Brightness::MAX; -/// let val: u8 = b.into(); -/// -/// let b = Brightness::try_from(7).unwrap(); -/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); -/// let result = connection.send(Command::Brightness(b)); -/// ``` -#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] -pub struct Brightness(u8); - -impl From for u8 { - fn from(brightness: Brightness) -> Self { - Self::from(&brightness) - } -} - -impl From<&Brightness> for u8 { - fn from(brightness: &Brightness) -> Self { - brightness.0 - } -} - -impl TryFrom for Brightness { - type Error = u8; - - fn try_from(value: u8) -> Result { - if value > Self::MAX.0 { - Err(value) - } else { - Ok(Brightness(value)) - } - } -} - -impl Brightness { - /// highest possible brightness value, 11 - pub const MAX: Brightness = Brightness(11); - /// lowest possible brightness value, 0 - pub const MIN: Brightness = Brightness(0); - - /// Create a brightness value without returning an error for brightnesses above [Brightness::MAX]. - /// - /// returns: the specified value as a [Brightness], or [Brightness::MAX]. - pub fn saturating_from(value: u8) -> Brightness { - if value > Brightness::MAX.into() { - Brightness::MAX - } else { - Brightness(value) - } - } -} - -impl Default for Brightness { - fn default() -> Self { - Self::MAX - } -} - -#[cfg(feature = "rand")] -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Brightness { - Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn brightness_from_u8() { - assert_eq!(Err(100), Brightness::try_from(100)); - assert_eq!(Ok(Brightness(1)), Brightness::try_from(1)); - } - - #[test] - #[cfg(feature = "rand")] - fn rand_brightness() { - let mut rng = rand::thread_rng(); - for _ in 0..100 { - let _: Brightness = rng.gen(); - } - } - - #[test] - fn saturating_convert() { - assert_eq!(Brightness::MAX, Brightness::saturating_from(100)); - assert_eq!(Brightness(5), Brightness::saturating_from(5)); - } - - #[test] - #[cfg(feature = "rand")] - fn test() { - let mut rng = rand::thread_rng(); - assert_ne!(rng.gen::(), rng.gen()); - } -} diff --git a/crates/servicepoint/src/brightness_grid.rs b/crates/servicepoint/src/brightness_grid.rs deleted file mode 100644 index 5727e39..0000000 --- a/crates/servicepoint/src/brightness_grid.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::brightness::Brightness; -use crate::grid::Grid; -use crate::value_grid::ValueGrid; -use crate::ByteGrid; - -/// A grid containing brightness values. -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin}; -/// let mut grid = BrightnessGrid::new(2,2); -/// grid.set(0, 0, Brightness::MIN); -/// grid.set(1, 1, Brightness::MIN); -/// -/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); -/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap() -/// ``` -pub type BrightnessGrid = ValueGrid; - -impl BrightnessGrid { - /// Like [Self::load], but ignoring any out-of-range brightness values - pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { - ValueGrid::load(width, height, data).map(Brightness::saturating_from) - } -} - -impl From for Vec { - fn from(value: BrightnessGrid) -> Self { - value - .iter() - .map(|brightness| (*brightness).into()) - .collect() - } -} - -impl From<&BrightnessGrid> for ByteGrid { - fn from(value: &BrightnessGrid) -> Self { - let u8s = value - .iter() - .map(|brightness| (*brightness).into()) - .collect::>(); - ValueGrid::load(value.width(), value.height(), &u8s) - } -} - -impl TryFrom for BrightnessGrid { - type Error = u8; - - fn try_from(value: ByteGrid) -> Result { - let brightnesses = value - .iter() - .map(|b| Brightness::try_from(*b)) - .collect::, _>>()?; - Ok(BrightnessGrid::load( - value.width(), - value.height(), - &brightnesses, - )) - } -} - -#[cfg(test)] -mod tests { - use crate::value_grid::ValueGrid; - use crate::{Brightness, BrightnessGrid, DataRef, Grid}; - - #[test] - fn to_u8_grid() { - let mut grid = BrightnessGrid::new(2, 2); - grid.set(1, 0, Brightness::MIN); - grid.set(0, 1, Brightness::MAX); - let actual = ValueGrid::from(&grid); - assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); - } - - #[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]) - ); - } -} diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs deleted file mode 100644 index 0a7fdae..0000000 --- a/crates/servicepoint/src/byte_grid.rs +++ /dev/null @@ -1,4 +0,0 @@ -use crate::ValueGrid; - -/// A 2d grid of bytes - see [ValueGrid]. -pub type ByteGrid = ValueGrid; diff --git a/crates/servicepoint/src/char_grid.rs b/crates/servicepoint/src/char_grid.rs deleted file mode 100644 index d1a3fd7..0000000 --- a/crates/servicepoint/src/char_grid.rs +++ /dev/null @@ -1,298 +0,0 @@ -use crate::{Grid, SetValueSeriesError, TryLoadValueGridError, ValueGrid}; -use std::string::FromUtf8Error; - -/// A grid containing UTF-8 characters. -/// -/// To send a CharGrid to the display, use [Command::Utf8Data](crate::Command::Utf8Data). -/// -/// Also see [ValueGrid] for the non-specialized operations and examples. -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{CharGrid, Command, Connection, Origin}; -/// let grid = CharGrid::from("You can\nload multiline\nstrings directly"); -/// assert_eq!(grid.get_row_str(1), Some("load multiline\0\0".to_string())); -/// -/// # let connection = Connection::Fake; -/// let command = Command::Utf8Data(Origin::ZERO, grid); -/// ``` -pub type CharGrid = ValueGrid; - -impl CharGrid { - /// Loads a [CharGrid] with the specified width from the provided text, wrapping to as many rows as needed. - /// - /// The passed rows are extended with '\0' if needed. - /// - /// returns: [CharGrid] that contains a copy of the provided data. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let grid = CharGrid::wrap_str(2, "abc\ndef"); - /// ``` - pub fn wrap_str(width: usize, text: &str) -> Self { - let lines = text - .split('\n') - .flat_map(move |x| { - x.chars() - .collect::>() - .chunks(width) - .map(|c| { - let mut s = String::from_iter(c); - s.push_str(&"\0".repeat(width - s.chars().count())); - s - }) - .collect::>() - }) - .collect::>(); - let height = lines.len(); - let mut result = Self::new(width, height); - for (row, text_line) in lines.iter().enumerate() { - result.set_row_str(row, text_line).unwrap() - } - result - } - - /// Copies a column from the grid as a String. - /// - /// Returns [None] if x is out of bounds. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let grid = CharGrid::from("ab\ncd"); - /// let col = grid.get_col_str(0).unwrap(); // "ac" - /// ``` - pub fn get_col_str(&self, x: usize) -> Option { - 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. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let grid = CharGrid::from("ab\ncd"); - /// let row = grid.get_row_str(0).unwrap(); // "ab" - /// ``` - pub fn get_row_str(&self, y: usize) -> Option { - Some(String::from_iter(self.get_row(y)?)) - } - - /// Overwrites a row in the grid with a str. - /// - /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let mut grid = CharGrid::from("ab\ncd"); - /// grid.set_row_str(0, "ef").unwrap(); - /// ``` - pub fn set_row_str( - &mut self, - y: usize, - value: &str, - ) -> Result<(), SetValueSeriesError> { - self.set_row(y, value.chars().collect::>().as_ref()) - } - - /// Overwrites a column in the grid with a str. - /// - /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let mut grid = CharGrid::from("ab\ncd"); - /// grid.set_col_str(0, "ef").unwrap(); - /// ``` - pub fn set_col_str( - &mut self, - x: usize, - value: &str, - ) -> Result<(), SetValueSeriesError> { - self.set_col(x, value.chars().collect::>().as_ref()) - } - - /// Loads a [CharGrid] with the specified dimensions from the provided UTF-8 bytes. - /// - /// returns: [CharGrid] that contains the provided data, or [FromUtf8Error] if the data is invalid. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::CharGrid; - /// let grid = CharGrid::load_utf8(2, 2, [97u8, 98, 99, 100].to_vec()); - /// ``` - pub fn load_utf8( - width: usize, - height: usize, - bytes: Vec, - ) -> Result { - let s: Vec = String::from_utf8(bytes)?.chars().collect(); - Ok(CharGrid::try_load(width, height, s)?) - } -} - -#[derive(Debug, thiserror::Error)] -pub enum LoadUtf8Error { - #[error(transparent)] - FromUtf8Error(#[from] FromUtf8Error), - #[error(transparent)] - TryLoadError(#[from] TryLoadValueGridError), -} - -impl From<&str> for CharGrid { - fn from(value: &str) -> Self { - let value = value.replace("\r\n", "\n"); - let mut lines = value.split('\n').collect::>(); - let width = lines - .iter() - .fold(0, move |a, x| std::cmp::max(a, x.chars().count())); - - 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 for CharGrid { - fn from(value: String) -> Self { - CharGrid::from(&*value) - } -} - -impl From for String { - fn from(grid: CharGrid) -> Self { - String::from(&grid) - } -} - -impl From<&CharGrid> for String { - /// Converts a [CharGrid] into a [String]. - /// - /// Rows are separated by '\n'. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::CharGrid; - /// let grid = CharGrid::from("ab\ncd"); - /// let string = String::from(grid); - /// let grid = CharGrid::from(string); - /// ``` - fn from(value: &CharGrid) -> Self { - value - .iter_rows() - .map(String::from_iter) - .collect::>() - .join("\n") - } -} - -impl From<&CharGrid> for Vec { - /// Converts a [CharGrid] into a [`Vec`]. - /// - /// Rows are not separated. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{CharGrid, Grid}; - /// let grid = CharGrid::from("ab\ncd"); - /// let height = grid.height(); - /// let width = grid.width(); - /// let grid = CharGrid::load_utf8(width, height, grid.into()); - /// ``` - fn from(value: &CharGrid) -> Self { - String::from_iter(value.iter()).into_bytes() - } -} - -impl From for Vec { - /// See [`From<&CharGrid>::from`]. - fn from(value: CharGrid) -> Self { - Self::from(&value) - } -} - -#[cfg(test)] -mod test { - use super::*; - #[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(SetValueSeriesError::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() { - // conversion with .to_string() covers one more line - let original = "Hello\r\nWorld!\n...\n".to_string(); - - let grid = CharGrid::from(original); - assert_eq!(3, grid.height()); - assert_eq!("Hello\0\nWorld!\n...\0\0\0", String::from(grid)); - } - - #[test] - fn round_trip_bytes() { - let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0"); - let bytes: Vec = grid.clone().into(); - let copy = - CharGrid::load_utf8(grid.width(), grid.height(), bytes).unwrap(); - assert_eq!(grid, copy); - } - - #[test] - fn round_trip_string() { - let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0"); - let str: String = grid.clone().into(); - let copy = CharGrid::from(str); - assert_eq!(grid, copy); - } - - #[test] - fn wrap_str() { - let grid = CharGrid::wrap_str(2, "abc\ndef"); - assert_eq!(4, grid.height()); - assert_eq!("ab\nc\0\nde\nf\0", String::from(grid)); - } -} diff --git a/crates/servicepoint/src/command.rs b/crates/servicepoint/src/command.rs deleted file mode 100644 index 4efda1f..0000000 --- a/crates/servicepoint/src/command.rs +++ /dev/null @@ -1,968 +0,0 @@ -use crate::command_code::CommandCode; -use crate::compression::into_decompressed; -use crate::*; - -/// Type alias for documenting the meaning of the u16 in enum values -pub type Offset = usize; - -/// A low-level display command. -/// -/// This struct and associated functions implement the UDP protocol for the display. -/// -/// To send a [Command], use a [connection][crate::Connection]. -/// -/// # Available commands -/// -/// To send text, take a look at [Command::Cp437Data]. -/// -/// To draw pixels, the easiest command to use is [Command::BitmapLinearWin]. -/// -/// The other BitmapLinear-Commands operate on a region of pixel memory directly. -/// [Command::BitmapLinear] overwrites a region. -/// [Command::BitmapLinearOr], [Command::BitmapLinearAnd] and [Command::BitmapLinearXor] apply logical operations per pixel. -/// -/// Out of bounds operations may be truncated or ignored by the display. -/// -/// # Compression -/// -/// Some commands can contain compressed payloads. -/// To get started, use [CompressionCode::Uncompressed]. -/// -/// If you want to archive the best performance (e.g. latency), -/// you can try the different compression algorithms for your hardware and use case. -/// -/// In memory, the payload is not compressed in the [Command]. -/// Payload (de-)compression happens when converting the [Command] into a [Packet] or vice versa. -/// -/// # Examples -/// -/// ```rust -/// use servicepoint::{Brightness, Command, Connection, Packet}; -/// # -/// // create command -/// let command = Command::Brightness(Brightness::MAX); -/// -/// // turn command into Packet -/// let packet: Packet = command.clone().into(); -/// -/// // read command from packet -/// let round_tripped = Command::try_from(packet).unwrap(); -/// -/// // round tripping produces exact copy -/// assert_eq!(command, round_tripped); -/// -/// // send command -/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); -/// connection.send(command).unwrap(); -/// ``` -#[derive(Debug, Clone, PartialEq)] -pub enum Command { - /// Set all pixels to the off state. Does not affect brightness. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection}; - /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); - /// connection.send(Command::Clear).unwrap(); - /// ``` - Clear, - - /// Show text on the screen. - /// - /// The text is sent in the form of a 2D grid of UTF-8 encoded characters (the default encoding in rust). - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection, Origin, CharGrid}; - /// # let connection = Connection::Fake; - /// let grid = CharGrid::from("Hello,\nWorld!"); - /// connection.send(Command::Utf8Data(Origin::ZERO, grid)).expect("send failed"); - /// ``` - Utf8Data(Origin, CharGrid), - - /// Show text on the screen. - /// - /// The text is sent in the form of a 2D grid of [CP-437] encoded characters. - /// - ///
You probably want to use [Command::Utf8Data] instead
- /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection, Origin, CharGrid, Cp437Grid}; - /// # let connection = Connection::Fake; - /// let grid = CharGrid::from("Hello,\nWorld!"); - /// let grid = Cp437Grid::from(&grid); - /// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed"); - /// ``` - /// - /// ```rust - /// # use servicepoint::{Command, Connection, Cp437Grid, Origin}; - /// # let connection = Connection::Fake; - /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap(); - /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap(); - /// ``` - /// [CP-437]: https://en.wikipedia.org/wiki/Code_page_437 - Cp437Data(Origin, Cp437Grid), - - /// Overwrites a rectangular region of pixels. - /// - /// Origin coordinates must be divisible by 8. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, CompressionCode, Grid, Bitmap}; - /// # let connection = servicepoint::Connection::Fake; - /// # - /// let mut pixels = Bitmap::max_sized(); - /// // draw something to the pixels here - /// # pixels.set(2, 5, true); - /// - /// // create command to send pixels - /// let command = Command::BitmapLinearWin( - /// servicepoint::Origin::ZERO, - /// pixels, - /// CompressionCode::Uncompressed - /// ); - /// - /// connection.send(command).expect("send failed"); - /// ``` - BitmapLinearWin(Origin, Bitmap, CompressionCode), - - /// Set the brightness of all tiles to the same value. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Brightness, Command, Connection}; - /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); - /// let command = Command::Brightness(Brightness::MAX); - /// connection.send(command).unwrap(); - /// ``` - Brightness(Brightness), - - /// Set the brightness of individual tiles in a rectangular area of the display. - CharBrightness(Origin, BrightnessGrid), - - /// Set pixel data starting at the pixel offset on screen. - /// - /// The screen will continuously overwrite more pixel data without regarding the offset, meaning - /// once the starting row is full, overwriting will continue on column 0. - /// - /// The contained [BitVec] is always uncompressed. - BitmapLinear(Offset, BitVec, CompressionCode), - - /// Set pixel data according to an and-mask starting at the offset. - /// - /// The screen will continuously overwrite more pixel data without regarding the offset, meaning - /// once the starting row is full, overwriting will continue on column 0. - /// - /// The contained [BitVec] is always uncompressed. - BitmapLinearAnd(Offset, BitVec, CompressionCode), - - /// Set pixel data according to an or-mask starting at the offset. - /// - /// The screen will continuously overwrite more pixel data without regarding the offset, meaning - /// once the starting row is full, overwriting will continue on column 0. - /// - /// The contained [BitVec] is always uncompressed. - BitmapLinearOr(Offset, BitVec, CompressionCode), - - /// Set pixel data according to a xor-mask starting at the offset. - /// - /// The screen will continuously overwrite more pixel data without regarding the offset, meaning - /// once the starting row is full, overwriting will continue on column 0. - /// - /// The contained [BitVec] is always uncompressed. - BitmapLinearXor(Offset, BitVec, CompressionCode), - - /// Kills the udp daemon on the display, which usually results in a restart. - /// - /// Please do not send this in your normal program flow. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection}; - /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); - /// connection.send(Command::HardReset).unwrap(); - /// ``` - HardReset, - - ///
Untested
- /// - /// Slowly decrease brightness until off or something like that? - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection}; - /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); - /// connection.send(Command::FadeOut).unwrap(); - /// ``` - FadeOut, - - /// Legacy command code, gets ignored by the real display. - /// - /// Might be useful as a noop package. - /// - /// # Examples - /// - /// ```rust - /// # use servicepoint::{Command, Connection}; - /// # let connection = Connection::open("127.0.0.1:2342").unwrap(); - /// // this sends a packet that does nothing - /// # #[allow(deprecated)] - /// connection.send(Command::BitmapLegacy).unwrap(); - /// ``` - #[deprecated] - BitmapLegacy, -} - -/// Err values for [Command::try_from]. -#[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), - #[error(transparent)] - InvalidUtf8(#[from] std::string::FromUtf8Error), -} - -impl TryFrom for Command { - type Error = TryFromPacketError; - - /// Try to interpret the [Packet] as one containing a [Command] - fn try_from(packet: Packet) -> Result { - let Packet { - header: Header { - command_code, a, .. - }, - .. - } = packet; - let command_code = match CommandCode::try_from(command_code) { - Err(()) => { - return Err(TryFromPacketError::InvalidCommand(command_code)); - } - Ok(value) => value, - }; - - match command_code { - CommandCode::Clear => { - Self::packet_into_command_only(packet, Command::Clear) - } - CommandCode::Brightness => Self::packet_into_brightness(&packet), - CommandCode::HardReset => { - Self::packet_into_command_only(packet, Command::HardReset) - } - CommandCode::FadeOut => { - Self::packet_into_command_only(packet, Command::FadeOut) - } - CommandCode::Cp437Data => Self::packet_into_cp437(&packet), - CommandCode::CharBrightness => { - Self::packet_into_char_brightness(&packet) - } - CommandCode::Utf8Data => Self::packet_into_utf8(&packet), - #[allow(deprecated)] - CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy), - CommandCode::BitmapLinear => { - let (vec, compression) = - Self::packet_into_linear_bitmap(packet)?; - Ok(Command::BitmapLinear(a as Offset, vec, compression)) - } - CommandCode::BitmapLinearAnd => { - let (vec, compression) = - Self::packet_into_linear_bitmap(packet)?; - Ok(Command::BitmapLinearAnd(a as Offset, vec, compression)) - } - CommandCode::BitmapLinearOr => { - let (vec, compression) = - Self::packet_into_linear_bitmap(packet)?; - Ok(Command::BitmapLinearOr(a as Offset, vec, compression)) - } - CommandCode::BitmapLinearXor => { - let (vec, compression) = - Self::packet_into_linear_bitmap(packet)?; - Ok(Command::BitmapLinearXor(a as Offset, vec, compression)) - } - CommandCode::BitmapLinearWinUncompressed => { - Self::packet_into_bitmap_win( - packet, - CompressionCode::Uncompressed, - ) - } - #[cfg(feature = "compression_zlib")] - CommandCode::BitmapLinearWinZlib => { - Self::packet_into_bitmap_win(packet, CompressionCode::Zlib) - } - #[cfg(feature = "compression_bzip2")] - CommandCode::BitmapLinearWinBzip2 => { - Self::packet_into_bitmap_win(packet, CompressionCode::Bzip2) - } - #[cfg(feature = "compression_lzma")] - CommandCode::BitmapLinearWinLzma => { - Self::packet_into_bitmap_win(packet, CompressionCode::Lzma) - } - #[cfg(feature = "compression_zstd")] - CommandCode::BitmapLinearWinZstd => { - Self::packet_into_bitmap_win(packet, CompressionCode::Zstd) - } - } - } -} - -impl Command { - fn packet_into_bitmap_win( - packet: Packet, - compression: CompressionCode, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a: tiles_x, - b: pixels_y, - c: tile_w, - d: pixel_h, - }, - payload, - } = packet; - - let payload = match into_decompressed(compression, payload) { - None => return Err(TryFromPacketError::DecompressionFailed), - Some(decompressed) => decompressed, - }; - - Ok(Command::BitmapLinearWin( - Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize), - Bitmap::load( - tile_w as usize * TILE_SIZE, - pixel_h as usize, - &payload, - ), - compression, - )) - } - - /// Helper method for checking that a packet is empty and only contains a command code - fn packet_into_command_only( - packet: Packet, - command: Command, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a, - b, - c, - d, - }, - payload, - } = packet; - if !payload.is_empty() { - Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) - } else if a != 0 || b != 0 || c != 0 || d != 0 { - Err(TryFromPacketError::ExtraneousHeaderValues) - } else { - Ok(command) - } - } - - /// Helper method for Packets into `BitmapLinear*`-Commands - fn packet_into_linear_bitmap( - packet: Packet, - ) -> Result<(BitVec, CompressionCode), TryFromPacketError> { - let Packet { - header: - Header { - b: length, - c: sub, - d: reserved, - .. - }, - payload, - } = packet; - if reserved != 0 { - return Err(TryFromPacketError::ExtraneousHeaderValues); - } - let sub = match CompressionCode::try_from(sub) { - Err(()) => { - return Err(TryFromPacketError::InvalidCompressionCode(sub)); - } - Ok(value) => value, - }; - let payload = match into_decompressed(sub, payload) { - None => return Err(TryFromPacketError::DecompressionFailed), - Some(value) => value, - }; - if payload.len() != length as usize { - return Err(TryFromPacketError::UnexpectedPayloadSize( - length as usize, - payload.len(), - )); - } - Ok((BitVec::from_vec(payload), sub)) - } - - fn packet_into_char_brightness( - packet: &Packet, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a: x, - b: y, - c: width, - d: height, - }, - payload, - } = packet; - - let grid = ByteGrid::load(*width as usize, *height as usize, payload); - let grid = match BrightnessGrid::try_from(grid) { - Ok(grid) => grid, - Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)), - }; - - Ok(Command::CharBrightness( - Origin::new(*x as usize, *y as usize), - grid, - )) - } - - fn packet_into_brightness( - packet: &Packet, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a, - b, - c, - d, - }, - payload, - } = packet; - if payload.len() != 1 { - return Err(TryFromPacketError::UnexpectedPayloadSize( - 1, - payload.len(), - )); - } - - if *a != 0 || *b != 0 || *c != 0 || *d != 0 { - return Err(TryFromPacketError::ExtraneousHeaderValues); - } - - match Brightness::try_from(payload[0]) { - Ok(b) => Ok(Command::Brightness(b)), - Err(_) => Err(TryFromPacketError::InvalidBrightness(payload[0])), - } - } - - fn packet_into_cp437( - packet: &Packet, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a, - b, - c, - d, - }, - payload, - } = packet; - Ok(Command::Cp437Data( - Origin::new(*a as usize, *b as usize), - Cp437Grid::load(*c as usize, *d as usize, payload), - )) - } - - fn packet_into_utf8( - packet: &Packet, - ) -> Result { - let Packet { - header: - Header { - command_code: _, - a, - b, - c, - d, - }, - payload, - } = packet; - let payload: Vec<_> = - String::from_utf8(payload.clone())?.chars().collect(); - Ok(Command::Utf8Data( - Origin::new(*a as usize, *b as usize), - CharGrid::load(*c as usize, *d as usize, &payload), - )) - } -} - -#[cfg(test)] -mod tests { - use crate::command::TryFromPacketError; - use crate::command_code::CommandCode; - use crate::{ - BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, Command, - CompressionCode, Cp437Grid, Header, Origin, Packet, Pixels, - }; - - fn round_trip(original: Command) { - let packet: Packet = original.clone().into(); - let copy: Command = match Command::try_from(packet) { - Ok(command) => command, - Err(err) => panic!("could not reload {original:?}: {err:?}"), - }; - assert_eq!(copy, original); - } - - fn all_compressions<'t>() -> &'t [CompressionCode] { - &[ - CompressionCode::Uncompressed, - #[cfg(feature = "compression_lzma")] - CompressionCode::Lzma, - #[cfg(feature = "compression_bzip2")] - CompressionCode::Bzip2, - #[cfg(feature = "compression_zlib")] - CompressionCode::Zlib, - #[cfg(feature = "compression_zstd")] - CompressionCode::Zstd, - ] - } - - #[test] - fn round_trip_clear() { - round_trip(Command::Clear); - } - - #[test] - fn round_trip_hard_reset() { - round_trip(Command::HardReset); - } - - #[test] - fn round_trip_fade_out() { - round_trip(Command::FadeOut); - } - - #[test] - fn round_trip_brightness() { - round_trip(Command::Brightness(Brightness::try_from(6).unwrap())); - } - - #[test] - #[allow(deprecated)] - fn round_trip_bitmap_legacy() { - round_trip(Command::BitmapLegacy); - } - - #[test] - fn round_trip_char_brightness() { - round_trip(Command::CharBrightness( - Origin::new(5, 2), - BrightnessGrid::new(7, 5), - )); - } - - #[test] - fn round_trip_cp437_data() { - round_trip(Command::Cp437Data(Origin::new(5, 2), Cp437Grid::new(7, 5))); - } - - #[test] - fn round_trip_utf8_data() { - round_trip(Command::Utf8Data(Origin::new(5, 2), CharGrid::new(7, 5))); - } - - #[test] - fn round_trip_bitmap_linear() { - for compression in all_compressions().iter().copied() { - round_trip(Command::BitmapLinear( - 23, - BitVec::repeat(false, 40), - compression, - )); - round_trip(Command::BitmapLinearAnd( - 23, - BitVec::repeat(false, 40), - compression, - )); - round_trip(Command::BitmapLinearOr( - 23, - BitVec::repeat(false, 40), - compression, - )); - round_trip(Command::BitmapLinearXor( - 23, - BitVec::repeat(false, 40), - compression, - )); - round_trip(Command::BitmapLinearWin( - Origin::ZERO, - Bitmap::max_sized(), - compression, - )); - } - } - - #[test] - fn error_invalid_command() { - let p = Packet { - header: Header { - command_code: 0xFF, - a: 0x00, - b: 0x00, - c: 0x00, - d: 0x00, - }, - payload: vec![], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::InvalidCommand(0xFF)) - )) - } - - #[test] - fn error_extraneous_header_values_clear() { - let p = Packet { - header: Header { - command_code: CommandCode::Clear.into(), - a: 0x05, - b: 0x00, - c: 0x00, - d: 0x00, - }, - payload: vec![], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::ExtraneousHeaderValues) - )) - } - - #[test] - fn error_extraneous_header_values_brightness() { - let p = Packet { - header: Header { - command_code: CommandCode::Brightness.into(), - a: 0x00, - b: 0x13, - c: 0x37, - d: 0x00, - }, - payload: vec![5], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::ExtraneousHeaderValues) - )) - } - - #[test] - fn error_extraneous_header_hard_reset() { - let p = Packet { - header: Header { - command_code: CommandCode::HardReset.into(), - a: 0x00, - b: 0x00, - c: 0x00, - d: 0x01, - }, - payload: vec![], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::ExtraneousHeaderValues) - )) - } - - #[test] - fn error_extraneous_header_fade_out() { - let p = Packet { - header: Header { - command_code: CommandCode::FadeOut.into(), - a: 0x10, - b: 0x00, - c: 0x00, - d: 0x01, - }, - payload: vec![], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::ExtraneousHeaderValues) - )) - } - - #[test] - fn error_unexpected_payload() { - let p = Packet { - header: Header { - command_code: CommandCode::FadeOut.into(), - a: 0x00, - b: 0x00, - c: 0x00, - d: 0x00, - }, - payload: vec![5, 7], - }; - let result = Command::try_from(p); - assert!(matches!( - result, - Err(TryFromPacketError::UnexpectedPayloadSize(0, 2)) - )) - } - - #[test] - fn error_decompression_failed_win() { - for compression in all_compressions().iter().copied() { - let p: Packet = Command::BitmapLinearWin( - Origin::new(16, 8), - Bitmap::new(8, 8), - compression, - ) - .into(); - - let Packet { - header, - mut payload, - } = p; - - // mangle it - for byte in payload.iter_mut() { - *byte -= *byte / 2; - } - - let p = Packet { header, payload }; - let result = Command::try_from(p); - if compression != CompressionCode::Uncompressed { - assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) - } else { - assert!(result.is_ok()); - } - } - } - - #[test] - fn error_decompression_failed_and() { - for compression in all_compressions().iter().copied() { - let p: Packet = Command::BitmapLinearAnd( - 0, - BitVec::repeat(false, 8), - compression, - ) - .into(); - let Packet { - header, - mut payload, - } = p; - - // mangle it - for byte in payload.iter_mut() { - *byte -= *byte / 2; - } - - let p = Packet { header, payload }; - let result = Command::try_from(p); - if compression != CompressionCode::Uncompressed { - assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) - } else { - // when not compressing, there is no way to detect corrupted data - assert!(result.is_ok()); - } - } - } - - #[test] - fn unexpected_payload_size_brightness() { - assert_eq!( - Command::try_from(Packet { - header: Header { - command_code: CommandCode::Brightness.into(), - a: 0, - b: 0, - c: 0, - d: 0, - }, - payload: vec!() - }), - Err(TryFromPacketError::UnexpectedPayloadSize(1, 0)) - ); - - assert_eq!( - Command::try_from(Packet { - header: Header { - command_code: CommandCode::Brightness.into(), - a: 0, - b: 0, - c: 0, - d: 0, - }, - payload: vec!(0, 0) - }), - Err(TryFromPacketError::UnexpectedPayloadSize(1, 2)) - ); - } - - #[test] - fn error_reserved_used() { - let Packet { header, payload } = Command::BitmapLinear( - 0, - BitVec::repeat(false, 8), - CompressionCode::Uncompressed, - ) - .into(); - let Header { - command_code: command, - a: offset, - b: length, - c: sub, - d: _reserved, - } = header; - let p = Packet { - header: Header { - command_code: command, - a: offset, - b: length, - c: sub, - d: 69, - }, - payload, - }; - assert_eq!( - Command::try_from(p), - Err(TryFromPacketError::ExtraneousHeaderValues) - ); - } - - #[test] - fn error_invalid_compression() { - let Packet { header, payload } = Command::BitmapLinear( - 0, - BitVec::repeat(false, 8), - CompressionCode::Uncompressed, - ) - .into(); - let Header { - command_code: command, - a: offset, - b: length, - c: _sub, - d: reserved, - } = header; - let p = Packet { - header: Header { - command_code: command, - a: offset, - b: length, - c: 42, - d: reserved, - }, - payload, - }; - assert_eq!( - Command::try_from(p), - Err(TryFromPacketError::InvalidCompressionCode(42)) - ); - } - - #[test] - fn error_unexpected_size() { - let Packet { header, payload } = Command::BitmapLinear( - 0, - BitVec::repeat(false, 8), - CompressionCode::Uncompressed, - ) - .into(); - let Header { - command_code: command, - a: offset, - b: length, - c: compression, - d: reserved, - } = header; - let p = Packet { - header: Header { - command_code: command, - a: offset, - b: 420, - c: compression, - d: reserved, - }, - payload, - }; - assert_eq!( - Command::try_from(p), - Err(TryFromPacketError::UnexpectedPayloadSize( - 420, - length as usize, - )) - ); - } - - #[test] - fn origin_add() { - assert_eq!( - Origin::::new(4, 2), - Origin::new(1, 0) + Origin::new(3, 2) - ); - } - - #[test] - fn packet_into_char_brightness_invalid() { - let grid = BrightnessGrid::new(2, 2); - let command = Command::CharBrightness(Origin::ZERO, grid); - let mut packet: Packet = command.into(); - let slot = packet.payload.get_mut(1).unwrap(); - *slot = 23; - assert_eq!( - Command::try_from(packet), - Err(TryFromPacketError::InvalidBrightness(23)) - ); - } - - #[test] - fn packet_into_brightness_invalid() { - let mut packet: Packet = Command::Brightness(Brightness::MAX).into(); - let slot = packet.payload.get_mut(0).unwrap(); - *slot = 42; - assert_eq!( - Command::try_from(packet), - Err(TryFromPacketError::InvalidBrightness(42)) - ); - } -} diff --git a/crates/servicepoint/src/command_code.rs b/crates/servicepoint/src/command_code.rs deleted file mode 100644 index a105522..0000000 --- a/crates/servicepoint/src/command_code.rs +++ /dev/null @@ -1,214 +0,0 @@ -/// The u16 command codes used for the [Command]s. -#[repr(u16)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum CommandCode { - Clear = 0x0002, - Cp437Data = 0x0003, - CharBrightness = 0x0005, - Brightness = 0x0007, - HardReset = 0x000b, - FadeOut = 0x000d, - #[deprecated] - BitmapLegacy = 0x0010, - BitmapLinear = 0x0012, - BitmapLinearWinUncompressed = 0x0013, - BitmapLinearAnd = 0x0014, - BitmapLinearOr = 0x0015, - BitmapLinearXor = 0x0016, - #[cfg(feature = "compression_zlib")] - BitmapLinearWinZlib = 0x0017, - #[cfg(feature = "compression_bzip2")] - BitmapLinearWinBzip2 = 0x0018, - #[cfg(feature = "compression_lzma")] - BitmapLinearWinLzma = 0x0019, - Utf8Data = 0x0020, - #[cfg(feature = "compression_zstd")] - BitmapLinearWinZstd = 0x001A, -} - -impl From for u16 { - /// returns the u16 command code corresponding to the enum value - fn from(value: CommandCode) -> Self { - value as u16 - } -} - -impl TryFrom for CommandCode { - type Error = (); - - /// Returns the enum value for the specified `u16` or `Error` if the code is unknown. - fn try_from(value: u16) -> Result { - match value { - value if value == CommandCode::Clear as u16 => { - Ok(CommandCode::Clear) - } - value if value == CommandCode::Cp437Data as u16 => { - Ok(CommandCode::Cp437Data) - } - value if value == CommandCode::CharBrightness as u16 => { - Ok(CommandCode::CharBrightness) - } - value if value == CommandCode::Brightness as u16 => { - Ok(CommandCode::Brightness) - } - value if value == CommandCode::HardReset as u16 => { - Ok(CommandCode::HardReset) - } - value if value == CommandCode::FadeOut as u16 => { - Ok(CommandCode::FadeOut) - } - #[allow(deprecated)] - value if value == CommandCode::BitmapLegacy as u16 => { - Ok(CommandCode::BitmapLegacy) - } - value if value == CommandCode::BitmapLinear as u16 => { - Ok(CommandCode::BitmapLinear) - } - value - if value == CommandCode::BitmapLinearWinUncompressed as u16 => - { - Ok(CommandCode::BitmapLinearWinUncompressed) - } - value if value == CommandCode::BitmapLinearAnd as u16 => { - Ok(CommandCode::BitmapLinearAnd) - } - value if value == CommandCode::BitmapLinearOr as u16 => { - Ok(CommandCode::BitmapLinearOr) - } - value if value == CommandCode::BitmapLinearXor as u16 => { - Ok(CommandCode::BitmapLinearXor) - } - #[cfg(feature = "compression_zstd")] - value if value == CommandCode::BitmapLinearWinZstd as u16 => { - Ok(CommandCode::BitmapLinearWinZstd) - } - #[cfg(feature = "compression_lzma")] - value if value == CommandCode::BitmapLinearWinLzma as u16 => { - Ok(CommandCode::BitmapLinearWinLzma) - } - #[cfg(feature = "compression_zlib")] - value if value == CommandCode::BitmapLinearWinZlib as u16 => { - Ok(CommandCode::BitmapLinearWinZlib) - } - #[cfg(feature = "compression_bzip2")] - value if value == CommandCode::BitmapLinearWinBzip2 as u16 => { - Ok(CommandCode::BitmapLinearWinBzip2) - } - value if value == CommandCode::Utf8Data as u16 => { - Ok(CommandCode::Utf8Data) - } - _ => Err(()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn clear() { - assert_eq!(CommandCode::try_from(0x0002), Ok(CommandCode::Clear)); - assert_eq!(u16::from(CommandCode::Clear), 0x0002); - } - - #[test] - fn cp437_data() { - assert_eq!(CommandCode::try_from(0x0003), Ok(CommandCode::Cp437Data)); - assert_eq!(u16::from(CommandCode::Cp437Data), 0x0003); - } - - #[test] - fn char_brightness() { - assert_eq!(CommandCode::try_from(0x0005), Ok(CommandCode::CharBrightness)); - assert_eq!(u16::from(CommandCode::CharBrightness), 0x0005); - } - - #[test] - fn brightness() { - assert_eq!(CommandCode::try_from(0x0007), Ok(CommandCode::Brightness)); - assert_eq!(u16::from(CommandCode::Brightness), 0x0007); - } - - #[test] - fn hard_reset() { - assert_eq!(CommandCode::try_from(0x000b), Ok(CommandCode::HardReset)); - assert_eq!(u16::from(CommandCode::HardReset), 0x000b); - } - - #[test] - fn fade_out() { - assert_eq!(CommandCode::try_from(0x000d), Ok(CommandCode::FadeOut)); - assert_eq!(u16::from(CommandCode::FadeOut), 0x000d); - } - - #[test] - #[allow(deprecated)] - fn bitmap_legacy() { - assert_eq!(CommandCode::try_from(0x0010), Ok(CommandCode::BitmapLegacy)); - assert_eq!(u16::from(CommandCode::BitmapLegacy), 0x0010); - } - - #[test] - fn linear() { - assert_eq!(CommandCode::try_from(0x0012), Ok(CommandCode::BitmapLinear)); - assert_eq!(u16::from(CommandCode::BitmapLinear), 0x0012); - } - - #[test] - fn linear_and() { - assert_eq!(CommandCode::try_from(0x0014), Ok(CommandCode::BitmapLinearAnd)); - assert_eq!(u16::from(CommandCode::BitmapLinearAnd), 0x0014); - } - - #[test] - fn linear_xor() { - assert_eq!(CommandCode::try_from(0x0016), Ok(CommandCode::BitmapLinearXor)); - assert_eq!(u16::from(CommandCode::BitmapLinearXor), 0x0016); - } - - #[test] - #[cfg(feature = "compression_zlib")] - fn bitmap_win_zlib() { - assert_eq!(CommandCode::try_from(0x0017), Ok(CommandCode::BitmapLinearWinZlib)); - assert_eq!(u16::from(CommandCode::BitmapLinearWinZlib), 0x0017); - } - - #[test] - #[cfg(feature = "compression_bzip2")] - fn bitmap_win_bzip2() { - assert_eq!(CommandCode::try_from(0x0018), Ok(CommandCode::BitmapLinearWinBzip2)); - assert_eq!(u16::from(CommandCode::BitmapLinearWinBzip2), 0x0018); - } - - #[test] - #[cfg(feature = "compression_lzma")] - fn bitmap_win_lzma() { - assert_eq!(CommandCode::try_from(0x0019), Ok(CommandCode::BitmapLinearWinLzma)); - assert_eq!(u16::from(CommandCode::BitmapLinearWinLzma), 0x0019); - } - - #[test] - #[cfg(feature = "compression_zstd")] - fn bitmap_win_zstd() { - assert_eq!(CommandCode::try_from(0x001A), Ok(CommandCode::BitmapLinearWinZstd)); - assert_eq!(u16::from(CommandCode::BitmapLinearWinZstd), 0x001A); - } - - #[test] - fn bitmap_win_uncompressed() { - assert_eq!(CommandCode::try_from(0x0013), Ok(CommandCode::BitmapLinearWinUncompressed)); - assert_eq!(u16::from(CommandCode::BitmapLinearWinUncompressed), 0x0013); - } - - #[test] - fn utf8_data() { - assert_eq!(CommandCode::try_from(0x0020), Ok(CommandCode::Utf8Data)); - assert_eq!(u16::from(CommandCode::Utf8Data), 0x0020); - } - - #[test] - fn linear_or() { - assert_eq!(CommandCode::try_from(0x0015), Ok(CommandCode::BitmapLinearOr)); - assert_eq!(u16::from(CommandCode::BitmapLinearOr), 0x0015); - } -} \ No newline at end of file diff --git a/crates/servicepoint/src/compression.rs b/crates/servicepoint/src/compression.rs deleted file mode 100644 index 2e78073..0000000 --- a/crates/servicepoint/src/compression.rs +++ /dev/null @@ -1,115 +0,0 @@ -#[allow(unused)] -use std::io::{Read, Write}; - -#[cfg(feature = "compression_bzip2")] -use bzip2::read::{BzDecoder, BzEncoder}; -#[cfg(feature = "compression_zlib")] -use flate2::{FlushCompress, FlushDecompress, Status}; -#[cfg(feature = "compression_zstd")] -use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder}; - -use crate::{CompressionCode, Payload}; - -pub(crate) fn into_decompressed( - kind: CompressionCode, - payload: Payload, -) -> Option { - match kind { - CompressionCode::Uncompressed => Some(payload), - #[cfg(feature = "compression_zlib")] - CompressionCode::Zlib => { - let mut decompress = flate2::Decompress::new(true); - let mut buffer = [0u8; 10000]; - - let status = match decompress.decompress( - &payload, - &mut buffer, - FlushDecompress::Finish, - ) { - Err(_) => return None, - Ok(status) => status, - }; - - match status { - Status::Ok => None, - Status::BufError => None, - Status::StreamEnd => Some( - buffer[0..(decompress.total_out() as usize)].to_owned(), - ), - } - } - #[cfg(feature = "compression_bzip2")] - CompressionCode::Bzip2 => { - let mut decoder = BzDecoder::new(&*payload); - let mut decompressed = vec![]; - match decoder.read_to_end(&mut decompressed) { - Err(_) => None, - Ok(_) => Some(decompressed), - } - } - #[cfg(feature = "compression_lzma")] - CompressionCode::Lzma => match lzma::decompress(&payload) { - Err(_) => None, - Ok(decompressed) => Some(decompressed), - }, - #[cfg(feature = "compression_zstd")] - CompressionCode::Zstd => { - let mut decoder = match ZstdDecoder::new(&*payload) { - Err(_) => return None, - Ok(value) => value, - }; - let mut decompressed = vec![]; - match decoder.read_to_end(&mut decompressed) { - Err(_) => None, - Ok(_) => Some(decompressed), - } - } - } -} - -pub(crate) fn into_compressed( - kind: CompressionCode, - payload: Payload, -) -> Payload { - match kind { - CompressionCode::Uncompressed => payload, - #[cfg(feature = "compression_zlib")] - CompressionCode::Zlib => { - let mut compress = - flate2::Compress::new(flate2::Compression::fast(), true); - let mut buffer = [0u8; 10000]; - - match compress - .compress(&payload, &mut buffer, FlushCompress::Finish) - .expect("compress failed") - { - Status::Ok => panic!("buffer should be big enough"), - Status::BufError => panic!("BufError"), - Status::StreamEnd => {} - }; - buffer[..compress.total_out() as usize].to_owned() - } - #[cfg(feature = "compression_bzip2")] - CompressionCode::Bzip2 => { - let mut encoder = - BzEncoder::new(&*payload, bzip2::Compression::fast()); - let mut compressed = vec![]; - match encoder.read_to_end(&mut compressed) { - Err(err) => panic!("could not compress payload: {}", err), - Ok(_) => compressed, - } - } - #[cfg(feature = "compression_lzma")] - CompressionCode::Lzma => lzma::compress(&payload, 6).unwrap(), - #[cfg(feature = "compression_zstd")] - CompressionCode::Zstd => { - let mut encoder = - ZstdEncoder::new(vec![], zstd::DEFAULT_COMPRESSION_LEVEL) - .expect("could not create encoder"); - encoder - .write_all(&payload) - .expect("could not compress payload"); - encoder.finish().expect("could not finish encoding") - } - } -} diff --git a/crates/servicepoint/src/compression_code.rs b/crates/servicepoint/src/compression_code.rs deleted file mode 100644 index db25221..0000000 --- a/crates/servicepoint/src/compression_code.rs +++ /dev/null @@ -1,120 +0,0 @@ -/// Specifies the kind of compression to use. Availability depends on features. -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Command, CompressionCode, Origin, Bitmap}; -/// // create command without payload compression -/// # let pixels = Bitmap::max_sized(); -/// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Uncompressed); -/// -/// // create command with payload compressed with lzma and appropriate header flags -/// # let pixels = Bitmap::max_sized(); -/// _ = Command::BitmapLinearWin(Origin::ZERO, pixels, CompressionCode::Lzma); -/// ``` -#[repr(u16)] -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum CompressionCode { - /// no compression - Uncompressed = 0x0, - #[cfg(feature = "compression_zlib")] - /// compress using flate2 with zlib header - Zlib = 0x677a, - #[cfg(feature = "compression_bzip2")] - /// compress using bzip2 - Bzip2 = 0x627a, - #[cfg(feature = "compression_lzma")] - /// compress using lzma - Lzma = 0x6c7a, - #[cfg(feature = "compression_zstd")] - /// compress using Zstandard - Zstd = 0x7a73, -} - -impl From for u16 { - fn from(value: CompressionCode) -> Self { - value as u16 - } -} - -impl TryFrom for CompressionCode { - type Error = (); - - fn try_from(value: u16) -> Result { - match value { - value if value == CompressionCode::Uncompressed as u16 => { - Ok(CompressionCode::Uncompressed) - } - #[cfg(feature = "compression_zlib")] - value if value == CompressionCode::Zlib as u16 => { - Ok(CompressionCode::Zlib) - } - #[cfg(feature = "compression_bzip2")] - value if value == CompressionCode::Bzip2 as u16 => { - Ok(CompressionCode::Bzip2) - } - #[cfg(feature = "compression_lzma")] - value if value == CompressionCode::Lzma as u16 => { - Ok(CompressionCode::Lzma) - } - #[cfg(feature = "compression_zstd")] - value if value == CompressionCode::Zstd as u16 => { - Ok(CompressionCode::Zstd) - } - _ => Err(()), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn uncompressed() { - assert_eq!( - CompressionCode::try_from(0x0000), - Ok(CompressionCode::Uncompressed) - ); - assert_eq!(u16::from(CompressionCode::Uncompressed), 0x0000); - } - - #[test] - #[cfg(feature = "compression_zlib")] - fn zlib() { - assert_eq!( - CompressionCode::try_from(0x677a), - Ok(CompressionCode::Zlib) - ); - assert_eq!(u16::from(CompressionCode::Zlib), 0x677a); - } - - #[test] - #[cfg(feature = "compression_bzip2")] - fn bzip2() { - assert_eq!( - CompressionCode::try_from(0x627a), - Ok(CompressionCode::Bzip2) - ); - assert_eq!(u16::from(CompressionCode::Bzip2), 0x627a); - } - - #[test] - #[cfg(feature = "compression_lzma")] - fn lzma() { - assert_eq!( - CompressionCode::try_from(0x6c7a), - Ok(CompressionCode::Lzma) - ); - assert_eq!(u16::from(CompressionCode::Lzma), 0x6c7a); - } - - #[test] - #[cfg(feature = "compression_zstd")] - fn zstd() { - assert_eq!( - CompressionCode::try_from(0x7a73), - Ok(CompressionCode::Zstd) - ); - assert_eq!(u16::from(CompressionCode::Zstd), 0x7a73); - } -} diff --git a/crates/servicepoint/src/connection.rs b/crates/servicepoint/src/connection.rs deleted file mode 100644 index 417fd1d..0000000 --- a/crates/servicepoint/src/connection.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::packet::Packet; -use std::fmt::Debug; - -/// A connection to the display. -/// -/// Used to send [Packets][Packet] or [Commands][crate::Command]. -/// -/// # Examples -/// ```rust -/// let connection = servicepoint::Connection::open("127.0.0.1:2342") -/// .expect("connection failed"); -/// connection.send(servicepoint::Command::Clear) -/// .expect("send failed"); -/// ``` -#[derive(Debug)] -pub enum Connection { - /// A connection using the UDP protocol. - /// - /// Use this when sending commands directly to the display. - /// - /// Requires the feature "protocol_udp" which is enabled by default. - #[cfg(feature = "protocol_udp")] - Udp(std::net::UdpSocket), - - /// A connection using the WebSocket protocol. - /// - /// Note that you will need to forward the WebSocket messages via UDP to the display. - /// You can use [servicepoint-websocket-relay] for this. - /// - /// To create a new WebSocket automatically, use [Connection::open_websocket]. - /// - /// Requires the feature "protocol_websocket" which is disabled by default. - /// - /// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay - #[cfg(feature = "protocol_websocket")] - WebSocket( - std::sync::Mutex< - tungstenite::WebSocket< - tungstenite::stream::MaybeTlsStream, - >, - >, - ), - - /// A fake connection for testing that does not actually send anything. - Fake, -} - -#[derive(Debug, thiserror::Error)] -pub enum SendError { - #[error("IO error occurred while sending")] - IoError(#[from] std::io::Error), - #[cfg(feature = "protocol_websocket")] - #[error("WebSocket error occurred while sending")] - WebsocketError(#[from] tungstenite::Error), -} - -impl Connection { - /// Open a new UDP socket and connect to the provided host. - /// - /// Note that this is UDP, which means that the open call can succeed even if the display is unreachable. - /// - /// The address of the display in CCCB is `172.23.42.29:2342`. - /// - /// # Errors - /// - /// Any errors resulting from binding the udp socket. - /// - /// # Examples - /// ```rust - /// let connection = servicepoint::Connection::open("127.0.0.1:2342") - /// .expect("connection failed"); - /// ``` - #[cfg(feature = "protocol_udp")] - pub fn open( - addr: impl std::net::ToSocketAddrs + Debug, - ) -> std::io::Result { - log::info!("connecting to {addr:?}"); - let socket = std::net::UdpSocket::bind("0.0.0.0:0")?; - socket.connect(addr)?; - Ok(Self::Udp(socket)) - } - - /// Open a new WebSocket and connect to the provided host. - /// - /// Requires the feature "protocol_websocket" which is disabled by default. - /// - /// # Examples - /// - /// ```no_run - /// use tungstenite::http::Uri; - /// use servicepoint::{Command, Connection}; - /// let uri = "ws://localhost:8080".parse().unwrap(); - /// let mut connection = Connection::open_websocket(uri) - /// .expect("could not connect"); - /// connection.send(Command::Clear) - /// .expect("send failed"); - /// ``` - #[cfg(feature = "protocol_websocket")] - pub fn open_websocket( - uri: tungstenite::http::Uri, - ) -> tungstenite::Result { - use tungstenite::{ - client::IntoClientRequest, connect, ClientRequestBuilder, - }; - - log::info!("connecting to {uri:?}"); - - let request = ClientRequestBuilder::new(uri).into_client_request()?; - let (sock, _) = connect(request)?; - Ok(Self::WebSocket(std::sync::Mutex::new(sock))) - } - - /// Send something packet-like to the display. Usually this is in the form of a Command. - /// - /// # Arguments - /// - /// - `packet`: the packet-like to send - /// - /// returns: true if packet was sent, otherwise false - /// - /// # Examples - /// - /// ```rust - /// let connection = servicepoint::Connection::Fake; - /// // turn off all pixels on display - /// connection.send(servicepoint::Command::Clear) - /// .expect("send failed"); - /// ``` - pub fn send(&self, packet: impl Into) -> Result<(), SendError> { - let packet = packet.into(); - log::debug!("sending {packet:?}"); - let data: Vec = packet.into(); - match self { - #[cfg(feature = "protocol_udp")] - Connection::Udp(socket) => { - socket - .send(&data) - .map_err(SendError::IoError) - .map(move |_| ()) // ignore Ok value - } - #[cfg(feature = "protocol_websocket")] - Connection::WebSocket(socket) => { - let mut socket = socket.lock().unwrap(); - socket - .send(tungstenite::Message::Binary(data.into())) - .map_err(SendError::WebsocketError) - } - Connection::Fake => { - let _ = data; - Ok(()) - } - } - } -} - -impl Drop for Connection { - fn drop(&mut self) { - #[cfg(feature = "protocol_websocket")] - if let Connection::WebSocket(sock) = self { - _ = sock.try_lock().map(move |mut sock| sock.close(None)); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn send_fake() { - 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::Fake.send(packet).unwrap() - } -} diff --git a/crates/servicepoint/src/constants.rs b/crates/servicepoint/src/constants.rs deleted file mode 100644 index 95c8684..0000000 --- a/crates/servicepoint/src/constants.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::time::Duration; - -/// size of a single tile in one dimension -pub const TILE_SIZE: usize = 8; - -/// Display tile count in the x-direction -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; -/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); -/// ``` -pub const TILE_WIDTH: usize = 56; - -/// Display tile count in the y-direction -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH}; -/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT); -/// ``` -pub const TILE_HEIGHT: usize = 20; - -/// Display width in pixels -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; -/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); -/// ``` -pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; - -/// Display height in pixels -/// -/// # Examples -/// -/// ```rust -/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap}; -/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); -/// ``` -pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; - -/// pixel count on whole screen -pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; - -/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. -/// -/// # Examples -/// -/// ```rust -/// # use std::time::Instant; -/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap}; -/// # let connection = servicepoint::Connection::Fake; -/// # let pixels = Bitmap::max_sized(); -/// loop { -/// let start = Instant::now(); -/// -/// // Change pixels here -/// -/// connection.send(Command::BitmapLinearWin( -/// Origin::new(0,0), -/// pixels, -/// CompressionCode::Lzma -/// )) -/// .expect("send failed"); -/// -/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby -/// std::thread::sleep(FRAME_PACING - start.elapsed()); -/// # break; // prevent doctest from hanging -/// } -/// ``` -pub const FRAME_PACING: Duration = Duration::from_millis(30); diff --git a/crates/servicepoint/src/cp437.rs b/crates/servicepoint/src/cp437.rs deleted file mode 100644 index cb9b945..0000000 --- a/crates/servicepoint/src/cp437.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::collections::HashMap; - -/// Contains functions to convert between UTF-8 and Codepage 437. -/// -/// See -pub struct Cp437Converter; - -/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters -/// -/// Mostly follows CP437, except 0x0A, which is kept for use as line ending. -/// -/// See -/// -/// Mostly copied from . License: GPL-3.0 -#[rustfmt::skip] -const CP437_TO_UTF8: [char; 256] = [ - /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', - /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼', - /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', - /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', - /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - /* 5X */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_', - /* 6X */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - /* 7X */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂', - /* 8X */ 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å', - /* 9X */ 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ', - /* AX */ 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»', - /* BX */ '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐', - /* CX */ '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧', - /* DX */ '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀', - /* EX */ 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩', - /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', -]; -static UTF8_TO_CP437: once_cell::sync::Lazy> = - once_cell::sync::Lazy::new(|| { - let pairs = CP437_TO_UTF8 - .iter() - .enumerate() - .map(move |(index, char)| (*char, index as u8)); - HashMap::from_iter(pairs) - }); - -impl Cp437Converter { - const MISSING_CHAR_CP437: u8 = 0x3F; // '?' - - /// Convert the provided bytes to UTF-8. - pub fn cp437_to_str(cp437: &[u8]) -> String { - cp437 - .iter() - .map(move |char| Self::cp437_to_char(*char)) - .collect() - } - - /// Convert a single CP-437 character to UTF-8. - pub fn cp437_to_char(cp437: u8) -> char { - CP437_TO_UTF8[cp437 as usize] - } - - /// Convert the provided text to CP-437 bytes. - /// - /// Characters that are not available are mapped to '?'. - pub fn str_to_cp437(utf8: &str) -> Vec { - utf8.chars().map(Self::char_to_cp437).collect() - } - - /// Convert a single UTF-8 character to CP-437. - pub fn char_to_cp437(utf8: char) -> u8 { - *UTF8_TO_CP437 - .get(&utf8) - .unwrap_or(&Self::MISSING_CHAR_CP437) - } -} - -#[cfg(test)] -mod tests_feature_cp437 { - use super::*; - - #[test] - fn convert_str() { - // test text from https://int10h.org/oldschool-pc-fonts/fontlist/font?ibm_bios - let utf8 = r#"A quick brown fox jumps over the lazy dog. - 0123456789 ¿?¡!`'"., <>()[]{} &@%*^#$\/ - - * Wieniläinen sioux'ta puhuva ökyzombie diggaa Åsan roquefort-tacoja. - * Ça me fait peur de fêter noël là, sur cette île bizarroïde où une mère et sa môme essaient de me tuer avec un gâteau à la cigüe brûlé. - * Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich. - * El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro. - - ┌─┬─┐ ╔═╦═╗ ╒═╤═╕ ╓─╥─╖ - │ │ │ ║ ║ ║ │ │ │ ║ ║ ║ - ├─┼─┤ ╠═╬═╣ ╞═╪═╡ ╟─╫─╢ - └─┴─┘ ╚═╩═╝ ╘═╧═╛ ╙─╨─╜ - - ░░░░░ ▐▀█▀▌ .·∙•○°○•∙·. - ▒▒▒▒▒ ▐ █ ▌ ☺☻ ♥♦♣♠ ♪♫☼ - ▓▓▓▓▓ ▐▀█▀▌ $ ¢ £ ¥ ₧ - █████ ▐▄█▄▌ ◄►▲▼ ←→↑↓↕↨ - - ⌠ - │dx ≡ Σ √x²ⁿ·δx - ⌡"#; - - let cp437 = Cp437Converter::str_to_cp437(utf8); - let actual = Cp437Converter::cp437_to_str(&cp437); - assert_eq!(utf8, actual) - } - - #[test] - fn convert_invalid() { - assert_eq!( - Cp437Converter::cp437_to_char(Cp437Converter::char_to_cp437('😜')), - '?' - ); - } -} diff --git a/crates/servicepoint/src/cp437_grid.rs b/crates/servicepoint/src/cp437_grid.rs deleted file mode 100644 index 99506bc..0000000 --- a/crates/servicepoint/src/cp437_grid.rs +++ /dev/null @@ -1,163 +0,0 @@ -/// A grid containing codepage 437 characters. -/// -/// The encoding is currently not enforced. -pub type Cp437Grid = crate::value_grid::ValueGrid; - -/// 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 { - /// Load an ASCII-only [&str] into a [Cp437Grid] of specified width. - /// - /// # Panics - /// - /// - for width == 0 - /// - on empty strings - pub fn load_ascii( - value: &str, - width: usize, - wrap: bool, - ) -> Result { - assert!(width > 0); - assert!(!value.is_empty()); - - let mut chars = { - let mut x = 0; - let mut y = 0; - - for (index, char) in value.chars().enumerate() { - if !char.is_ascii() { - return Err(InvalidCharError { index, char }); - } - - let is_lf = char == '\n'; - if is_lf || (wrap && x == width) { - y += 1; - x = 0; - if is_lf { - continue; - } - } - - x += 1; - } - - Cp437Grid::new(width, y + 1) - }; - - let mut x = 0; - let mut y = 0; - for char in value.chars().map(move |c| c as u8) { - let is_lf = char == b'\n'; - if is_lf || (wrap && x == width) { - y += 1; - x = 0; - if is_lf { - continue; - } - } - - if wrap || x < width { - chars.set(x, y, char); - } - x += 1; - } - - Ok(chars) - } -} - -use crate::Grid; -#[allow(unused)] // depends on features -pub use feature_cp437::*; - -#[cfg(feature = "cp437")] -mod feature_cp437 { - use super::*; - use crate::{CharGrid, Cp437Converter}; - - impl From<&Cp437Grid> for CharGrid { - fn from(value: &Cp437Grid) -> Self { - value.map(Cp437Converter::cp437_to_char) - } - } - - impl From for CharGrid { - fn from(value: Cp437Grid) -> Self { - Self::from(&value) - } - } - - impl From<&CharGrid> for Cp437Grid { - fn from(value: &CharGrid) -> Self { - value.map(Cp437Converter::char_to_cp437) - } - } - - impl From for Cp437Grid { - fn from(value: CharGrid) -> Self { - Self::from(&value) - } - } -} -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn load_ascii_nowrap() { - let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] - .map(move |c| c as u8); - let expected = Cp437Grid::load(5, 2, &chars); - - let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap(); - // comma will be removed because line is too long and wrap is off - assert_eq!(actual, expected); - } - - #[test] - fn load_ascii_wrap() { - let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] - .map(move |c| c as u8); - let expected = Cp437Grid::load(5, 2, &chars); - - let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap(); - // line break will be added - assert_eq!(actual, expected); - } - - #[test] - fn load_ascii_invalid() { - assert_eq!( - Err(InvalidCharError { - char: '🥶', - index: 2 - }), - Cp437Grid::load_ascii("?#🥶42", 3, false) - ); - } -} - -#[cfg(test)] -#[cfg(feature = "cp437")] -mod tests_feature_cp437 { - use super::*; - use crate::CharGrid; - - #[test] - fn round_trip_cp437() { - let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); - let cp437 = Cp437Grid::from(utf8.clone()); - let actual = CharGrid::from(cp437); - assert_eq!(actual, utf8); - } -} diff --git a/crates/servicepoint/src/data_ref.rs b/crates/servicepoint/src/data_ref.rs deleted file mode 100644 index b8ff624..0000000 --- a/crates/servicepoint/src/data_ref.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// A trait for getting the underlying raw byte slices of data containers. -/// -/// The expectation is that you can create an equal instance with this data given the additional -/// metadata needed. -pub trait DataRef { - /// Get the underlying bytes writable. - /// - /// Note that depending on the struct this is implemented on, writing invalid values here might - /// lead to panics later in the lifetime of the program or on the receiving side. - fn data_ref_mut(&mut self) -> &mut [T]; - - /// Get the underlying bytes read-only. - fn data_ref(&self) -> &[T]; -} diff --git a/crates/servicepoint/src/grid.rs b/crates/servicepoint/src/grid.rs deleted file mode 100644 index 68fe102..0000000 --- a/crates/servicepoint/src/grid.rs +++ /dev/null @@ -1,84 +0,0 @@ -/// A two-dimensional grid of `T` -pub trait Grid { - /// Sets the value at the specified position - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: T); - - /// Get the current value at the specified position - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn get(&self, x: usize, y: usize) -> T; - - /// Get the current value at the specified position if the position is inside of bounds - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// returns: Value at position or None - fn get_optional(&self, x: isize, y: isize) -> Option { - if self.is_in_bounds(x, y) { - Some(self.get(x as usize, y as usize)) - } else { - None - } - } - - /// Sets the value at the specified position if the position is inside of bounds - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// returns: the old value or None - fn set_optional(&mut self, x: isize, y: isize, value: T) -> bool { - if self.is_in_bounds(x, y) { - self.set(x as usize, y as usize, value); - true - } else { - false - } - } - - /// Sets all cells in the grid to the specified value - fn fill(&mut self, value: T); - - /// the size in x-direction - fn width(&self) -> usize; - - /// the height in y-direction - fn height(&self) -> usize; - - /// Checks whether the specified signed position is in grid bounds - fn is_in_bounds(&self, x: isize, y: isize) -> bool { - x >= 0 - && x < self.width() as isize - && y >= 0 - && y < self.height() as isize - } - - /// Asserts that the specified unsigned position is in grid bounds. - /// - /// # Panics - /// - /// When the specified position is out of bounds for this grid. - fn assert_in_bounds(&self, x: usize, y: usize) { - let width = self.width(); - assert!(x < width, "cannot access index [{x}, {y}] because x is outside of bounds [0..{width})"); - let height = self.height(); - assert!(y < height, "cannot access index [{x}, {y}] because x is outside of bounds [0..{height})"); - } -} diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs deleted file mode 100644 index 790751a..0000000 --- a/crates/servicepoint/src/lib.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Abstractions for the UDP protocol of the CCCB servicepoint display. -//! -//! Your starting point is a [Connection] to the display. -//! With a connection, you can send [Command]s. -//! When received, the display will update the state of its pixels. -//! -//! # Examples -//! -//! ### Clear display -//! -//! ```rust -//! use servicepoint::{Connection, Command}; -//! -//! // establish a connection -//! let connection = Connection::open("127.0.0.1:2342") -//! .expect("connection failed"); -//! -//! // turn off all pixels on display -//! connection.send(Command::Clear) -//! .expect("send failed"); -//! ``` -//! -//! ### Set all pixels to on -//! -//! ```rust -//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap}; -//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed"); -//! // turn on all pixels in a grid -//! let mut pixels = Bitmap::max_sized(); -//! pixels.fill(true); -//! -//! // create command to send pixels -//! let command = Command::BitmapLinearWin( -//! servicepoint::Origin::ZERO, -//! pixels, -//! CompressionCode::Uncompressed -//! ); -//! -//! // send command to display -//! connection.send(command).expect("send failed"); -//! ``` -//! -//! ### Send text -//! -//! ```rust -//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap, CharGrid}; -//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed"); -//! // create a text grid -//! let mut grid = CharGrid::from("Hello\nCCCB?"); -//! // modify the grid -//! grid.set(grid.width() - 1, 1, '!'); -//! // create the command to send the data -//! let command = Command::Utf8Data(servicepoint::Origin::ZERO, grid); -//! // send command to display -//! connection.send(command).expect("send failed"); -//! ``` - -pub use crate::bit_vec::{bitvec, BitVec}; -pub use crate::bitmap::Bitmap; -pub use crate::brightness::Brightness; -pub use crate::brightness_grid::BrightnessGrid; -pub use crate::byte_grid::ByteGrid; -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::constants::*; -pub use crate::cp437::Cp437Converter; -pub use crate::cp437_grid::Cp437Grid; -pub use crate::data_ref::DataRef; -pub use crate::grid::Grid; -pub use crate::origin::{Origin, Pixels, Tiles}; -pub use crate::packet::{Header, Packet, Payload}; -pub use crate::value_grid::{ - IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid, -}; - -mod bit_vec; -mod bitmap; -mod brightness; -mod brightness_grid; -mod byte_grid; -mod char_grid; -mod command; -mod command_code; -mod compression; -mod compression_code; -mod connection; -mod constants; -mod cp437_grid; -mod data_ref; -mod grid; -mod origin; -mod packet; -mod value_grid; - -#[cfg(feature = "cp437")] -mod cp437; - -// include README.md in doctest -#[doc = include_str!("../README.md")] -#[cfg(doctest)] -pub struct ReadmeDocTests; diff --git a/crates/servicepoint/src/origin.rs b/crates/servicepoint/src/origin.rs deleted file mode 100644 index 345b89e..0000000 --- a/crates/servicepoint/src/origin.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::TILE_SIZE; -use std::marker::PhantomData; - -/// An origin marks the top left position of a window sent to the display. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Origin { - /// position in the width direction - pub x: usize, - /// position in the height direction - pub y: usize, - phantom_data: PhantomData, -} - -impl Origin { - /// Top-left. Equivalent to `Origin::ZERO`. - pub const ZERO: Self = Self { - x: 0, - y: 0, - phantom_data: PhantomData, - }; - - /// Create a new [Origin] instance for the provided position. - pub fn new(x: usize, y: usize) -> Self { - Self { - x, - y, - phantom_data: PhantomData, - } - } -} - -impl std::ops::Add> for Origin { - type Output = Origin; - - fn add(self, rhs: Origin) -> Self::Output { - Origin { - x: self.x + rhs.x, - y: self.y + rhs.y, - phantom_data: PhantomData, - } - } -} - -pub trait DisplayUnit {} - -/// Marks something to be measured in number of pixels. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Pixels(); - -/// Marks something to be measured in number of iles. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Tiles(); - -impl DisplayUnit for Pixels {} - -impl DisplayUnit for Tiles {} - -impl From<&Origin> for Origin { - fn from(value: &Origin) -> Self { - Self { - x: value.x * TILE_SIZE, - y: value.y * TILE_SIZE, - phantom_data: PhantomData, - } - } -} - -impl TryFrom<&Origin> for Origin { - type Error = (); - - fn try_from(value: &Origin) -> Result { - let (x, x_rem) = (value.x / TILE_SIZE, value.x % TILE_SIZE); - if x_rem != 0 { - return Err(()); - } - let (y, y_rem) = (value.y / TILE_SIZE, value.y % TILE_SIZE); - if y_rem != 0 { - return Err(()); - } - - Ok(Self { - x, - y, - phantom_data: PhantomData, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn origin_tile_to_pixel() { - let tile: Origin = Origin::new(1, 2); - let actual: Origin = Origin::from(&tile); - let expected: Origin = Origin::new(8, 16); - assert_eq!(actual, expected); - } - - #[test] - fn origin_pixel_to_tile() { - let pixel: Origin = Origin::new(8, 16); - let actual: Origin = Origin::try_from(&pixel).unwrap(); - let expected: Origin = Origin::new(1, 2); - assert_eq!(actual, expected); - } - - #[test] - #[should_panic] - fn origin_pixel_to_tile_fail_y() { - let pixel: Origin = Origin::new(8, 15); - let _: Origin = Origin::try_from(&pixel).unwrap(); - } - - #[test] - #[should_panic] - fn origin_pixel_to_tile_fail_x() { - let pixel: Origin = Origin::new(7, 16); - let _: Origin = Origin::try_from(&pixel).unwrap(); - } -} diff --git a/crates/servicepoint/src/packet.rs b/crates/servicepoint/src/packet.rs deleted file mode 100644 index 099c50c..0000000 --- a/crates/servicepoint/src/packet.rs +++ /dev/null @@ -1,358 +0,0 @@ -//! Raw packet manipulation. -//! -//! Should probably only be used directly to use features not exposed by the library. -//! -//! # Examples -//! -//! Converting a packet to a command and back: -//! -//! ```rust -//! use servicepoint::{Command, Packet}; -//! # let command = Command::Clear; -//! let packet: Packet = command.into(); -//! let command: Command = Command::try_from(packet).expect("could not read command from packet"); -//! ``` -//! -//! Converting a packet to bytes and back: -//! -//! ```rust -//! use servicepoint::{Command, Packet}; -//! # let command = Command::Clear; -//! # let packet: Packet = command.into(); -//! let bytes: Vec = packet.into(); -//! let packet = Packet::try_from(bytes).expect("could not read packet from bytes"); -//! ``` - -use crate::command_code::CommandCode; -use crate::compression::into_compressed; -use crate::{ - Bitmap, Command, CompressionCode, Grid, Offset, Origin, Pixels, Tiles, - TILE_SIZE, -}; -use std::mem::size_of; - -/// A raw header. -/// -/// The header specifies the kind of command, the size of the payload and where to display the -/// payload, where applicable. -/// -/// Because the meaning of most fields depend on the command, there are no speaking names for them. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Header { - /// The first two bytes specify which command this packet represents. - pub command_code: u16, - /// First command-specific value - pub a: u16, - /// Second command-specific value - pub b: u16, - /// Third command-specific value - pub c: u16, - /// Fourth command-specific value - pub d: u16, -} - -/// The raw payload. -/// -/// Should probably only be used directly to use features not exposed by the library. -pub type Payload = Vec; - -/// The raw packet. -/// -/// Contents should probably only be used directly to use features not exposed by the library. -/// -/// You may want to use [Command] instead. -/// -/// -#[derive(Clone, Debug, PartialEq)] -pub struct Packet { - /// Meta-information for the packed command - pub header: Header, - /// The data for the packed command - pub payload: Payload, -} - -impl From for Vec { - /// Turn the packet into raw bytes ready to send - fn from(value: Packet) -> Self { - let Packet { - header: - Header { - command_code: mode, - a, - b, - c, - d, - }, - payload, - } = value; - - let mut packet = vec![0u8; 10 + payload.len()]; - packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode)); - packet[2..=3].copy_from_slice(&u16::to_be_bytes(a)); - packet[4..=5].copy_from_slice(&u16::to_be_bytes(b)); - packet[6..=7].copy_from_slice(&u16::to_be_bytes(c)); - packet[8..=9].copy_from_slice(&u16::to_be_bytes(d)); - - packet[10..].copy_from_slice(&payload); - - packet - } -} - -impl TryFrom<&[u8]> for Packet { - type Error = (); - - /// Tries to interpret the bytes as a [Packet]. - /// - /// returns: `Error` if slice is not long enough to be a [Packet] - fn try_from(value: &[u8]) -> Result { - if value.len() < size_of::
() { - return Err(()); - } - - let header = { - let command_code = Self::u16_from_be_slice(&value[0..=1]); - let a = Self::u16_from_be_slice(&value[2..=3]); - let b = Self::u16_from_be_slice(&value[4..=5]); - let c = Self::u16_from_be_slice(&value[6..=7]); - let d = Self::u16_from_be_slice(&value[8..=9]); - Header { - command_code, - a, - b, - c, - d, - } - }; - let payload = value[10..].to_vec(); - - Ok(Packet { header, payload }) - } -} - -impl TryFrom> for Packet { - type Error = (); - - fn try_from(value: Vec) -> Result { - Self::try_from(value.as_slice()) - } -} - -impl From for Packet { - /// Move the [Command] into a [Packet] instance for sending. - #[allow(clippy::cast_possible_truncation)] - fn from(value: Command) -> Self { - match value { - Command::Clear => Self::command_code_only(CommandCode::Clear), - Command::FadeOut => Self::command_code_only(CommandCode::FadeOut), - Command::HardReset => { - Self::command_code_only(CommandCode::HardReset) - } - #[allow(deprecated)] - Command::BitmapLegacy => { - Self::command_code_only(CommandCode::BitmapLegacy) - } - Command::CharBrightness(origin, grid) => { - Self::origin_grid_to_packet( - origin, - grid, - CommandCode::CharBrightness, - ) - } - Command::Brightness(brightness) => Packet { - header: Header { - command_code: CommandCode::Brightness.into(), - a: 0x00000, - b: 0x0000, - c: 0x0000, - d: 0x0000, - }, - payload: vec![brightness.into()], - }, - Command::BitmapLinearWin(origin, pixels, compression) => { - Self::bitmap_win_into_packet(origin, pixels, compression) - } - Command::BitmapLinear(offset, bits, compression) => { - Self::bitmap_linear_into_packet( - CommandCode::BitmapLinear, - offset, - compression, - bits.into(), - ) - } - Command::BitmapLinearAnd(offset, bits, compression) => { - Self::bitmap_linear_into_packet( - CommandCode::BitmapLinearAnd, - offset, - compression, - bits.into(), - ) - } - Command::BitmapLinearOr(offset, bits, compression) => { - Self::bitmap_linear_into_packet( - CommandCode::BitmapLinearOr, - offset, - compression, - bits.into(), - ) - } - Command::BitmapLinearXor(offset, bits, compression) => { - Self::bitmap_linear_into_packet( - CommandCode::BitmapLinearXor, - offset, - compression, - bits.into(), - ) - } - Command::Cp437Data(origin, grid) => Self::origin_grid_to_packet( - origin, - grid, - CommandCode::Cp437Data, - ), - Command::Utf8Data(origin, grid) => { - Self::origin_grid_to_packet(origin, grid, CommandCode::Utf8Data) - } - } - } -} - -impl Packet { - /// Helper method for `BitmapLinear*`-Commands into [Packet] - #[allow(clippy::cast_possible_truncation)] - fn bitmap_linear_into_packet( - command: CommandCode, - offset: Offset, - compression: CompressionCode, - payload: Vec, - ) -> Packet { - let length = payload.len() as u16; - let payload = into_compressed(compression, payload); - Packet { - header: Header { - command_code: command.into(), - a: offset as u16, - b: length, - c: compression.into(), - d: 0, - }, - payload, - } - } - - #[allow(clippy::cast_possible_truncation)] - fn bitmap_win_into_packet( - origin: Origin, - pixels: Bitmap, - compression: CompressionCode, - ) -> Packet { - debug_assert_eq!(origin.x % 8, 0); - debug_assert_eq!(pixels.width() % 8, 0); - - let tile_x = (origin.x / TILE_SIZE) as u16; - let tile_w = (pixels.width() / TILE_SIZE) as u16; - let pixel_h = pixels.height() as u16; - let payload = into_compressed(compression, pixels.into()); - let command = match compression { - CompressionCode::Uncompressed => { - CommandCode::BitmapLinearWinUncompressed - } - #[cfg(feature = "compression_zlib")] - CompressionCode::Zlib => CommandCode::BitmapLinearWinZlib, - #[cfg(feature = "compression_bzip2")] - CompressionCode::Bzip2 => CommandCode::BitmapLinearWinBzip2, - #[cfg(feature = "compression_lzma")] - CompressionCode::Lzma => CommandCode::BitmapLinearWinLzma, - #[cfg(feature = "compression_zstd")] - CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, - }; - - Packet { - header: Header { - command_code: command.into(), - a: tile_x, - b: origin.y as u16, - c: tile_w, - d: pixel_h, - }, - payload, - } - } - - /// Helper method for creating empty packets only containing the command code - fn command_code_only(code: CommandCode) -> Packet { - Packet { - header: Header { - command_code: code.into(), - a: 0x0000, - b: 0x0000, - c: 0x0000, - d: 0x0000, - }, - payload: vec![], - } - } - - fn u16_from_be_slice(slice: &[u8]) -> u16 { - let mut bytes = [0u8; 2]; - bytes[0] = slice[0]; - bytes[1] = slice[1]; - u16::from_be_bytes(bytes) - } - - fn origin_grid_to_packet( - origin: Origin, - grid: impl Grid + Into, - command_code: CommandCode, - ) -> Packet { - Packet { - header: Header { - command_code: command_code.into(), - a: origin.x as u16, - b: origin.y as u16, - c: grid.width() as u16, - d: grid.height() as u16, - }, - payload: grid.into(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn round_trip() { - let p = Packet { - header: Header { - command_code: 0, - a: 1, - b: 2, - c: 3, - d: 4, - }, - payload: vec![42u8; 23], - }; - let data: Vec = p.into(); - let p = Packet::try_from(data).unwrap(); - assert_eq!( - p, - Packet { - header: Header { - command_code: 0, - a: 1, - b: 2, - c: 3, - d: 4 - }, - payload: vec![42u8; 23] - } - ); - } - - #[test] - fn too_small() { - let data = vec![0u8; 4]; - assert_eq!(Packet::try_from(data.as_slice()), Err(())) - } -} diff --git a/crates/servicepoint/src/value_grid.rs b/crates/servicepoint/src/value_grid.rs deleted file mode 100644 index b9cfea5..0000000 --- a/crates/servicepoint/src/value_grid.rs +++ /dev/null @@ -1,590 +0,0 @@ -use std::fmt::Debug; -use std::slice::{Iter, IterMut}; - -use crate::*; - -/// A type that can be stored in a [ValueGrid], e.g. [char], [u8]. -pub trait Value: Sized + Default + Copy + Clone + Debug {} -impl Value for T {} - -/// A 2D grid of values. -/// -/// The memory layout is the one the display expects in [Command]s. -/// -/// This structure can be used with any type that implements the [Value] trait. -/// You can also use the concrete type aliases provided in this crate, e.g. [CharGrid] and [ByteGrid]. -#[derive(Debug, Clone, PartialEq)] -pub struct ValueGrid { - width: usize, - height: usize, - data: Vec, -} - -/// Error type for methods that change a whole column or row at once -#[derive(thiserror::Error, Debug, PartialEq)] -pub enum SetValueSeriesError { - #[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 ValueGrid { - /// Creates a new [ValueGrid] with the specified dimensions. - /// - /// # Arguments - /// - /// - width: size in x-direction - /// - height: size in y-direction - /// - /// returns: [ValueGrid] initialized to default value. - pub fn new(width: usize, height: usize) -> Self { - Self { - data: vec![Default::default(); width * height], - width, - height, - } - } - - /// Loads a [ValueGrid] with the specified dimensions from the provided data. - /// - /// returns: [ValueGrid] that contains a copy of the provided data - /// - /// # Panics - /// - /// - when the dimensions and data size do not match exactly. - #[must_use] - pub fn load(width: usize, height: usize, data: &[T]) -> Self { - assert_eq!( - width * height, - data.len(), - "dimension mismatch for data {data:?}" - ); - Self { - data: Vec::from(data), - width, - height, - } - } - - /// Creates a [ValueGrid] with the specified width from the provided data without copying it. - /// - /// returns: [ValueGrid] that contains the provided data. - /// - /// # Panics - /// - /// - when the data size is not dividable by the width. - #[must_use] - pub fn from_vec(width: usize, data: Vec) -> Self { - let len = data.len(); - let height = len / width; - assert_eq!(0, len % width, "dimension mismatch - len {len} is not dividable by {width}"); - Self { data, width, height } - } - - /// Loads a [ValueGrid] with the specified width from the provided data, wrapping to as many rows as needed. - /// - /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError]. - /// - /// # Examples - /// - /// ``` - /// # use servicepoint::ValueGrid; - /// let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap(); - /// ``` - pub fn wrap( - width: usize, - data: &[T], - ) -> Result { - let len = data.len(); - if len % width != 0 { - return Err(TryLoadValueGridError::InvalidDimensions); - } - Ok(Self::load(width, len / width, data)) - } - - /// Loads a [ValueGrid] with the specified dimensions from the provided data. - /// - /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError]. - pub fn try_load( - width: usize, - height: usize, - data: Vec, - ) -> Result { - if width * height != data.len() { - return Err(TryLoadValueGridError::InvalidDimensions); - } - - Ok(Self { - data, - width, - height, - }) - } - - /// Iterate over all cells in [ValueGrid]. - /// - /// Order is equivalent to the following loop: - /// ``` - /// # use servicepoint::{ByteGrid, Grid}; - /// # let grid = ByteGrid::new(2,2); - /// for y in 0..grid.height() { - /// for x in 0..grid.width() { - /// grid.get(x, y); - /// } - /// } - /// ``` - pub fn iter(&self) -> Iter { - self.data.iter() - } - - /// Iterate over all rows in [ValueGrid] top to bottom. - pub fn iter_rows(&self) -> IterGridRows { - IterGridRows { - byte_grid: self, - row: 0, - } - } - - /// Returns an iterator that allows modifying each value. - /// - /// The iterator yields all cells from top left to bottom right. - pub fn iter_mut(&mut self) -> IterMut { - self.data.iter_mut() - } - - /// Get a mutable reference to the current value at the specified position. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut T { - self.assert_in_bounds(x, y); - &mut self.data[x + y * self.width] - } - - /// Get a mutable reference to the current value at the specified position if position is in bounds. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell - /// - /// returns: Reference to cell or None - pub fn get_ref_mut_optional( - &mut self, - x: isize, - y: isize, - ) -> Option<&mut T> { - if self.is_in_bounds(x, y) { - Some(&mut self.data[x as usize + y as usize * self.width]) - } else { - None - } - } - - /// Convert between ValueGrid types. - /// - /// See also [Iterator::map]. - /// - /// # Examples - /// - /// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command]. - /// ``` - /// # fn foo(grid: &mut ByteGrid) {} - /// # use servicepoint::{Brightness, BrightnessGrid, ByteGrid, Command, Origin, TILE_HEIGHT, TILE_WIDTH}; - /// let mut grid: ByteGrid = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT); - /// foo(&mut grid); - /// let grid: BrightnessGrid = grid.map(Brightness::saturating_from); - /// let command = Command::CharBrightness(Origin::ZERO, grid); - /// ``` - /// [Brightness]: [crate::Brightness] - /// [Command]: [crate::Command] - pub fn map(&self, f: F) -> ValueGrid - where - TConverted: Value, - F: Fn(T) -> TConverted, - { - let data = self - .data_ref() - .iter() - .map(|elem| f(*elem)) - .collect::>(); - ValueGrid::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> { - 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> { - 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<(), SetValueSeriesError> { - if col.len() != self.height() { - return Err(SetValueSeriesError::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(SetValueSeriesError::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<(), SetValueSeriesError> { - let width = self.width(); - if row.len() != width { - return Err(SetValueSeriesError::InvalidLength { - expected: width, - actual: row.len(), - }); - } - - let chunk = match self.data.chunks_exact_mut(width).nth(y) { - Some(row) => row, - None => { - return Err(SetValueSeriesError::OutOfBounds { - size: self.height(), - index: y, - }) - } - }; - - chunk.copy_from_slice(row); - Ok(()) - } -} - -/// Errors that can occur when loading a grid -#[derive(Debug, thiserror::Error, PartialEq)] -pub enum TryLoadValueGridError { - #[error("The provided dimensions do not match with the data size")] - /// The provided dimensions do not match with the data size - InvalidDimensions, -} - -impl Grid for ValueGrid { - /// Sets the value of the cell at the specified position in the `ValueGrid. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell - /// - `value`: the value to write to the cell - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: T) { - self.assert_in_bounds(x, y); - self.data[x + y * self.width] = value; - } - - /// Gets the current value at the specified position. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn get(&self, x: usize, y: usize) -> T { - self.assert_in_bounds(x, y); - self.data[x + y * self.width] - } - - fn fill(&mut self, value: T) { - self.data.fill(value); - } - - fn width(&self) -> usize { - self.width - } - - fn height(&self) -> usize { - self.height - } -} - -impl DataRef for ValueGrid { - /// Get the underlying byte rows mutable - fn data_ref_mut(&mut self) -> &mut [T] { - self.data.as_mut_slice() - } - - /// Get the underlying byte rows read only - fn data_ref(&self) -> &[T] { - self.data.as_slice() - } -} - -impl From> for Vec { - /// Turn into the underlying [`Vec`] containing the rows of bytes. - fn from(value: ValueGrid) -> Self { - value.data - } -} - -/// An iterator iver the rows in a [ValueGrid] -pub struct IterGridRows<'t, T: Value> { - byte_grid: &'t ValueGrid, - row: usize, -} - -impl<'t, T: Value> Iterator for IterGridRows<'t, T> { - type Item = Iter<'t, T>; - - fn next(&mut self) -> Option { - if self.row >= self.byte_grid.height { - return None; - } - - let start = self.row * self.byte_grid.width; - let end = start + self.byte_grid.width; - let result = self.byte_grid.data[start..end].iter(); - self.row += 1; - Some(result) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - value_grid::{SetValueSeriesError, ValueGrid}, - *, - }; - - #[test] - fn fill() { - let mut grid = ValueGrid::::new(2, 2); - assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]); - - grid.fill(42); - assert_eq!(grid.data, [42; 4]); - } - - #[test] - fn get_set() { - let mut grid = ValueGrid::new(2, 2); - assert_eq!(grid.get(0, 0), 0); - assert_eq!(grid.get(1, 1), 0); - - grid.set(0, 0, 42); - grid.set(1, 0, 23); - assert_eq!(grid.data, [42, 23, 0, 0]); - - assert_eq!(grid.get(0, 0), 42); - assert_eq!(grid.get(1, 0), 23); - assert_eq!(grid.get(1, 1), 0); - } - - #[test] - fn load() { - let mut grid = ValueGrid::new(2, 3); - for x in 0..grid.width { - for y in 0..grid.height { - grid.set(x, y, (x + y) as u8); - } - } - - assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); - - let data: Vec = grid.into(); - - let grid = ValueGrid::load(2, 3, &data); - assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); - } - - #[test] - fn mut_data_ref() { - let mut vec = ValueGrid::new(2, 2); - - let data_ref = vec.data_ref_mut(); - data_ref.copy_from_slice(&[1, 2, 3, 4]); - - assert_eq!(vec.data, [1, 2, 3, 4]); - assert_eq!(vec.get(1, 0), 2) - } - - #[test] - fn iter() { - let mut vec = ValueGrid::new(2, 2); - vec.set(1, 1, 5); - - let mut iter = vec.iter(); - assert_eq!(*iter.next().unwrap(), 0); - assert_eq!(*iter.next().unwrap(), 0); - assert_eq!(*iter.next().unwrap(), 0); - assert_eq!(*iter.next().unwrap(), 5); - } - - #[test] - fn iter_mut() { - let mut vec = ValueGrid::new(2, 3); - for (index, cell) in vec.iter_mut().enumerate() { - *cell = index as u8; - } - - assert_eq!(vec.data_ref(), [0, 1, 2, 3, 4, 5]); - } - - #[test] - fn iter_rows() { - let vec = ValueGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); - for (y, row) in vec.iter_rows().enumerate() { - for (x, val) in row.enumerate() { - assert_eq!(*val, (x + y) as u8); - } - } - } - - #[test] - #[should_panic] - fn out_of_bounds_x() { - let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); - vec.set(2, 1, 5); - } - - #[test] - #[should_panic] - fn out_of_bounds_y() { - let vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); - vec.get(1, 2); - } - - #[test] - fn ref_mut() { - let mut vec = ValueGrid::from_vec(3, vec![0, 1, 2, 3,4,5,6,7,8]); - - let top_left = vec.get_ref_mut(0, 0); - *top_left += 5; - let somewhere = vec.get_ref_mut(2, 1); - *somewhere = 42; - - assert_eq!(None, vec.get_ref_mut_optional(3, 2)); - assert_eq!(None, vec.get_ref_mut_optional(2, 3)); - assert_eq!(Some(&mut 5), vec.get_ref_mut_optional(0, 0)); - assert_eq!(Some(&mut 42), vec.get_ref_mut_optional(2, 1)); - assert_eq!(Some(&mut 8), vec.get_ref_mut_optional(2, 2)); - } - - #[test] - fn optional() { - let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]); - grid.set_optional(0, 0, 5); - grid.set_optional(-1, 0, 8); - grid.set_optional(0, 8, 42); - assert_eq!(grid.data, [5, 1, 2, 3]); - - assert_eq!(grid.get_optional(0, 0), Some(5)); - assert_eq!(grid.get_optional(0, 8), None); - } - - #[test] - fn col() { - let mut grid = ValueGrid::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(SetValueSeriesError::OutOfBounds { size: 2, index: 2 }) - ); - assert_eq!( - grid.set_col(0, &[5, 7]), - Err(SetValueSeriesError::InvalidLength { - expected: 3, - actual: 2 - }) - ); - assert_eq!(grid.get_col(0), Some(vec![5, 7, 9])); - } - - #[test] - fn row() { - let mut grid = ValueGrid::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(SetValueSeriesError::OutOfBounds { size: 3, index: 3 }) - ); - assert_eq!( - grid.set_row(2, &[5, 7, 3]), - Err(SetValueSeriesError::InvalidLength { - expected: 2, - actual: 3 - }) - ); - } - - #[test] - fn wrap() { - let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap(); - assert_eq!(grid.height(), 3); - - let grid = ValueGrid::wrap(4, &[0, 1, 2, 3, 4, 5]); - assert_eq!(grid.err(), Some(TryLoadValueGridError::InvalidDimensions)); - } -} diff --git a/crates/servicepoint_binding_c/Cargo.toml b/crates/servicepoint_binding_c/Cargo.toml deleted file mode 100644 index e93d2f1..0000000 --- a/crates/servicepoint_binding_c/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "servicepoint_binding_c" -version.workspace = true -publish = true -edition = "2021" -license = "GPL-3.0-or-later" -description = "C bindings for the servicepoint crate." -homepage = "https://docs.rs/crate/servicepoint_binding_c" -repository = "https://git.berlin.ccc.de/servicepoint/servicepoint" -readme = "README.md" -links = "servicepoint" -keywords = ["cccb", "cccb-servicepoint", "cbindgen"] - -[lib] -crate-type = ["staticlib", "cdylib", "rlib"] - -[build-dependencies] -cbindgen = "0.27.0" - -[dependencies.servicepoint] -version = "0.13.1" -path = "../servicepoint" -features = ["all_compressions"] - -[lints] -workspace = true - -[package.metadata.docs.rs] -all-features = true diff --git a/crates/servicepoint_binding_c/README.md b/crates/servicepoint_binding_c/README.md deleted file mode 100644 index 892c9e5..0000000 --- a/crates/servicepoint_binding_c/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# servicepoint_binding_c - -[![crates.io](https://img.shields.io/crates/v/servicepoint_binding_c.svg)](https://crates.io/crates/servicepoint) -[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint_binding_c)](https://crates.io/crates/servicepoint) -[![docs.rs](https://img.shields.io/docsrs/servicepoint_binding_c)](https://docs.rs/servicepoint/latest/servicepoint/) -[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint_binding_c)](../../LICENSE) - -In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. -It is called "Service Point Display" or "Airport Display". - -This crate contains C bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP. - -## Examples - -```c++ -#include -#include "servicepoint.h" - -int main(void) { - SPConnection *connection = sp_connection_open("172.23.42.29:2342"); - if (connection == NULL) - return 1; - - SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); - sp_bitmap_fill(pixels, true); - - SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); - while (sp_connection_send_command(connection, sp_command_clone(command))); - - sp_command_free(command); - sp_connection_free(connection); - return 0; -} -``` - -A full example including Makefile is available as part of this crate. - -## Note on stability - -This library is still in early development. -You can absolutely use it, and it works, but expect minor breaking changes with every version bump. -Please specify the full version including patch in your Cargo.toml until 1.0 is released. - -## Installation - -Copy the header to your project and compile against. - -You have the choice of linking statically (recommended) or dynamically. -- The C example shows how to link statically against the `staticlib` variant. -- When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI guarantees yet. - -## Notes on differences to rust library - -- function names are: `sp_` \ \. -- Instances get consumed in the same way they do when writing rust code. Do not use an instance after an (implicit!) free. -- Option or Result turn into nullable return values - check for NULL! -- There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable. -- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe. -- documentation is included in the header and available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/) - -## Everything else - -Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for further information. diff --git a/crates/servicepoint_binding_c/build.rs b/crates/servicepoint_binding_c/build.rs deleted file mode 100644 index 93bf703..0000000 --- a/crates/servicepoint_binding_c/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Build script generating the header for the `servicepoint` C library. -//! -//! When the environment variable `SERVICEPOINT_HEADER_OUT` is set, the header is copied there from -//! the out directory. This can be used to use the build script as a command line tool from other -//! build tools. - -use std::{env, fs::copy}; - -use cbindgen::{generate_with_config, Config}; - -fn main() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - println!("cargo::rerun-if-changed={crate_dir}"); - - let config = - Config::from_file(crate_dir.clone() + "/cbindgen.toml").unwrap(); - - let output_dir = env::var("OUT_DIR").unwrap(); - let header_file = output_dir.clone() + "/servicepoint.h"; - - generate_with_config(crate_dir, config) - .unwrap() - .write_to_file(&header_file); - println!("cargo:include={output_dir}"); - - println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT"); - if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") { - let header_copy = header_out + "/servicepoint.h"; - println!("cargo:warning=Copying header to {header_copy}"); - copy(header_file, &header_copy).unwrap(); - println!("cargo::rerun-if-changed={header_copy}"); - } -} diff --git a/crates/servicepoint_binding_c/cbindgen.toml b/crates/servicepoint_binding_c/cbindgen.toml deleted file mode 100644 index 7fc0fdf..0000000 --- a/crates/servicepoint_binding_c/cbindgen.toml +++ /dev/null @@ -1,36 +0,0 @@ -language = "C" -include_version = true -cpp_compat = true - -autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" - -############################ Code Style Options ################################ - -braces = "SameLine" -line_length = 80 -tab_width = 4 -documentation = true -documentation_style = "auto" -documentation_length = "full" -line_endings = "LF" - -############################# Codegen Options ################################## - -style = "type" -usize_is_size_t = true - -# this is needed because otherwise the order in the C# bindings is different on different machines -sort_by = "Name" - -[parse] -parse_deps = false - -[parse.expand] -all_features = true - -[export] -include = [] -exclude = [] - -[enum] -rename_variants = "QualifiedScreamingSnakeCase" diff --git a/crates/servicepoint_binding_c/examples/lang_c/Cargo.toml b/crates/servicepoint_binding_c/examples/lang_c/Cargo.toml deleted file mode 100644 index 2231f3c..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "lang_c" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -test = false - -[build-dependencies] -cc = "1.2" - -[dependencies] -servicepoint_binding_c = { path = "../.." } diff --git a/crates/servicepoint_binding_c/examples/lang_c/Makefile b/crates/servicepoint_binding_c/examples/lang_c/Makefile deleted file mode 100644 index 6b15722..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -CC := gcc - -THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) -REPO_ROOT := $(THIS_DIR)/../../../.. - -build: out/lang_c - -clean: - rm -r out || true - rm include/servicepoint.h || true - cargo clean - -run: out/lang_c - out/lang_c - -PHONY: build clean dependencies run - -out/lang_c: dependencies src/main.c - mkdir -p out || true - ${CC} src/main.c \ - -I include \ - -L $(REPO_ROOT)/target/release \ - -Wl,-Bstatic -lservicepoint_binding_c \ - -Wl,-Bdynamic -llzma \ - -o out/lang_c - -dependencies: FORCE - mkdir -p include || true - # generate servicepoint header and binary to link against - SERVICEPOINT_HEADER_OUT=$(THIS_DIR)/include cargo build \ - --manifest-path=$(REPO_ROOT)/crates/servicepoint_binding_c/Cargo.toml \ - --release - -FORCE: ; diff --git a/crates/servicepoint_binding_c/examples/lang_c/build.rs b/crates/servicepoint_binding_c/examples/lang_c/build.rs deleted file mode 100644 index 4f92e1d..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/build.rs +++ /dev/null @@ -1,17 +0,0 @@ -const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE"; - -fn main() { - println!("cargo::rerun-if-changed=src/main.c"); - println!("cargo::rerun-if-changed=build.rs"); - println!("cargo::rerun-if-env-changed={SP_INCLUDE}"); - - let sp_include = - std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap(); - - // this builds a lib, this is only to check that the example compiles - let mut cc = cc::Build::new(); - cc.file("src/main.c"); - cc.include(&sp_include); - cc.opt_level(2); - cc.compile("lang_c"); -} diff --git a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h deleted file mode 100644 index d9cbe57..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h +++ /dev/null @@ -1,1867 +0,0 @@ -/* Generated with cbindgen:0.27.0 */ - -/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ - -#include -#include -#include -#include -#include - -/** - * Count of possible brightness values - */ -#define SP_BRIGHTNESS_LEVELS 12 - -/** - * see [servicepoint::Brightness::MAX] - */ -#define SP_BRIGHTNESS_MAX 11 - -/** - * see [servicepoint::Brightness::MIN] - */ -#define SP_BRIGHTNESS_MIN 0 - -/** - * pixel count on whole screen - */ -#define SP_PIXEL_COUNT (SP_PIXEL_WIDTH * SP_PIXEL_HEIGHT) - -/** - * Display height in pixels - */ -#define SP_PIXEL_HEIGHT (SP_TILE_HEIGHT * SP_TILE_SIZE) - -/** - * Display width in pixels - */ -#define SP_PIXEL_WIDTH (SP_TILE_WIDTH * SP_TILE_SIZE) - -/** - * Display tile count in the y-direction - */ -#define SP_TILE_HEIGHT 20 - -/** - * size of a single tile in one dimension - */ -#define SP_TILE_SIZE 8 - -/** - * Display tile count in the x-direction - */ -#define SP_TILE_WIDTH 56 - -/** - * Specifies the kind of compression to use. - */ -enum SPCompressionCode -#ifdef __cplusplus - : uint16_t -#endif // __cplusplus - { - /** - * no compression - */ - SP_COMPRESSION_CODE_UNCOMPRESSED = 0, - /** - * compress using flate2 with zlib header - */ - SP_COMPRESSION_CODE_ZLIB = 26490, - /** - * compress using bzip2 - */ - SP_COMPRESSION_CODE_BZIP2 = 25210, - /** - * compress using lzma - */ - SP_COMPRESSION_CODE_LZMA = 27770, - /** - * compress using Zstandard - */ - SP_COMPRESSION_CODE_ZSTD = 31347, -}; -#ifndef __cplusplus -typedef uint16_t SPCompressionCode; -#endif // __cplusplus - -/** - * A vector of bits - * - * # Examples - * ```C - * SPBitVec vec = sp_bitvec_new(8); - * sp_bitvec_set(vec, 5, true); - * sp_bitvec_free(vec); - * ``` - */ -typedef struct SPBitVec SPBitVec; - -/** - * A grid of pixels. - * - * # Examples - * - * ```C - * Cp437Grid grid = sp_bitmap_new(8, 3); - * sp_bitmap_fill(grid, true); - * sp_bitmap_set(grid, 0, 0, false); - * sp_bitmap_free(grid); - * ``` - */ -typedef struct SPBitmap SPBitmap; - -/** - * A grid containing brightness values. - * - * # Examples - * ```C - * SPConnection connection = sp_connection_open("127.0.0.1:2342"); - * if (connection == NULL) - * return 1; - * - * SPBrightnessGrid grid = sp_brightness_grid_new(2, 2); - * sp_brightness_grid_set(grid, 0, 0, 0); - * sp_brightness_grid_set(grid, 1, 1, 10); - * - * SPCommand command = sp_command_char_brightness(grid); - * sp_connection_free(connection); - * ``` - */ -typedef struct SPBrightnessGrid SPBrightnessGrid; - -/** - * A C-wrapper for grid containing UTF-8 characters. - * - * As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers. - * - * The encoding is enforced in most cases by the rust standard library - * and will panic when provided with illegal characters. - * - * # Examples - * - * ```C - * CharGrid grid = sp_char_grid_new(4, 3); - * sp_char_grid_fill(grid, '?'); - * sp_char_grid_set(grid, 0, 0, '!'); - * sp_char_grid_free(grid); - * ``` - */ -typedef struct SPCharGrid SPCharGrid; - -/** - * A low-level display command. - * - * This struct and associated functions implement the UDP protocol for the display. - * - * To send a [SPCommand], use a [SPConnection]. - * - * # Examples - * - * ```C - * sp_connection_send_command(connection, sp_command_clear()); - * sp_connection_send_command(connection, sp_command_brightness(5)); - * ``` - * - * [SPConnection]: [crate::SPConnection] - */ -typedef struct SPCommand SPCommand; - -/** - * A connection to the display. - * - * # Examples - * - * ```C - * CConnection connection = sp_connection_open("172.23.42.29:2342"); - * if (connection != NULL) - * sp_connection_send_command(connection, sp_command_clear()); - * ``` - */ -typedef struct SPConnection SPConnection; - -/** - * A C-wrapper for grid containing codepage 437 characters. - * - * The encoding is currently not enforced. - * - * # Examples - * - * ```C - * Cp437Grid grid = sp_cp437_grid_new(4, 3); - * sp_cp437_grid_fill(grid, '?'); - * sp_cp437_grid_set(grid, 0, 0, '!'); - * sp_cp437_grid_free(grid); - * ``` - */ -typedef struct SPCp437Grid SPCp437Grid; - -/** - * The raw packet - */ -typedef struct SPPacket SPPacket; - -/** - * Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. - * - * You should not create an instance of this type in your C code. - * - * # Safety - * - * The caller has to make sure that: - * - * - accesses to the memory pointed to with `start` is never accessed outside `length` - * - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in - * the function returning this type. - * - an instance of this created from C is never passed to a consuming function, as the rust code - * will try to free the memory of a potentially separate allocator. - */ -typedef struct { - /** - * The start address of the memory - */ - uint8_t *start; - /** - * The amount of memory in bytes - */ - size_t length; -} SPByteSlice; - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -/** - * Clones a [SPBitmap]. - * - * Will never return NULL. - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - `bitmap` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitmap_free`. - */ -SPBitmap *sp_bitmap_clone(const SPBitmap *bitmap); - -/** - * Sets the state of all pixels in the [SPBitmap]. - * - * # Arguments - * - * - `bitmap`: instance to write to - * - `value`: the value to set all pixels to - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - `bitmap` is not written to or read from concurrently - */ -void sp_bitmap_fill(SPBitmap *bitmap, bool value); - -/** - * Deallocates a [SPBitmap]. - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - `bitmap` is not used concurrently or after bitmap call - * - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand] - * - * [SPCommand]: [crate::SPCommand] - */ -void sp_bitmap_free(SPBitmap *bitmap); - -/** - * Gets the current value at the specified position in the [SPBitmap]. - * - * # Arguments - * - * - `bitmap`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when `bitmap` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - `bitmap` is not written to concurrently - */ -bool sp_bitmap_get(const SPBitmap *bitmap, size_t x, size_t y); - -/** - * Gets the height in pixels of the [SPBitmap] instance. - * - * # Arguments - * - * - `bitmap`: instance to read from - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - */ -size_t sp_bitmap_height(const SPBitmap *bitmap); - -/** - * Loads a [SPBitmap] with the specified dimensions from the provided data. - * - * # Arguments - * - * - `width`: size in pixels in x-direction - * - `height`: size in pixels in y-direction - * - * returns: [SPBitmap] that contains a copy of the provided data. Will never return NULL. - * - * # Panics - * - * - when `data` is NULL - * - when the dimensions and data size do not match exactly. - * - when the width is not dividable by 8 - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory location of at least `data_length` bytes in size. - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitmap_free`. - */ -SPBitmap *sp_bitmap_load(size_t width, - size_t height, - const uint8_t *data, - size_t data_length); - -/** - * Creates a new [SPBitmap] with the specified dimensions. - * - * # Arguments - * - * - `width`: size in pixels in x-direction - * - `height`: size in pixels in y-direction - * - * returns: [SPBitmap] initialized to all pixels off. Will never return NULL. - * - * # Panics - * - * - when the width is not dividable by 8 - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitmap_free`. - */ -SPBitmap *sp_bitmap_new(size_t width, - size_t height); - -/** - * Creates a new [SPBitmap] with a size matching the screen. - * - * returns: [SPBitmap] initialized to all pixels off. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling [sp_bitmap_free]. - */ -SPBitmap *sp_bitmap_new_screen_sized(void); - -/** - * Sets the value of the specified position in the [SPBitmap]. - * - * # Arguments - * - * - `bitmap`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when `bitmap` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - `bitmap` is not written to or read from concurrently - */ -void sp_bitmap_set(SPBitmap *bitmap, size_t x, size_t y, bool value); - -/** - * Gets an unsafe reference to the data of the [SPBitmap] instance. - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - * - the returned memory range is never accessed after the passed [SPBitmap] has been freed - * - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly - */ -SPByteSlice sp_bitmap_unsafe_data_ref(SPBitmap *bitmap); - -/** - * Gets the width in pixels of the [SPBitmap] instance. - * - * # Arguments - * - * - `bitmap`: instance to read from - * - * # Panics - * - * - when `bitmap` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid [SPBitmap] - */ -size_t sp_bitmap_width(const SPBitmap *bitmap); - -/** - * Clones a [SPBitVec]. - * - * returns: new [SPBitVec] instance. Will never return NULL. - * - * # Panics - * - * - when `bit_vec` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - `bit_vec` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitvec_free`. - */ -SPBitVec *sp_bitvec_clone(const SPBitVec *bit_vec); - -/** - * Sets the value of all bits in the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - `value`: the value to set all bits to - * - * # Panics - * - * - when `bit_vec` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - `bit_vec` is not written to or read from concurrently - */ -void sp_bitvec_fill(SPBitVec *bit_vec, bool value); - -/** - * Deallocates a [SPBitVec]. - * - * # Panics - * - * - when `but_vec` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - `bit_vec` 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); - -/** - * Gets the value of a bit from the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to read from - * - `index`: the bit index to read - * - * returns: value of the bit - * - * # Panics - * - * - when `bit_vec` is NULL - * - when accessing `index` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - `bit_vec` is not written to concurrently - */ -bool sp_bitvec_get(const SPBitVec *bit_vec, size_t index); - -/** - * Returns true if length is 0. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - * # Panics - * - * - when `bit_vec` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - */ -bool sp_bitvec_is_empty(const SPBitVec *bit_vec); - -/** - * Gets the length of the [SPBitVec] in bits. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - * # Panics - * - * - when `bit_vec` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - */ -size_t sp_bitvec_len(const SPBitVec *bit_vec); - -/** - * Interpret the data as a series of bits and load then into a new [SPBitVec] instance. - * - * returns: [SPBitVec] instance containing data. Will never return NULL. - * - * # Panics - * - * - when `data` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory location of at least `data_length` - * bytes in size. - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitvec_free`. - */ -SPBitVec *sp_bitvec_load(const uint8_t *data, - size_t data_length); - -/** - * Creates a new [SPBitVec] instance. - * - * # Arguments - * - * - `size`: size in bits. - * - * returns: [SPBitVec] with all bits set to false. Will never return NULL. - * - * # Panics - * - * - when `size` is not divisible by 8. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_bitvec_free`. - */ -SPBitVec *sp_bitvec_new(size_t size); - -/** - * Sets the value of a bit in the [SPBitVec]. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - `index`: the bit index to edit - * - `value`: the value to set the bit to - * - * # Panics - * - * - when `bit_vec` is NULL - * - when accessing `index` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - `bit_vec` is not written to or read from concurrently - */ -void sp_bitvec_set(SPBitVec *bit_vec, size_t index, bool value); - -/** - * Gets an unsafe reference to the data of the [SPBitVec] instance. - * - * # Arguments - * - * - `bit_vec`: instance to write to - * - * # Panics - * - * - when `bit_vec` is NULL - * - * ## Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid [SPBitVec] - * - the returned memory range is never accessed after the passed [SPBitVec] has been freed - * - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly - */ -SPByteSlice sp_bitvec_unsafe_data_ref(SPBitVec *bit_vec); - -/** - * Clones a [SPBrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: new [SPBrightnessGrid] instance. Will never return NULL. - * - * # Panics - * - * - when `brightness_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - * - `brightness_grid` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_brightness_grid_free`. - */ -SPBrightnessGrid *sp_brightness_grid_clone(const SPBrightnessGrid *brightness_grid); - -/** - * Sets the value of all cells in the [SPBrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to write to - * - `value`: the value to set all cells to - * - * # Panics - * - * - when `brightness_grid` is NULL - * - When providing an invalid brightness value - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - * - `brightness_grid` is not written to or read from concurrently - */ -void sp_brightness_grid_fill(SPBrightnessGrid *brightness_grid, uint8_t value); - -/** - * Deallocates a [SPBrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * # Panics - * - * - when `brightness_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - * - `brightness_grid` 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); - -/** - * Gets the current value at the specified position. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * returns: value at position - * - * # Panics - * - * - when `brightness_grid` is NULL - * - When accessing `x` or `y` out of bounds. - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - * - `brightness_grid` is not written to concurrently - */ -uint8_t sp_brightness_grid_get(const SPBrightnessGrid *brightness_grid, - size_t x, - size_t y); - -/** - * Gets the height of the [SPBrightnessGrid] instance. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: height - * - * # Panics - * - * - when `brightness_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - */ -size_t sp_brightness_grid_height(const SPBrightnessGrid *brightness_grid); - -/** - * Loads a [SPBrightnessGrid] with the specified dimensions from the provided data. - * - * returns: new [SPBrightnessGrid] instance. Will never return NULL. - * - * # Panics - * - * - when `data` is NULL - * - when the provided `data_length` does not match `height` and `width` - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory location of at least `data_length` - * bytes in size. - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_brightness_grid_free`. - */ -SPBrightnessGrid *sp_brightness_grid_load(size_t width, - size_t height, - const uint8_t *data, - size_t data_length); - -/** - * Creates a new [SPBrightnessGrid] with the specified dimensions. - * - * returns: [SPBrightnessGrid] initialized to 0. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_brightness_grid_free`. - */ -SPBrightnessGrid *sp_brightness_grid_new(size_t width, - size_t height); - -/** - * Sets the value of the specified position in the [SPBrightnessGrid]. - * - * # Arguments - * - * - `brightness_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when `brightness_grid` is NULL - * - When accessing `x` or `y` out of bounds. - * - When providing an invalid brightness value - * - * # Safety - * - * The caller has to make sure that: - * - * - `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, - size_t x, - size_t y, - uint8_t value); - -/** - * Gets an unsafe reference to the data of the [SPBrightnessGrid] instance. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: slice of bytes underlying the `brightness_grid`. - * - * # Panics - * - * - when `brightness_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - * - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed - * - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly - */ -SPByteSlice sp_brightness_grid_unsafe_data_ref(SPBrightnessGrid *brightness_grid); - -/** - * Gets the width of the [SPBrightnessGrid] instance. - * - * # Arguments - * - * - `brightness_grid`: instance to read from - * - * returns: width - * - * # Panics - * - * - when `brightness_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `brightness_grid` points to a valid [SPBrightnessGrid] - */ -size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid); - -/** - * Clones a [SPCharGrid]. - * - * Will never return NULL. - * - * # Panics - * - * - when `char_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - * - `char_grid` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_char_grid_free`. - */ -SPCharGrid *sp_char_grid_clone(const SPCharGrid *char_grid); - -/** - * Sets the value of all cells in the [SPCharGrid]. - * - * # Arguments - * - * - `char_grid`: instance to write to - * - `value`: the value to set all cells to - * - * # Panics - * - * - when `char_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - * - `char_grid` is not written to or read from concurrently - */ -void sp_char_grid_fill(SPCharGrid *char_grid, uint32_t value); - -/** - * Deallocates a [SPCharGrid]. - * - * # Panics - * - * - when `char_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - * - `char_grid` is not used concurrently or after char_grid call - * - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand] - * - * [SPCommand]: [crate::SPCommand] - */ -void sp_char_grid_free(SPCharGrid *char_grid); - -/** - * Gets the current value at the specified position. - * - * # Arguments - * - * - `char_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when `char_grid` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - * - `char_grid` is not written to concurrently - */ -uint32_t sp_char_grid_get(const SPCharGrid *char_grid, size_t x, size_t y); - -/** - * Gets the height of the [SPCharGrid] instance. - * - * # Arguments - * - * - `char_grid`: instance to read from - * - * # Panics - * - * - when `char_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - */ -size_t sp_char_grid_height(const SPCharGrid *char_grid); - -/** - * Loads a [SPCharGrid] with the specified dimensions from the provided data. - * - * Will never return NULL. - * - * # Panics - * - * - when `data` is NULL - * - when the provided `data_length` does not match `height` and `width` - * - when `data` is not valid UTF-8 - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory location of at least `data_length` - * bytes in size. - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_char_grid_free`. - */ -SPCharGrid *sp_char_grid_load(size_t width, - size_t height, - const uint8_t *data, - size_t data_length); - -/** - * Creates a new [SPCharGrid] with the specified dimensions. - * - * returns: [SPCharGrid] initialized to 0. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_char_grid_free`. - */ -SPCharGrid *sp_char_grid_new(size_t width, - size_t height); - -/** - * Sets the value of the specified position in the [SPCharGrid]. - * - * # Arguments - * - * - `char_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when `char_grid` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPBitVec] - * - `char_grid` is not written to or read from concurrently - * - * [SPBitVec]: [crate::SPBitVec] - */ -void sp_char_grid_set(SPCharGrid *char_grid, - size_t x, - size_t y, - uint32_t value); - -/** - * Gets the width of the [SPCharGrid] instance. - * - * # Arguments - * - * - `char_grid`: instance to read from - * - * # Panics - * - * - when `char_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `char_grid` points to a valid [SPCharGrid] - */ -size_t sp_char_grid_width(const SPCharGrid *char_grid); - -/** - * Set pixel data starting at the pixel offset on screen. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL. - * - * # Panics - * - * - when `bit_vec` is null - * - when `compression_code` is not a valid value - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid instance of [SPBitVec] - * - `bit_vec` is not used concurrently or after this call - * - `compression` matches one of the allowed enum values - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_bitmap_linear(size_t offset, - SPBitVec *bit_vec, - SPCompressionCode compression); - -/** - * Set pixel data according to an and-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL. - * - * # Panics - * - * - when `bit_vec` is null - * - when `compression_code` is not a valid value - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid instance of [SPBitVec] - * - `bit_vec` is not used concurrently or after this call - * - `compression` matches one of the allowed enum values - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_bitmap_linear_and(size_t offset, - SPBitVec *bit_vec, - SPCompressionCode compression); - -/** - * Set pixel data according to an or-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL. - * - * # Panics - * - * - when `bit_vec` is null - * - when `compression_code` is not a valid value - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid instance of [SPBitVec] - * - `bit_vec` is not used concurrently or after this call - * - `compression` matches one of the allowed enum values - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_bitmap_linear_or(size_t offset, - SPBitVec *bit_vec, - SPCompressionCode compression); - -/** - * Sets a window of pixels to the specified values. - * - * The passed [SPBitmap] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL. - * - * # Panics - * - * - when `bitmap` is null - * - when `compression_code` is not a valid value - * - * # Safety - * - * The caller has to make sure that: - * - * - `bitmap` points to a valid instance of [SPBitmap] - * - `bitmap` is not used concurrently or after this call - * - `compression` matches one of the allowed enum values - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_bitmap_linear_win(size_t x, - size_t y, - SPBitmap *bitmap, - SPCompressionCode compression_code); - -/** - * Set pixel data according to a xor-mask starting at the offset. - * - * The screen will continuously overwrite more pixel data without regarding the offset, meaning - * once the starting row is full, overwriting will continue on column 0. - * - * The contained [SPBitVec] is always uncompressed. - * - * The passed [SPBitVec] gets consumed. - * - * Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL. - * - * # Panics - * - * - when `bit_vec` is null - * - when `compression_code` is not a valid value - * - * # Safety - * - * The caller has to make sure that: - * - * - `bit_vec` points to a valid instance of [SPBitVec] - * - `bit_vec` is not used concurrently or after this call - * - `compression` matches one of the allowed enum values - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_bitmap_linear_xor(size_t offset, - SPBitVec *bit_vec, - SPCompressionCode compression); - -/** - * Set the brightness of all tiles to the same value. - * - * Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL. - * - * # Panics - * - * - When the provided brightness value is out of range (0-11). - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_brightness(uint8_t brightness); - -/** - * Set the brightness of individual tiles in a rectangular area of the display. - * - * The passed [SPBrightnessGrid] gets consumed. - * - * Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL. - * - * # Panics - * - * - when `grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `grid` points to a valid instance of [SPBrightnessGrid] - * - `grid` is not used concurrently or after this call - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_char_brightness(size_t x, - size_t y, - SPBrightnessGrid *grid); - -/** - * Set all pixels to the off state. - * - * Does not affect brightness. - * - * Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. - * - * # Examples - * - * ```C - * sp_connection_send_command(connection, sp_command_clear()); - * ``` - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_clear(void); - -/** - * Clones a [SPCommand] instance. - * - * returns: new [SPCommand] instance. Will never return NULL. - * - * # Panics - * - * - when `command` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `command` points to a valid instance of [SPCommand] - * - `command` is not written to concurrently - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_clone(const SPCommand *command); - -/** - * Show codepage 437 encoded text on the screen. - * - * The passed [SPCp437Grid] gets consumed. - * - * Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL. - * - * # Panics - * - * - when `grid` is null - * - * # Safety - * - * The caller has to make sure that: - * - * - `grid` points to a valid instance of [SPCp437Grid] - * - `grid` is not used concurrently or after this call - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_cp437_data(size_t x, - size_t y, - SPCp437Grid *grid); - -/** - * A yet-to-be-tested command. - * - * Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_fade_out(void); - -/** - * Deallocates a [SPCommand]. - * - * # Examples - * - * ```C - * SPCommand c = sp_command_clear(); - * sp_command_free(c); - * ``` - * - * # Panics - * - * - when `command` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `command` points to a valid [SPCommand] - * - `command` is not used concurrently or after this call - * - `command` was not passed to another consuming function, e.g. to create a [SPPacket] - */ -void sp_command_free(SPCommand *command); - -/** - * Kills the udp daemon on the display, which usually results in a restart. - * - * Please do not send this in your normal program flow. - * - * Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_hard_reset(void); - -/** - * Tries to turn a [SPPacket] into a [SPCommand]. - * - * The packet is deallocated in the process. - * - * Returns: pointer to new [SPCommand] instance or NULL if parsing failed. - * - * # Panics - * - * - when `packet` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - [SPPacket] points to a valid instance of [SPPacket] - * - [SPPacket] is not used concurrently or after this call - * - the result is checked for NULL - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_try_from_packet(SPPacket *packet); - -/** - * Show UTF-8 encoded text on the screen. - * - * The passed [SPCharGrid] gets consumed. - * - * Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL. - * - * # Panics - * - * - when `grid` is null - * - * # Safety - * - * The caller has to make sure that: - * - * - `grid` points to a valid instance of [SPCharGrid] - * - `grid` is not used concurrently or after this call - * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_command_free`. - */ -SPCommand *sp_command_utf8_data(size_t x, - size_t y, - SPCharGrid *grid); - -/** - * Creates a new instance of [SPConnection] for testing that does not actually send anything. - * - * returns: a new instance. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_connection_free`. - */ -SPConnection *sp_connection_fake(void); - -/** - * Closes and deallocates a [SPConnection]. - * - * # Panics - * - * - when `connection` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `connection` points to a valid [SPConnection] - * - `connection` is not used concurrently or after this call - */ -void sp_connection_free(SPConnection *connection); - -/** - * Creates a new instance of [SPConnection]. - * - * returns: NULL if connection fails, or connected instance - * - * # Panics - * - * - when `host` is null or an invalid host - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_connection_free`. - */ -SPConnection *sp_connection_open(const char *host); - -/** - * Sends a [SPCommand] to the display using the [SPConnection]. - * - * The passed `command` gets consumed. - * - * returns: true in case of success - * - * # Panics - * - * - when `connection` is NULL - * - when `command` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `connection` points to a valid instance of [SPConnection] - * - `command` points to a valid instance of [SPPacket] - * - `command` is not used concurrently or after this call - */ -bool sp_connection_send_command(const SPConnection *connection, - SPCommand *command); - -/** - * Sends a [SPPacket] to the display using the [SPConnection]. - * - * The passed `packet` gets consumed. - * - * returns: true in case of success - * - * # Panics - * - * - when `connection` is NULL - * - when `packet` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `connection` points to a valid instance of [SPConnection] - * - `packet` points to a valid instance of [SPPacket] - * - `packet` is not used concurrently or after this call - */ -bool sp_connection_send_packet(const SPConnection *connection, - SPPacket *packet); - -/** - * Clones a [SPCp437Grid]. - * - * Will never return NULL. - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - * - `cp437_grid` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_cp437_grid_free`. - */ -SPCp437Grid *sp_cp437_grid_clone(const SPCp437Grid *cp437_grid); - -/** - * Sets the value of all cells in the [SPCp437Grid]. - * - * # Arguments - * - * - `cp437_grid`: instance to write to - * - `value`: the value to set all cells to - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - * - `cp437_grid` is not written to or read from concurrently - */ -void sp_cp437_grid_fill(SPCp437Grid *cp437_grid, uint8_t value); - -/** - * Deallocates a [SPCp437Grid]. - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - * - `cp437_grid` 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); - -/** - * Gets the current value at the specified position. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - * - `x` and `y`: position of the cell to read - * - * # Panics - * - * - when `cp437_grid` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - * - `cp437_grid` is not written to concurrently - */ -uint8_t sp_cp437_grid_get(const SPCp437Grid *cp437_grid, size_t x, size_t y); - -/** - * Gets the height of the [SPCp437Grid] instance. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - */ -size_t sp_cp437_grid_height(const SPCp437Grid *cp437_grid); - -/** - * Loads a [SPCp437Grid] with the specified dimensions from the provided data. - * - * Will never return NULL. - * - * # Panics - * - * - when `data` is NULL - * - when the provided `data_length` does not match `height` and `width` - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory location of at least `data_length` - * bytes in size. - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_cp437_grid_free`. - */ -SPCp437Grid *sp_cp437_grid_load(size_t width, - size_t height, - const uint8_t *data, - size_t data_length); - -/** - * Creates a new [SPCp437Grid] with the specified dimensions. - * - * returns: [SPCp437Grid] initialized to 0. Will never return NULL. - * - * # Safety - * - * The caller has to make sure that: - * - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_cp437_grid_free`. - */ -SPCp437Grid *sp_cp437_grid_new(size_t width, - size_t height); - -/** - * Sets the value of the specified position in the [SPCp437Grid]. - * - * # Arguments - * - * - `cp437_grid`: instance to write to - * - `x` and `y`: position of the cell - * - `value`: the value to write to the cell - * - * returns: old value of the cell - * - * # Panics - * - * - when `cp437_grid` is NULL - * - when accessing `x` or `y` out of bounds - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPBitVec] - * - `cp437_grid` is not written to or read from concurrently - * - * [SPBitVec]: [crate::SPBitVec] - */ -void sp_cp437_grid_set(SPCp437Grid *cp437_grid, - size_t x, - size_t y, - uint8_t value); - -/** - * Gets an unsafe reference to the data of the [SPCp437Grid] instance. - * - * Will never return NULL. - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * ## Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - * - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed - * - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly - */ -SPByteSlice sp_cp437_grid_unsafe_data_ref(SPCp437Grid *cp437_grid); - -/** - * Gets the width of the [SPCp437Grid] instance. - * - * # Arguments - * - * - `cp437_grid`: instance to read from - * - * # Panics - * - * - when `cp437_grid` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `cp437_grid` points to a valid [SPCp437Grid] - */ -size_t sp_cp437_grid_width(const SPCp437Grid *cp437_grid); - -/** - * Clones a [SPPacket]. - * - * Will never return NULL. - * - * # Panics - * - * - when `packet` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `packet` points to a valid [SPPacket] - * - `packet` is not written to concurrently - * - the returned instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_packet_free`. - */ -SPPacket *sp_packet_clone(const SPPacket *packet); - -/** - * Deallocates a [SPPacket]. - * - * # Panics - * - * - when `packet` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `packet` points to a valid [SPPacket] - * - `packet` is not used concurrently or after this call - */ -void sp_packet_free(SPPacket *packet); - -/** - * Turns a [SPCommand] into a [SPPacket]. - * The [SPCommand] gets consumed. - * - * Will never return NULL. - * - * # Panics - * - * - when `command` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - [SPCommand] points to a valid instance of [SPCommand] - * - [SPCommand] is not used concurrently or after this call - * - the returned [SPPacket] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_packet_free`. - */ -SPPacket *sp_packet_from_command(SPCommand *command); - -/** - * Creates a raw [SPPacket] from parts. - * - * # Arguments - * - * - `command_code` specifies which command this packet contains - * - `a`, `b`, `c` and `d` are command-specific header values - * - `payload` is the optional data that is part of the command - * - `payload_len` is the size of the payload - * - * returns: new instance. Will never return null. - * - * # Panics - * - * - when `payload` is null, but `payload_len` is not zero - * - when `payload_len` is zero, but `payload` is nonnull - * - * # Safety - * - * The caller has to make sure that: - * - * - `payload` points to a valid memory region of at least `payload_len` bytes - * - `payload` is not written to concurrently - * - the returned [SPPacket] instance is freed in some way, either by using a consuming function or - * by explicitly calling [sp_packet_free]. - */ -SPPacket *sp_packet_from_parts(uint16_t command_code, - uint16_t a, - uint16_t b, - uint16_t c, - uint16_t d, - const uint8_t *payload, - size_t payload_len); - -/** - * Tries to load a [SPPacket] from the passed array with the specified length. - * - * returns: NULL in case of an error, pointer to the allocated packet otherwise - * - * # Panics - * - * - when `data` is NULL - * - * # Safety - * - * The caller has to make sure that: - * - * - `data` points to a valid memory region of at least `length` bytes - * - `data` is not written to concurrently - * - the returned [SPPacket] instance is freed in some way, either by using a consuming function or - * by explicitly calling `sp_packet_free`. - */ -SPPacket *sp_packet_try_load(const uint8_t *data, - size_t length); - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus diff --git a/crates/servicepoint_binding_c/examples/lang_c/src/lib.rs b/crates/servicepoint_binding_c/examples/lang_c/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/servicepoint_binding_c/examples/lang_c/src/main.c b/crates/servicepoint_binding_c/examples/lang_c/src/main.c deleted file mode 100644 index 1454804..0000000 --- a/crates/servicepoint_binding_c/examples/lang_c/src/main.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include "servicepoint.h" - -int main(void) { - SPConnection *connection = sp_connection_open("localhost:2342"); - if (connection == NULL) - return 1; - - SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); - sp_bitmap_fill(pixels, true); - - SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED); - sp_connection_send_command(connection, command); - - sp_connection_free(connection); - return 0; -} diff --git a/crates/servicepoint_binding_c/src/bitmap.rs b/crates/servicepoint_binding_c/src/bitmap.rs deleted file mode 100644 index 3313385..0000000 --- a/crates/servicepoint_binding_c/src/bitmap.rs +++ /dev/null @@ -1,296 +0,0 @@ -//! C functions for interacting with [SPBitmap]s -//! -//! prefix `sp_bitmap_` - -use servicepoint::{DataRef, Grid}; -use std::ptr::NonNull; - -use crate::byte_slice::SPByteSlice; - -/// A grid of pixels. -/// -/// # Examples -/// -/// ```C -/// Cp437Grid grid = sp_bitmap_new(8, 3); -/// sp_bitmap_fill(grid, true); -/// sp_bitmap_set(grid, 0, 0, false); -/// sp_bitmap_free(grid); -/// ``` -pub struct SPBitmap(pub(crate) servicepoint::Bitmap); - -/// Creates a new [SPBitmap] with the specified dimensions. -/// -/// # Arguments -/// -/// - `width`: size in pixels in x-direction -/// - `height`: size in pixels in y-direction -/// -/// returns: [SPBitmap] initialized to all pixels off. Will never return NULL. -/// -/// # Panics -/// -/// - when the width is not dividable by 8 -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitmap_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_new( - width: usize, - height: usize, -) -> NonNull { - let result = Box::new(SPBitmap(servicepoint::Bitmap::new(width, height))); - NonNull::from(Box::leak(result)) -} - -/// Creates a new [SPBitmap] with a size matching the screen. -/// -/// returns: [SPBitmap] initialized to all pixels off. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling [sp_bitmap_free]. -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull { - let result = Box::new(SPBitmap(servicepoint::Bitmap::max_sized())); - NonNull::from(Box::leak(result)) -} - -/// Loads a [SPBitmap] with the specified dimensions from the provided data. -/// -/// # Arguments -/// -/// - `width`: size in pixels in x-direction -/// - `height`: size in pixels in y-direction -/// -/// returns: [SPBitmap] that contains a copy of the provided data. Will never return NULL. -/// -/// # Panics -/// -/// - when `data` is NULL -/// - when the dimensions and data size do not match exactly. -/// - when the width is not dividable by 8 -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory location of at least `data_length` bytes in size. -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitmap_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_load( - width: usize, - height: usize, - data: *const u8, - data_length: usize, -) -> NonNull { - assert!(!data.is_null()); - let data = std::slice::from_raw_parts(data, data_length); - let result = - Box::new(SPBitmap(servicepoint::Bitmap::load(width, height, data))); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPBitmap]. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - `bitmap` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitmap_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_clone( - bitmap: *const SPBitmap, -) -> NonNull { - assert!(!bitmap.is_null()); - let result = Box::new(SPBitmap((*bitmap).0.clone())); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPBitmap]. -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - `bitmap` is not used concurrently or after bitmap call -/// - `bitmap` was not passed to another consuming function, e.g. to create a [SPCommand] -/// -/// [SPCommand]: [crate::SPCommand] -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_free(bitmap: *mut SPBitmap) { - assert!(!bitmap.is_null()); - _ = Box::from_raw(bitmap); -} - -/// Gets the current value at the specified position in the [SPBitmap]. -/// -/// # Arguments -/// -/// - `bitmap`: instance to read from -/// - `x` and `y`: position of the cell to read -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - `bitmap` is not written to concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_get( - bitmap: *const SPBitmap, - x: usize, - y: usize, -) -> bool { - assert!(!bitmap.is_null()); - (*bitmap).0.get(x, y) -} - -/// Sets the value of the specified position in the [SPBitmap]. -/// -/// # Arguments -/// -/// - `bitmap`: instance to write to -/// - `x` and `y`: position of the cell -/// - `value`: the value to write to the cell -/// -/// returns: old value of the cell -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - `bitmap` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_set( - bitmap: *mut SPBitmap, - x: usize, - y: usize, - value: bool, -) { - assert!(!bitmap.is_null()); - (*bitmap).0.set(x, y, value); -} - -/// Sets the state of all pixels in the [SPBitmap]. -/// -/// # Arguments -/// -/// - `bitmap`: instance to write to -/// - `value`: the value to set all pixels to -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - `bitmap` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_fill(bitmap: *mut SPBitmap, value: bool) { - assert!(!bitmap.is_null()); - (*bitmap).0.fill(value); -} - -/// Gets the width in pixels of the [SPBitmap] instance. -/// -/// # Arguments -/// -/// - `bitmap`: instance to read from -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_width(bitmap: *const SPBitmap) -> usize { - assert!(!bitmap.is_null()); - (*bitmap).0.width() -} - -/// Gets the height in pixels of the [SPBitmap] instance. -/// -/// # Arguments -/// -/// - `bitmap`: instance to read from -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_height(bitmap: *const SPBitmap) -> usize { - assert!(!bitmap.is_null()); - (*bitmap).0.height() -} - -/// Gets an unsafe reference to the data of the [SPBitmap] instance. -/// -/// # Panics -/// -/// - when `bitmap` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid [SPBitmap] -/// - the returned memory range is never accessed after the passed [SPBitmap] has been freed -/// - the returned memory range is never accessed concurrently, either via the [SPBitmap] or directly -#[no_mangle] -pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref( - bitmap: *mut SPBitmap, -) -> SPByteSlice { - assert!(!bitmap.is_null()); - let data = (*bitmap).0.data_ref_mut(); - SPByteSlice { - start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), - length: data.len(), - } -} diff --git a/crates/servicepoint_binding_c/src/bitvec.rs b/crates/servicepoint_binding_c/src/bitvec.rs deleted file mode 100644 index 484e849..0000000 --- a/crates/servicepoint_binding_c/src/bitvec.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! C functions for interacting with [SPBitVec]s -//! -//! prefix `sp_bitvec_` - -use crate::SPByteSlice; -use std::ptr::NonNull; - -/// A vector of bits -/// -/// # Examples -/// ```C -/// SPBitVec vec = sp_bitvec_new(8); -/// sp_bitvec_set(vec, 5, true); -/// sp_bitvec_free(vec); -/// ``` -pub struct SPBitVec(servicepoint::BitVec); - -impl From for SPBitVec { - fn from(actual: servicepoint::BitVec) -> Self { - Self(actual) - } -} - -impl From for servicepoint::BitVec { - fn from(value: SPBitVec) -> Self { - value.0 - } -} - -impl Clone for SPBitVec { - fn clone(&self) -> Self { - SPBitVec(self.0.clone()) - } -} - -/// Creates a new [SPBitVec] instance. -/// -/// # Arguments -/// -/// - `size`: size in bits. -/// -/// returns: [SPBitVec] with all bits set to false. Will never return NULL. -/// -/// # Panics -/// -/// - when `size` is not divisible by 8. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitvec_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull { - let result = Box::new(SPBitVec(servicepoint::BitVec::repeat(false, size))); - NonNull::from(Box::leak(result)) -} - -/// Interpret the data as a series of bits and load then into a new [SPBitVec] instance. -/// -/// returns: [SPBitVec] instance containing data. Will never return NULL. -/// -/// # Panics -/// -/// - when `data` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory location of at least `data_length` -/// bytes in size. -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitvec_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_load( - data: *const u8, - data_length: usize, -) -> NonNull { - assert!(!data.is_null()); - let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPBitVec(servicepoint::BitVec::from_slice(data))); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPBitVec]. -/// -/// returns: new [SPBitVec] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - `bit_vec` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_bitvec_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_clone( - bit_vec: *const SPBitVec, -) -> NonNull { - assert!(!bit_vec.is_null()); - let result = Box::new((*bit_vec).clone()); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPBitVec]. -/// -/// # Panics -/// -/// - when `but_vec` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - `bit_vec` 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()); - _ = Box::from_raw(bit_vec); -} - -/// Gets the value of a bit from the [SPBitVec]. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to read from -/// - `index`: the bit index to read -/// -/// returns: value of the bit -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// - when accessing `index` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - `bit_vec` is not written to concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_get( - bit_vec: *const SPBitVec, - index: usize, -) -> bool { - assert!(!bit_vec.is_null()); - *(*bit_vec).0.get(index).unwrap() -} - -/// Sets the value of a bit in the [SPBitVec]. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to write to -/// - `index`: the bit index to edit -/// - `value`: the value to set the bit to -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// - when accessing `index` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - `bit_vec` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_set( - bit_vec: *mut SPBitVec, - index: usize, - value: bool, -) { - assert!(!bit_vec.is_null()); - (*bit_vec).0.set(index, value) -} - -/// Sets the value of all bits in the [SPBitVec]. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to write to -/// - `value`: the value to set all bits to -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - `bit_vec` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_fill(bit_vec: *mut SPBitVec, value: bool) { - assert!(!bit_vec.is_null()); - (*bit_vec).0.fill(value) -} - -/// Gets the length of the [SPBitVec] in bits. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to write to -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_len(bit_vec: *const SPBitVec) -> usize { - assert!(!bit_vec.is_null()); - (*bit_vec).0.len() -} - -/// Returns true if length is 0. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to write to -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_is_empty(bit_vec: *const SPBitVec) -> bool { - assert!(!bit_vec.is_null()); - (*bit_vec).0.is_empty() -} - -/// Gets an unsafe reference to the data of the [SPBitVec] instance. -/// -/// # Arguments -/// -/// - `bit_vec`: instance to write to -/// -/// # Panics -/// -/// - when `bit_vec` is NULL -/// -/// ## Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid [SPBitVec] -/// - the returned memory range is never accessed after the passed [SPBitVec] has been freed -/// - the returned memory range is never accessed concurrently, either via the [SPBitVec] or directly -#[no_mangle] -pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref( - bit_vec: *mut SPBitVec, -) -> SPByteSlice { - assert!(!bit_vec.is_null()); - let data = (*bit_vec).0.as_raw_mut_slice(); - SPByteSlice { - start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), - length: data.len(), - } -} diff --git a/crates/servicepoint_binding_c/src/brightness_grid.rs b/crates/servicepoint_binding_c/src/brightness_grid.rs deleted file mode 100644 index 83af008..0000000 --- a/crates/servicepoint_binding_c/src/brightness_grid.rs +++ /dev/null @@ -1,322 +0,0 @@ -//! C functions for interacting with [SPBrightnessGrid]s -//! -//! prefix `sp_brightness_grid_` - -use crate::SPByteSlice; -use servicepoint::{DataRef, Grid}; -use std::convert::Into; -use std::intrinsics::transmute; -use std::ptr::NonNull; - -/// see [servicepoint::Brightness::MIN] -pub const SP_BRIGHTNESS_MIN: u8 = 0; -/// see [servicepoint::Brightness::MAX] -pub const SP_BRIGHTNESS_MAX: u8 = 11; -/// Count of possible brightness values -pub const SP_BRIGHTNESS_LEVELS: u8 = 12; - -/// A grid containing brightness values. -/// -/// # Examples -/// ```C -/// SPConnection connection = sp_connection_open("127.0.0.1:2342"); -/// if (connection == NULL) -/// return 1; -/// -/// SPBrightnessGrid grid = sp_brightness_grid_new(2, 2); -/// sp_brightness_grid_set(grid, 0, 0, 0); -/// sp_brightness_grid_set(grid, 1, 1, 10); -/// -/// SPCommand command = sp_command_char_brightness(grid); -/// sp_connection_free(connection); -/// ``` -#[derive(Clone)] -pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); - -/// Creates a new [SPBrightnessGrid] with the specified dimensions. -/// -/// returns: [SPBrightnessGrid] initialized to 0. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_brightness_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_new( - width: usize, - height: usize, -) -> NonNull { - let result = Box::new(SPBrightnessGrid(servicepoint::BrightnessGrid::new( - width, height, - ))); - NonNull::from(Box::leak(result)) -} - -/// Loads a [SPBrightnessGrid] with the specified dimensions from the provided data. -/// -/// returns: new [SPBrightnessGrid] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `data` is NULL -/// - when the provided `data_length` does not match `height` and `width` -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory location of at least `data_length` -/// bytes in size. -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_brightness_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_load( - width: usize, - height: usize, - data: *const u8, - data_length: usize, -) -> NonNull { - assert!(!data.is_null()); - let data = std::slice::from_raw_parts(data, data_length); - let grid = servicepoint::ByteGrid::load(width, height, data); - let grid = servicepoint::BrightnessGrid::try_from(grid) - .expect("invalid brightness value"); - let result = Box::new(SPBrightnessGrid(grid)); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPBrightnessGrid]. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// -/// returns: new [SPBrightnessGrid] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -/// - `brightness_grid` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_brightness_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_clone( - brightness_grid: *const SPBrightnessGrid, -) -> NonNull { - assert!(!brightness_grid.is_null()); - let result = Box::new((*brightness_grid).clone()); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPBrightnessGrid]. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -/// - `brightness_grid` 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, -) { - assert!(!brightness_grid.is_null()); - _ = Box::from_raw(brightness_grid); -} - -/// Gets the current value at the specified position. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// - `x` and `y`: position of the cell to read -/// -/// returns: value at position -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// - When accessing `x` or `y` out of bounds. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -/// - `brightness_grid` is not written to concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_get( - brightness_grid: *const SPBrightnessGrid, - x: usize, - y: usize, -) -> u8 { - assert!(!brightness_grid.is_null()); - (*brightness_grid).0.get(x, y).into() -} - -/// Sets the value of the specified position in the [SPBrightnessGrid]. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to write to -/// - `x` and `y`: position of the cell -/// - `value`: the value to write to the cell -/// -/// returns: old value of the cell -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// - When accessing `x` or `y` out of bounds. -/// - When providing an invalid brightness value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `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( - brightness_grid: *mut SPBrightnessGrid, - x: usize, - y: usize, - value: u8, -) { - assert!(!brightness_grid.is_null()); - let brightness = servicepoint::Brightness::try_from(value) - .expect("invalid brightness value"); - (*brightness_grid).0.set(x, y, brightness); -} - -/// Sets the value of all cells in the [SPBrightnessGrid]. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to write to -/// - `value`: the value to set all cells to -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// - When providing an invalid brightness value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -/// - `brightness_grid` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_fill( - brightness_grid: *mut SPBrightnessGrid, - value: u8, -) { - assert!(!brightness_grid.is_null()); - let brightness = servicepoint::Brightness::try_from(value) - .expect("invalid brightness value"); - (*brightness_grid).0.fill(brightness); -} - -/// Gets the width of the [SPBrightnessGrid] instance. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// -/// returns: width -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_width( - brightness_grid: *const SPBrightnessGrid, -) -> usize { - assert!(!brightness_grid.is_null()); - (*brightness_grid).0.width() -} - -/// Gets the height of the [SPBrightnessGrid] instance. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// -/// returns: height -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_height( - brightness_grid: *const SPBrightnessGrid, -) -> usize { - assert!(!brightness_grid.is_null()); - (*brightness_grid).0.height() -} - -/// Gets an unsafe reference to the data of the [SPBrightnessGrid] instance. -/// -/// # Arguments -/// -/// - `brightness_grid`: instance to read from -/// -/// returns: slice of bytes underlying the `brightness_grid`. -/// -/// # Panics -/// -/// - when `brightness_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `brightness_grid` points to a valid [SPBrightnessGrid] -/// - the returned memory range is never accessed after the passed [SPBrightnessGrid] has been freed -/// - the returned memory range is never accessed concurrently, either via the [SPBrightnessGrid] or directly -#[no_mangle] -pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( - brightness_grid: *mut SPBrightnessGrid, -) -> SPByteSlice { - assert!(!brightness_grid.is_null()); - assert_eq!(core::mem::size_of::(), 1); - let data = (*brightness_grid).0.data_ref_mut(); - // this assumes more about the memory layout than rust guarantees. yikes! - let data: &mut [u8] = transmute(data); - SPByteSlice { - start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), - length: data.len(), - } -} diff --git a/crates/servicepoint_binding_c/src/byte_slice.rs b/crates/servicepoint_binding_c/src/byte_slice.rs deleted file mode 100644 index 678a5d7..0000000 --- a/crates/servicepoint_binding_c/src/byte_slice.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! FFI slice helper - -use std::ptr::NonNull; - -#[repr(C)] -/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. -/// -/// You should not create an instance of this type in your C code. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - accesses to the memory pointed to with `start` is never accessed outside `length` -/// - the lifetime of the `CByteSlice` does not outlive the memory it points to, as described in -/// the function returning this type. -/// - an instance of this created from C is never passed to a consuming function, as the rust code -/// will try to free the memory of a potentially separate allocator. -pub struct SPByteSlice { - /// The start address of the memory - pub start: NonNull, - /// The amount of memory in bytes - pub length: usize, -} diff --git a/crates/servicepoint_binding_c/src/char_grid.rs b/crates/servicepoint_binding_c/src/char_grid.rs deleted file mode 100644 index dfaf225..0000000 --- a/crates/servicepoint_binding_c/src/char_grid.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! C functions for interacting with [SPCharGrid]s -//! -//! prefix `sp_char_grid_` - -use servicepoint::Grid; -use std::ptr::NonNull; - -/// A C-wrapper for grid containing UTF-8 characters. -/// -/// As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers. -/// -/// The encoding is enforced in most cases by the rust standard library -/// and will panic when provided with illegal characters. -/// -/// # Examples -/// -/// ```C -/// CharGrid grid = sp_char_grid_new(4, 3); -/// sp_char_grid_fill(grid, '?'); -/// sp_char_grid_set(grid, 0, 0, '!'); -/// sp_char_grid_free(grid); -/// ``` -pub struct SPCharGrid(pub(crate) servicepoint::CharGrid); - -impl Clone for SPCharGrid { - fn clone(&self) -> Self { - SPCharGrid(self.0.clone()) - } -} - -/// Creates a new [SPCharGrid] with the specified dimensions. -/// -/// returns: [SPCharGrid] initialized to 0. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_char_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_new( - width: usize, - height: usize, -) -> NonNull { - let result = - Box::new(SPCharGrid(servicepoint::CharGrid::new(width, height))); - NonNull::from(Box::leak(result)) -} - -/// Loads a [SPCharGrid] with the specified dimensions from the provided data. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `data` is NULL -/// - when the provided `data_length` does not match `height` and `width` -/// - when `data` is not valid UTF-8 -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory location of at least `data_length` -/// bytes in size. -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_char_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_load( - width: usize, - height: usize, - data: *const u8, - data_length: usize, -) -> NonNull { - assert!(data.is_null()); - let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPCharGrid( - servicepoint::CharGrid::load_utf8(width, height, data.to_vec()) - .unwrap(), - )); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPCharGrid]. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -/// - `char_grid` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_char_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_clone( - char_grid: *const SPCharGrid, -) -> NonNull { - assert!(!char_grid.is_null()); - let result = Box::new((*char_grid).clone()); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPCharGrid]. -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -/// - `char_grid` is not used concurrently or after char_grid call -/// - `char_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_char_grid_free(char_grid: *mut SPCharGrid) { - assert!(!char_grid.is_null()); - _ = Box::from_raw(char_grid); -} - -/// Gets the current value at the specified position. -/// -/// # Arguments -/// -/// - `char_grid`: instance to read from -/// - `x` and `y`: position of the cell to read -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -/// - `char_grid` is not written to concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_get( - char_grid: *const SPCharGrid, - x: usize, - y: usize, -) -> u32 { - assert!(!char_grid.is_null()); - (*char_grid).0.get(x, y) as u32 -} - -/// Sets the value of the specified position in the [SPCharGrid]. -/// -/// # Arguments -/// -/// - `char_grid`: instance to write to -/// - `x` and `y`: position of the cell -/// - `value`: the value to write to the cell -/// -/// returns: old value of the cell -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPBitVec] -/// - `char_grid` is not written to or read from concurrently -/// -/// [SPBitVec]: [crate::SPBitVec] -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_set( - char_grid: *mut SPCharGrid, - x: usize, - y: usize, - value: u32, -) { - assert!(!char_grid.is_null()); - (*char_grid).0.set(x, y, char::from_u32(value).unwrap()); -} - -/// Sets the value of all cells in the [SPCharGrid]. -/// -/// # Arguments -/// -/// - `char_grid`: instance to write to -/// - `value`: the value to set all cells to -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -/// - `char_grid` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_fill( - char_grid: *mut SPCharGrid, - value: u32, -) { - assert!(!char_grid.is_null()); - (*char_grid).0.fill(char::from_u32(value).unwrap()); -} - -/// Gets the width of the [SPCharGrid] instance. -/// -/// # Arguments -/// -/// - `char_grid`: instance to read from -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_width( - char_grid: *const SPCharGrid, -) -> usize { - assert!(!char_grid.is_null()); - (*char_grid).0.width() -} - -/// Gets the height of the [SPCharGrid] instance. -/// -/// # Arguments -/// -/// - `char_grid`: instance to read from -/// -/// # Panics -/// -/// - when `char_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `char_grid` points to a valid [SPCharGrid] -#[no_mangle] -pub unsafe extern "C" fn sp_char_grid_height( - char_grid: *const SPCharGrid, -) -> usize { - assert!(!char_grid.is_null()); - (*char_grid).0.height() -} diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs deleted file mode 100644 index f7e50ea..0000000 --- a/crates/servicepoint_binding_c/src/command.rs +++ /dev/null @@ -1,498 +0,0 @@ -//! C functions for interacting with [SPCommand]s -//! -//! prefix `sp_command_` - -use std::ptr::{null_mut, NonNull}; - -use crate::{ - SPBitVec, SPBitmap, SPBrightnessGrid, SPCharGrid, SPCompressionCode, - SPCp437Grid, SPPacket, -}; - -/// A low-level display command. -/// -/// This struct and associated functions implement the UDP protocol for the display. -/// -/// To send a [SPCommand], use a [SPConnection]. -/// -/// # Examples -/// -/// ```C -/// 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 { - fn clone(&self) -> Self { - SPCommand(self.0.clone()) - } -} - -/// Tries to turn a [SPPacket] into a [SPCommand]. -/// -/// The packet is deallocated in the process. -/// -/// Returns: pointer to new [SPCommand] instance or NULL if parsing failed. -/// -/// # Panics -/// -/// - when `packet` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - [SPPacket] points to a valid instance of [SPPacket] -/// - [SPPacket] is not used concurrently or after this call -/// - the result is checked for NULL -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_try_from_packet( - packet: *mut SPPacket, -) -> *mut SPCommand { - let packet = *Box::from_raw(packet); - match servicepoint::Command::try_from(packet.0) { - Err(_) => null_mut(), - Ok(command) => Box::into_raw(Box::new(SPCommand(command))), - } -} - -/// Clones a [SPCommand] instance. -/// -/// returns: new [SPCommand] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `command` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `command` points to a valid instance of [SPCommand] -/// - `command` is not written to concurrently -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_clone( - command: *const SPCommand, -) -> NonNull { - assert!(!command.is_null()); - let result = Box::new((*command).clone()); - NonNull::from(Box::leak(result)) -} - -/// Set all pixels to the off state. -/// -/// Does not affect brightness. -/// -/// Returns: a new [servicepoint::Command::Clear] instance. Will never return NULL. -/// -/// # Examples -/// -/// ```C -/// sp_connection_send_command(connection, sp_command_clear()); -/// ``` -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_clear() -> NonNull { - let result = Box::new(SPCommand(servicepoint::Command::Clear)); - NonNull::from(Box::leak(result)) -} - -/// Kills the udp daemon on the display, which usually results in a restart. -/// -/// Please do not send this in your normal program flow. -/// -/// Returns: a new [servicepoint::Command::HardReset] instance. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull { - let result = Box::new(SPCommand(servicepoint::Command::HardReset)); - NonNull::from(Box::leak(result)) -} - -/// A yet-to-be-tested command. -/// -/// Returns: a new [servicepoint::Command::FadeOut] instance. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { - let result = Box::new(SPCommand(servicepoint::Command::FadeOut)); - NonNull::from(Box::leak(result)) -} - -/// Set the brightness of all tiles to the same value. -/// -/// Returns: a new [servicepoint::Command::Brightness] instance. Will never return NULL. -/// -/// # Panics -/// -/// - When the provided brightness value is out of range (0-11). -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_brightness( - brightness: u8, -) -> NonNull { - let brightness = servicepoint::Brightness::try_from(brightness) - .expect("invalid brightness"); - let result = - Box::new(SPCommand(servicepoint::Command::Brightness(brightness))); - NonNull::from(Box::leak(result)) -} - -/// Set the brightness of individual tiles in a rectangular area of the display. -/// -/// The passed [SPBrightnessGrid] gets consumed. -/// -/// Returns: a new [servicepoint::Command::CharBrightness] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `grid` points to a valid instance of [SPBrightnessGrid] -/// - `grid` is not used concurrently or after this call -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_char_brightness( - x: usize, - y: usize, - grid: *mut SPBrightnessGrid, -) -> NonNull { - assert!(!grid.is_null()); - let byte_grid = *Box::from_raw(grid); - let result = Box::new(SPCommand(servicepoint::Command::CharBrightness( - servicepoint::Origin::new(x, y), - byte_grid.0, - ))); - NonNull::from(Box::leak(result)) -} - -/// Set pixel data starting at the pixel offset on screen. -/// -/// The screen will continuously overwrite more pixel data without regarding the offset, meaning -/// once the starting row is full, overwriting will continue on column 0. -/// -/// The contained [SPBitVec] is always uncompressed. -/// -/// The passed [SPBitVec] gets consumed. -/// -/// Returns: a new [servicepoint::Command::BitmapLinear] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bit_vec` is null -/// - when `compression_code` is not a valid value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid instance of [SPBitVec] -/// - `bit_vec` is not used concurrently or after this call -/// - `compression` matches one of the allowed enum values -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_bitmap_linear( - offset: usize, - bit_vec: *mut SPBitVec, - compression: SPCompressionCode, -) -> NonNull { - assert!(!bit_vec.is_null()); - let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand(servicepoint::Command::BitmapLinear( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ))); - NonNull::from(Box::leak(result)) -} - -/// Set pixel data according to an and-mask starting at the offset. -/// -/// The screen will continuously overwrite more pixel data without regarding the offset, meaning -/// once the starting row is full, overwriting will continue on column 0. -/// -/// The contained [SPBitVec] is always uncompressed. -/// -/// The passed [SPBitVec] gets consumed. -/// -/// Returns: a new [servicepoint::Command::BitmapLinearAnd] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bit_vec` is null -/// - when `compression_code` is not a valid value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid instance of [SPBitVec] -/// - `bit_vec` is not used concurrently or after this call -/// - `compression` matches one of the allowed enum values -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_bitmap_linear_and( - offset: usize, - bit_vec: *mut SPBitVec, - compression: SPCompressionCode, -) -> NonNull { - assert!(!bit_vec.is_null()); - let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ))); - NonNull::from(Box::leak(result)) -} - -/// Set pixel data according to an or-mask starting at the offset. -/// -/// The screen will continuously overwrite more pixel data without regarding the offset, meaning -/// once the starting row is full, overwriting will continue on column 0. -/// -/// The contained [SPBitVec] is always uncompressed. -/// -/// The passed [SPBitVec] gets consumed. -/// -/// Returns: a new [servicepoint::Command::BitmapLinearOr] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bit_vec` is null -/// - when `compression_code` is not a valid value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid instance of [SPBitVec] -/// - `bit_vec` is not used concurrently or after this call -/// - `compression` matches one of the allowed enum values -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_bitmap_linear_or( - offset: usize, - bit_vec: *mut SPBitVec, - compression: SPCompressionCode, -) -> NonNull { - assert!(!bit_vec.is_null()); - let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ))); - NonNull::from(Box::leak(result)) -} - -/// Set pixel data according to a xor-mask starting at the offset. -/// -/// The screen will continuously overwrite more pixel data without regarding the offset, meaning -/// once the starting row is full, overwriting will continue on column 0. -/// -/// The contained [SPBitVec] is always uncompressed. -/// -/// The passed [SPBitVec] gets consumed. -/// -/// Returns: a new [servicepoint::Command::BitmapLinearXor] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bit_vec` is null -/// - when `compression_code` is not a valid value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bit_vec` points to a valid instance of [SPBitVec] -/// - `bit_vec` is not used concurrently or after this call -/// - `compression` matches one of the allowed enum values -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_bitmap_linear_xor( - offset: usize, - bit_vec: *mut SPBitVec, - compression: SPCompressionCode, -) -> NonNull { - assert!(!bit_vec.is_null()); - let bit_vec = *Box::from_raw(bit_vec); - let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( - offset, - bit_vec.into(), - compression.try_into().expect("invalid compression code"), - ))); - NonNull::from(Box::leak(result)) -} - -/// Show codepage 437 encoded text on the screen. -/// -/// The passed [SPCp437Grid] gets consumed. -/// -/// Returns: a new [servicepoint::Command::Cp437Data] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `grid` is null -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `grid` points to a valid instance of [SPCp437Grid] -/// - `grid` is not used concurrently or after this call -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_cp437_data( - x: usize, - y: usize, - grid: *mut SPCp437Grid, -) -> NonNull { - assert!(!grid.is_null()); - let grid = *Box::from_raw(grid); - let result = Box::new(SPCommand(servicepoint::Command::Cp437Data( - servicepoint::Origin::new(x, y), - grid.0, - ))); - NonNull::from(Box::leak(result)) -} - -/// Show UTF-8 encoded text on the screen. -/// -/// The passed [SPCharGrid] gets consumed. -/// -/// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `grid` is null -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `grid` points to a valid instance of [SPCharGrid] -/// - `grid` is not used concurrently or after this call -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_utf8_data( - x: usize, - y: usize, - grid: *mut SPCharGrid, -) -> NonNull { - assert!(!grid.is_null()); - let grid = *Box::from_raw(grid); - let result = Box::new(SPCommand(servicepoint::Command::Utf8Data( - servicepoint::Origin::new(x, y), - grid.0, - ))); - NonNull::from(Box::leak(result)) -} - -/// Sets a window of pixels to the specified values. -/// -/// The passed [SPBitmap] gets consumed. -/// -/// Returns: a new [servicepoint::Command::BitmapLinearWin] instance. Will never return NULL. -/// -/// # Panics -/// -/// - when `bitmap` is null -/// - when `compression_code` is not a valid value -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `bitmap` points to a valid instance of [SPBitmap] -/// - `bitmap` is not used concurrently or after this call -/// - `compression` matches one of the allowed enum values -/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_command_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_command_bitmap_linear_win( - x: usize, - y: usize, - bitmap: *mut SPBitmap, - compression_code: SPCompressionCode, -) -> NonNull { - assert!(!bitmap.is_null()); - let byte_grid = (*Box::from_raw(bitmap)).0; - let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( - servicepoint::Origin::new(x, y), - byte_grid, - compression_code - .try_into() - .expect("invalid compression code"), - ))); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPCommand]. -/// -/// # Examples -/// -/// ```C -/// SPCommand c = sp_command_clear(); -/// sp_command_free(c); -/// ``` -/// -/// # Panics -/// -/// - when `command` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `command` points to a valid [SPCommand] -/// - `command` is not used concurrently or after this call -/// - `command` was not passed to another consuming function, e.g. to create a [SPPacket] -#[no_mangle] -pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { - assert!(!command.is_null()); - _ = Box::from_raw(command); -} diff --git a/crates/servicepoint_binding_c/src/connection.rs b/crates/servicepoint_binding_c/src/connection.rs deleted file mode 100644 index 8b31243..0000000 --- a/crates/servicepoint_binding_c/src/connection.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! C functions for interacting with [SPConnection]s -//! -//! prefix `sp_connection_` - -use std::ffi::{c_char, CStr}; -use std::ptr::{null_mut, NonNull}; - -use crate::{SPCommand, SPPacket}; - -/// A connection to the display. -/// -/// # Examples -/// -/// ```C -/// CConnection connection = sp_connection_open("172.23.42.29:2342"); -/// if (connection != NULL) -/// sp_connection_send_command(connection, sp_command_clear()); -/// ``` -pub struct SPConnection(pub(crate) servicepoint::Connection); - -/// Creates a new instance of [SPConnection]. -/// -/// returns: NULL if connection fails, or connected instance -/// -/// # Panics -/// -/// - when `host` is null or an invalid host -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_connection_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_connection_open( - host: *const c_char, -) -> *mut SPConnection { - assert!(!host.is_null()); - let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); - let connection = match servicepoint::Connection::open(host) { - Err(_) => return null_mut(), - Ok(value) => value, - }; - - Box::into_raw(Box::new(SPConnection(connection))) -} - -/// Creates a new instance of [SPConnection] for testing that does not actually send anything. -/// -/// returns: a new instance. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_connection_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_connection_fake() -> NonNull { - let result = Box::new(SPConnection(servicepoint::Connection::Fake)); - NonNull::from(Box::leak(result)) -} - -/// Sends a [SPPacket] to the display using the [SPConnection]. -/// -/// The passed `packet` gets consumed. -/// -/// returns: true in case of success -/// -/// # Panics -/// -/// - when `connection` is NULL -/// - when `packet` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `connection` points to a valid instance of [SPConnection] -/// - `packet` points to a valid instance of [SPPacket] -/// - `packet` is not used concurrently or after this call -#[no_mangle] -pub unsafe extern "C" fn sp_connection_send_packet( - connection: *const SPConnection, - packet: *mut SPPacket, -) -> bool { - assert!(!connection.is_null()); - assert!(!packet.is_null()); - let packet = Box::from_raw(packet); - (*connection).0.send((*packet).0).is_ok() -} - -/// Sends a [SPCommand] to the display using the [SPConnection]. -/// -/// The passed `command` gets consumed. -/// -/// returns: true in case of success -/// -/// # Panics -/// -/// - when `connection` is NULL -/// - when `command` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `connection` points to a valid instance of [SPConnection] -/// - `command` points to a valid instance of [SPPacket] -/// - `command` is not used concurrently or after this call -#[no_mangle] -pub unsafe extern "C" fn sp_connection_send_command( - connection: *const SPConnection, - command: *mut SPCommand, -) -> bool { - assert!(!connection.is_null()); - assert!(!command.is_null()); - let command = (*Box::from_raw(command)).0; - (*connection).0.send(command).is_ok() -} - -/// Closes and deallocates a [SPConnection]. -/// -/// # Panics -/// -/// - when `connection` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `connection` points to a valid [SPConnection] -/// - `connection` is not used concurrently or after this call -#[no_mangle] -pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { - assert!(!connection.is_null()); - _ = Box::from_raw(connection); -} diff --git a/crates/servicepoint_binding_c/src/constants.rs b/crates/servicepoint_binding_c/src/constants.rs deleted file mode 100644 index 1a268f4..0000000 --- a/crates/servicepoint_binding_c/src/constants.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! re-exported constants for use in C - -use servicepoint::CompressionCode; -use std::time::Duration; - -/// size of a single tile in one dimension -pub const SP_TILE_SIZE: usize = 8; - -/// Display tile count in the x-direction -pub const SP_TILE_WIDTH: usize = 56; - -/// Display tile count in the y-direction -pub const SP_TILE_HEIGHT: usize = 20; - -/// Display width in pixels -pub const SP_PIXEL_WIDTH: usize = SP_TILE_WIDTH * SP_TILE_SIZE; - -/// Display height in pixels -pub const SP_PIXEL_HEIGHT: usize = SP_TILE_HEIGHT * SP_TILE_SIZE; - -/// pixel count on whole screen -pub const SP_PIXEL_COUNT: usize = SP_PIXEL_WIDTH * SP_PIXEL_HEIGHT; - -/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. -pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); - -/// Specifies the kind of compression to use. -#[repr(u16)] -pub enum SPCompressionCode { - /// no compression - Uncompressed = 0x0, - /// compress using flate2 with zlib header - Zlib = 0x677a, - /// compress using bzip2 - Bzip2 = 0x627a, - /// compress using lzma - Lzma = 0x6c7a, - /// compress using Zstandard - Zstd = 0x7a73, -} - -impl TryFrom for CompressionCode { - type Error = (); - - fn try_from(value: SPCompressionCode) -> Result { - CompressionCode::try_from(value as u16) - } -} diff --git a/crates/servicepoint_binding_c/src/cp437_grid.rs b/crates/servicepoint_binding_c/src/cp437_grid.rs deleted file mode 100644 index 9b366c8..0000000 --- a/crates/servicepoint_binding_c/src/cp437_grid.rs +++ /dev/null @@ -1,285 +0,0 @@ -//! C functions for interacting with [SPCp437Grid]s -//! -//! prefix `sp_cp437_grid_` - -use crate::SPByteSlice; -use servicepoint::{DataRef, Grid}; -use std::ptr::NonNull; - -/// A C-wrapper for grid containing codepage 437 characters. -/// -/// The encoding is currently not enforced. -/// -/// # Examples -/// -/// ```C -/// Cp437Grid grid = sp_cp437_grid_new(4, 3); -/// sp_cp437_grid_fill(grid, '?'); -/// sp_cp437_grid_set(grid, 0, 0, '!'); -/// sp_cp437_grid_free(grid); -/// ``` -pub struct SPCp437Grid(pub(crate) servicepoint::Cp437Grid); - -impl Clone for SPCp437Grid { - fn clone(&self) -> Self { - SPCp437Grid(self.0.clone()) - } -} - -/// Creates a new [SPCp437Grid] with the specified dimensions. -/// -/// returns: [SPCp437Grid] initialized to 0. Will never return NULL. -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_cp437_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_new( - width: usize, - height: usize, -) -> NonNull { - let result = - Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height))); - NonNull::from(Box::leak(result)) -} - -/// Loads a [SPCp437Grid] with the specified dimensions from the provided data. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `data` is NULL -/// - when the provided `data_length` does not match `height` and `width` -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory location of at least `data_length` -/// bytes in size. -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_cp437_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_load( - width: usize, - height: usize, - data: *const u8, - data_length: usize, -) -> NonNull { - assert!(data.is_null()); - let data = std::slice::from_raw_parts(data, data_length); - let result = Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( - width, height, data, - ))); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPCp437Grid]. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -/// - `cp437_grid` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_cp437_grid_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_clone( - cp437_grid: *const SPCp437Grid, -) -> NonNull { - assert!(!cp437_grid.is_null()); - let result = Box::new((*cp437_grid).clone()); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPCp437Grid]. -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -/// - `cp437_grid` 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()); - _ = Box::from_raw(cp437_grid); -} - -/// Gets the current value at the specified position. -/// -/// # Arguments -/// -/// - `cp437_grid`: instance to read from -/// - `x` and `y`: position of the cell to read -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -/// - `cp437_grid` is not written to concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_get( - cp437_grid: *const SPCp437Grid, - x: usize, - y: usize, -) -> u8 { - assert!(!cp437_grid.is_null()); - (*cp437_grid).0.get(x, y) -} - -/// Sets the value of the specified position in the [SPCp437Grid]. -/// -/// # Arguments -/// -/// - `cp437_grid`: instance to write to -/// - `x` and `y`: position of the cell -/// - `value`: the value to write to the cell -/// -/// returns: old value of the cell -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// - when accessing `x` or `y` out of bounds -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPBitVec] -/// - `cp437_grid` 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, - x: usize, - y: usize, - value: u8, -) { - assert!(!cp437_grid.is_null()); - (*cp437_grid).0.set(x, y, value); -} - -/// Sets the value of all cells in the [SPCp437Grid]. -/// -/// # Arguments -/// -/// - `cp437_grid`: instance to write to -/// - `value`: the value to set all cells to -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -/// - `cp437_grid` is not written to or read from concurrently -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_fill( - cp437_grid: *mut SPCp437Grid, - value: u8, -) { - assert!(!cp437_grid.is_null()); - (*cp437_grid).0.fill(value); -} - -/// Gets the width of the [SPCp437Grid] instance. -/// -/// # Arguments -/// -/// - `cp437_grid`: instance to read from -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_width( - cp437_grid: *const SPCp437Grid, -) -> usize { - assert!(!cp437_grid.is_null()); - (*cp437_grid).0.width() -} - -/// Gets the height of the [SPCp437Grid] instance. -/// -/// # Arguments -/// -/// - `cp437_grid`: instance to read from -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_height( - cp437_grid: *const SPCp437Grid, -) -> usize { - assert!(!cp437_grid.is_null()); - (*cp437_grid).0.height() -} - -/// Gets an unsafe reference to the data of the [SPCp437Grid] instance. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `cp437_grid` is NULL -/// -/// ## Safety -/// -/// The caller has to make sure that: -/// -/// - `cp437_grid` points to a valid [SPCp437Grid] -/// - the returned memory range is never accessed after the passed [SPCp437Grid] has been freed -/// - the returned memory range is never accessed concurrently, either via the [SPCp437Grid] or directly -#[no_mangle] -pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( - cp437_grid: *mut SPCp437Grid, -) -> SPByteSlice { - let data = (*cp437_grid).0.data_ref_mut(); - SPByteSlice { - start: NonNull::new(data.as_mut_ptr_range().start).unwrap(), - length: data.len(), - } -} diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs deleted file mode 100644 index 887fb40..0000000 --- a/crates/servicepoint_binding_c/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! C API wrapper for the [servicepoint](https://docs.rs/servicepoint/latest/servicepoint/) crate. -//! -//! # Examples -//! -//! Make sure to check out [this GitHub repo](https://github.com/arfst23/ServicePoint) as well! -//! -//! ```C -//! #include -//! #include "servicepoint.h" -//! -//! int main(void) { -//! SPConnection *connection = sp_connection_open("172.23.42.29:2342"); -//! if (connection == NULL) -//! return 1; -//! -//! SPBitmap *pixels = sp_bitmap_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); -//! sp_bitmap_fill(pixels, true); -//! -//! SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); -//! while (sp_connection_send_command(connection, sp_command_clone(command))); -//! -//! sp_command_free(command); -//! sp_connection_free(connection); -//! return 0; -//! } -//! ``` - -pub use crate::bitmap::*; -pub use crate::bitvec::*; -pub use crate::brightness_grid::*; -pub use crate::byte_slice::*; -pub use crate::char_grid::*; -pub use crate::command::*; -pub use crate::connection::*; -pub use crate::constants::*; -pub use crate::cp437_grid::*; -pub use crate::packet::*; - -mod bitmap; -mod bitvec; -mod brightness_grid; -mod byte_slice; -mod char_grid; -mod command; -mod connection; -mod constants; -mod cp437_grid; -mod packet; diff --git a/crates/servicepoint_binding_c/src/packet.rs b/crates/servicepoint_binding_c/src/packet.rs deleted file mode 100644 index 9293a8a..0000000 --- a/crates/servicepoint_binding_c/src/packet.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! C functions for interacting with [SPPacket]s -//! -//! prefix `sp_packet_` - -use std::ptr::{null_mut, NonNull}; - -use crate::SPCommand; - -/// The raw packet -pub struct SPPacket(pub(crate) servicepoint::Packet); - -/// Turns a [SPCommand] into a [SPPacket]. -/// The [SPCommand] gets consumed. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `command` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - [SPCommand] points to a valid instance of [SPCommand] -/// - [SPCommand] is not used concurrently or after this call -/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_packet_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_from_command( - command: *mut SPCommand, -) -> NonNull { - assert!(!command.is_null()); - let command = *Box::from_raw(command); - let result = Box::new(SPPacket(command.0.into())); - NonNull::from(Box::leak(result)) -} - -/// Tries to load a [SPPacket] from the passed array with the specified length. -/// -/// returns: NULL in case of an error, pointer to the allocated packet otherwise -/// -/// # Panics -/// -/// - when `data` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `data` points to a valid memory region of at least `length` bytes -/// - `data` is not written to concurrently -/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_packet_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_try_load( - data: *const u8, - length: usize, -) -> *mut SPPacket { - assert!(!data.is_null()); - let data = std::slice::from_raw_parts(data, length); - match servicepoint::Packet::try_from(data) { - Err(_) => null_mut(), - Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), - } -} - -/// Creates a raw [SPPacket] from parts. -/// -/// # Arguments -/// -/// - `command_code` specifies which command this packet contains -/// - `a`, `b`, `c` and `d` are command-specific header values -/// - `payload` is the optional data that is part of the command -/// - `payload_len` is the size of the payload -/// -/// returns: new instance. Will never return null. -/// -/// # Panics -/// -/// - when `payload` is null, but `payload_len` is not zero -/// - when `payload_len` is zero, but `payload` is nonnull -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `payload` points to a valid memory region of at least `payload_len` bytes -/// - `payload` is not written to concurrently -/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or -/// by explicitly calling [sp_packet_free]. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_from_parts( - command_code: u16, - a: u16, - b: u16, - c: u16, - d: u16, - payload: *const u8, - payload_len: usize, -) -> NonNull { - assert_eq!(payload.is_null(), payload_len == 0); - - let payload = if payload.is_null() { - vec![] - } else { - let payload = std::slice::from_raw_parts(payload, payload_len); - Vec::from(payload) - }; - - let packet = servicepoint::Packet { - header: servicepoint::Header { - command_code, - a, - b, - c, - d, - }, - payload, - }; - let result = Box::new(SPPacket(packet)); - NonNull::from(Box::leak(result)) -} - -/// Clones a [SPPacket]. -/// -/// Will never return NULL. -/// -/// # Panics -/// -/// - when `packet` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `packet` points to a valid [SPPacket] -/// - `packet` is not written to concurrently -/// - the returned instance is freed in some way, either by using a consuming function or -/// by explicitly calling `sp_packet_free`. -#[no_mangle] -pub unsafe extern "C" fn sp_packet_clone( - packet: *const SPPacket, -) -> NonNull { - assert!(!packet.is_null()); - let result = Box::new(SPPacket((*packet).0.clone())); - NonNull::from(Box::leak(result)) -} - -/// Deallocates a [SPPacket]. -/// -/// # Panics -/// -/// - when `packet` is NULL -/// -/// # Safety -/// -/// The caller has to make sure that: -/// -/// - `packet` points to a valid [SPPacket] -/// - `packet` is not used concurrently or after this call -#[no_mangle] -pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { - assert!(!packet.is_null()); - _ = Box::from_raw(packet) -} diff --git a/crates/servicepoint_binding_uniffi/Cargo.toml b/crates/servicepoint_binding_uniffi/Cargo.toml deleted file mode 100644 index 0af1af9..0000000 --- a/crates/servicepoint_binding_uniffi/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -name = "servicepoint_binding_uniffi" -version.workspace = true -publish = false -edition = "2021" -license = "GPL-3.0-or-later" -description = "C bindings for the servicepoint crate." -homepage = "https://docs.rs/crate/servicepoint_binding_c" -repository = "https://git.berlin.ccc.de/servicepoint/servicepoint" -#readme = "README.md" -keywords = ["cccb", "cccb-servicepoint", "uniffi"] - -[lib] -crate-type = ["cdylib"] - -[build-dependencies] -uniffi = { version = "0.25.3", features = ["build"] } - -[dependencies] -uniffi = { version = "0.25.3" } -thiserror.workspace = true - -[dependencies.servicepoint] -version = "0.13.1" -path = "../servicepoint" -features = ["all_compressions"] - -[dependencies.uniffi-bindgen-cs] -git = "https://github.com/NordSecurity/uniffi-bindgen-cs" -# tag="v0.8.3+v0.25.0" -rev = "f68639fbc720b50ebe561ba75c66c84dc456bdce" -optional = true - -[dependencies.uniffi-bindgen-go] -git = "https://github.com/NordSecurity/uniffi-bindgen-go.git" -# tag = "0.2.2+v0.25.0" -rev = "ba23bab72f1a9bcc39ce81924d3d9265598e017c" -optional = true - -[lints] -#workspace = true - -[package.metadata.docs.rs] -all-features = true - -[[bin]] -name = "uniffi-bindgen" -required-features = ["uniffi/cli"] - -[[bin]] -name = "uniffi-bindgen-cs" -required-features = ["cs"] - -[[bin]] -name = "uniffi-bindgen-go" -required-features = ["go"] - -[features] -default = [] -cs = ["dep:uniffi-bindgen-cs"] -go = ["dep:uniffi-bindgen-go"] diff --git a/crates/servicepoint_binding_uniffi/README.md b/crates/servicepoint_binding_uniffi/README.md deleted file mode 100644 index 27d05ed..0000000 --- a/crates/servicepoint_binding_uniffi/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# ServicePoint - -In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point -Display" or "Airport Display". - -This crate contains bindings for multiple programming languages, enabling non-rust-developers to use the library. - -Also take a look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for more -information. - -## Note on stability - -This library is still in early development. -You can absolutely use it, and it works, but expect minor breaking changes with every version bump. - -## Notes on differences to rust library - -- Performance will not be as good as the rust version: - - most objects are reference counted. - - objects with mutating methods will also have a MRSW lock -- You will not get rust backtraces in release builds of the native code -- Panic messages will work (PanicException) - -## Supported languages - -| Language | Support level | Notes | -|-----------|---------------|-------------------------------------------------------------------------------------------------| -| .NET (C#) | Full | see dedicated section | -| Ruby | Working | LD_LIBRARY_PATH has to be set, see example project | -| Python | Tested once | Required project file not included. The shared library will be loaded from the script location. | -| Go | untested | | -| Kotlin | untested | | -| Swift | untested | | - -## Installation - -Including this repository as a submodule and building from source is the recommended way of using the library. - -```bash -git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint.git -git commit -m "add servicepoint submodule" -``` - -Run `generate-bindings.sh` to regenerate all bindings. This will also build `libservicepoint.so` (or equivalent on your -platform). - -For languages not fully supported, there will be no project file for the library, just the naked source file(s). -If you successfully use a language, please open an issue or PR to add the missing ones. - -## .NET (C#) - -This is the best supported language. - -F# is not tested. If there are usability or functionality problems, please open an issue. - -Currently, the project file is hard-coded for Linux and will need tweaks for other platforms (e.g. `.dylib` instead of `.so`). - -You do not have to compile or copy the rust crate manually, as building `ServicePoint.csproj` also builds it. - -### Example - -```csharp -using System.Threading; -using ServicePoint; - -var connection = new Connection("127.0.0.1:2342"); -connection.Send(Command.Clear()); - -connection.Send(Command.Brightness(5)); - -var pixels = Bitmap.NewMaxSized(); -for (ulong offset = 0; offset < ulong.MaxValue; offset++) -{ - pixels.Fill(false); - - for (ulong y = 0; y < pixels.Height(); y++) - pixels.Set((y + offset) % pixels.Width(), y, true); - - connection.Send(Command.BitmapLinearWin(0, 0, pixels)); - Thread.Sleep(14); -} -``` - -A full example including project files is available as part of this crate. - -### Why is there no NuGet-Package? - -NuGet packages are not a good way to distribute native -binaries ([relevant issue](https://github.com/dotnet/sdk/issues/33845)). -Because of that, there is no NuGet package you can use directly. diff --git a/crates/servicepoint_binding_uniffi/generate-bindings.sh b/crates/servicepoint_binding_uniffi/generate-bindings.sh deleted file mode 100755 index bfb571c..0000000 --- a/crates/servicepoint_binding_uniffi/generate-bindings.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -e - -cargo build --release - -SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -TARGET_PATH="$(realpath "$SCRIPT_PATH"/../../target/release)" -SERVICEPOINT_SO="$TARGET_PATH/libservicepoint_binding_uniffi.so" -LIBRARIES_PATH="$SCRIPT_PATH/libraries" - -echo "Source: $SERVICEPOINT_SO" -echo "Output: $LIBRARIES_PATH" - -BINDGEN="cargo run --features=uniffi/cli --bin uniffi-bindgen -- " -BINDGEN_CS="cargo run --features=cs --bin uniffi-bindgen-cs -- " -BINDGEN_GO="cargo run --features=go --bin uniffi-bindgen-go -- " -COMMON_ARGS="--library $SERVICEPOINT_SO" - -${BINDGEN} generate $COMMON_ARGS --language python --out-dir "$LIBRARIES_PATH/python" -${BINDGEN} generate $COMMON_ARGS --language kotlin --out-dir "$LIBRARIES_PATH/kotlin" -${BINDGEN} generate $COMMON_ARGS --language swift --out-dir "$LIBRARIES_PATH/swift" -${BINDGEN} generate $COMMON_ARGS --language ruby --out-dir "$LIBRARIES_PATH/ruby/lib" -${BINDGEN_CS} $COMMON_ARGS --out-dir "$LIBRARIES_PATH/csharp/ServicePoint" -${BINDGEN_GO} $COMMON_ARGS --out-dir "$LIBRARIES_PATH/go/" diff --git a/crates/servicepoint_binding_uniffi/libraries/.gitignore b/crates/servicepoint_binding_uniffi/libraries/.gitignore deleted file mode 100644 index a875c72..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -go -kotlin -python -swift \ No newline at end of file diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore b/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore deleted file mode 100644 index 1746e32..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin -obj diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile b/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile deleted file mode 100644 index f44ed21..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' - -gem 'servicepoint', path: '..' diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile.lock b/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile.lock deleted file mode 100644 index 2b6d5f7..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/example/Gemfile.lock +++ /dev/null @@ -1,19 +0,0 @@ -PATH - remote: .. - specs: - servicepoint (0.0.0) - ffi - -GEM - remote: https://rubygems.org/ - specs: - ffi (1.17.0-x86_64-linux-gnu) - -PLATFORMS - x86_64-linux - -DEPENDENCIES - servicepoint! - -BUNDLED WITH - 2.3.27 diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.rb b/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.rb deleted file mode 100644 index 8d212f2..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.rb +++ /dev/null @@ -1,25 +0,0 @@ -require_relative "../lib/servicepoint_binding_uniffi" - -include ServicepointBindingUniffi - -connection = Connection.new("172.23.42.29:2342") - -pixels = Bitmap.new_max_sized -x_offset = 0 -loop do - - pixels.fill(false) - - (0..((pixels.height) -1)).each do |y| - pixels.set((y + x_offset) % pixels.width, y, true); - end - - command = Command.bitmap_linear_win(0, 0, pixels, CompressionCode::UNCOMPRESSED) - - connection.send(command) - sleep 0.0005 - - x_offset += 1 -end - - diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.sh b/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.sh deleted file mode 100755 index 25ed29e..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/example/example.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -LD_LIBRARY_PATH="../../../../../target/release:$LD_LIBRARY_PATH" ruby example.rb diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/lib/servicepoint_binding_uniffi.rb b/crates/servicepoint_binding_uniffi/libraries/ruby/lib/servicepoint_binding_uniffi.rb deleted file mode 100644 index d097798..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/lib/servicepoint_binding_uniffi.rb +++ /dev/null @@ -1,2003 +0,0 @@ -# This file was autogenerated by some hot garbage in the `uniffi` crate. -# Trust me, you don't want to mess with it! - -# Common helper code. -# -# Ideally this would live in a separate .rb file where it can be unittested etc -# in isolation, and perhaps even published as a re-useable package. -# -# However, it's important that the details of how this helper code works (e.g. the -# way that different builtin types are passed across the FFI) exactly match what's -# expected by the rust code on the other side of the interface. In practice right -# now that means coming from the exact some version of `uniffi` that was used to -# compile the rust component. The easiest way to ensure this is to bundle the Ruby -# helpers directly inline like we're doing here. - -require 'ffi' - - -module ServicepointBindingUniffi - def self.uniffi_in_range(i, type_name, min, max) - raise TypeError, "no implicit conversion of #{i} into Integer" unless i.respond_to?(:to_int) - i = i.to_int - raise RangeError, "#{type_name} requires #{min} <= value < #{max}" unless (min <= i && i < max) - i -end - -def self.uniffi_utf8(v) - raise TypeError, "no implicit conversion of #{v} into String" unless v.respond_to?(:to_str) - v = v.to_str.encode(Encoding::UTF_8) - raise Encoding::InvalidByteSequenceError, "not a valid UTF-8 encoded string" unless v.valid_encoding? - v -end - -def self.uniffi_bytes(v) - raise TypeError, "no implicit conversion of #{v} into String" unless v.respond_to?(:to_str) - v.to_str -end - - class RustBuffer < FFI::Struct - layout :capacity, :int32, - :len, :int32, - :data, :pointer - - def self.alloc(size) - return ServicepointBindingUniffi.rust_call(:ffi_servicepoint_binding_uniffi_rustbuffer_alloc, size) - end - - def self.reserve(rbuf, additional) - return ServicepointBindingUniffi.rust_call(:ffi_servicepoint_binding_uniffi_rustbuffer_reserve, rbuf, additional) - end - - def free - ServicepointBindingUniffi.rust_call(:ffi_servicepoint_binding_uniffi_rustbuffer_free, self) - end - - def capacity - self[:capacity] - end - - def len - self[:len] - end - - def len=(value) - self[:len] = value - end - - def data - self[:data] - end - - def to_s - "RustBuffer(capacity=#{capacity}, len=#{len}, data=#{data.read_bytes len})" - end - - # The allocated buffer will be automatically freed if an error occurs, ensuring that - # we don't accidentally leak it. - def self.allocWithBuilder - builder = RustBufferBuilder.new - - begin - yield builder - rescue => e - builder.discard - raise e - end - end - - # The RustBuffer will be freed once the context-manager exits, ensuring that we don't - # leak it even if an error occurs. - def consumeWithStream - stream = RustBufferStream.new self - - yield stream - - raise RuntimeError, 'junk data left in buffer after consuming' if stream.remaining != 0 - ensure - free - end# The primitive String type. - - def self.allocFromString(value) - RustBuffer.allocWithBuilder do |builder| - builder.write value.encode('utf-8') - return builder.finalize - end - end - - def consumeIntoString - consumeWithStream do |stream| - return stream.read(stream.remaining).force_encoding(Encoding::UTF_8) - end - end - - # The primitive Bytes type. - - def self.allocFromBytes(value) - RustBuffer.allocWithBuilder do |builder| - builder.write_Bytes(value) - return builder.finalize - end - end - - def consumeIntoBytes - consumeWithStream do |stream| - return stream.readBytes - end - end - - # The Record type Constants. - - def self.alloc_from_TypeConstants(v) - RustBuffer.allocWithBuilder do |builder| - builder.write_TypeConstants(v) - return builder.finalize - end - end - - def consumeIntoTypeConstants - consumeWithStream do |stream| - return stream.readTypeConstants - end - end - - - - # The Enum type CompressionCode. - - def self.alloc_from_TypeCompressionCode(v) - RustBuffer.allocWithBuilder do |builder| - builder.write_TypeCompressionCode(v) - return builder.finalize - end - end - - def consumeIntoTypeCompressionCode - consumeWithStream do |stream| - return stream.readTypeCompressionCode - end - end - - - - - -end - -module UniFFILib - class ForeignBytes < FFI::Struct - layout :len, :int32, - :data, :pointer - - def len - self[:len] - end - - def data - self[:data] - end - - def to_s - "ForeignBytes(len=#{len}, data=#{data.read_bytes(len)})" - end - end -end - -private_constant :UniFFILib - -# Helper for structured reading of values from a RustBuffer. -class RustBufferStream - - def initialize(rbuf) - @rbuf = rbuf - @offset = 0 - end - - def remaining - @rbuf.len - @offset - end - - def read(size) - raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len - - data = @rbuf.data.get_bytes @offset, size - - @offset += size - - data - end - - def readU8 - unpack_from 1, 'c' - end - - def readU64 - unpack_from 8, 'Q>' - end - - def readBool - v = unpack_from 1, 'c' - - return false if v == 0 - return true if v == 1 - - raise InternalError, 'Unexpected byte for Boolean type' - end - - def readString - size = unpack_from 4, 'l>' - - raise InternalError, 'Unexpected negative string length' if size.negative? - - read(size).force_encoding(Encoding::UTF_8) - end - - def readBytes - size = unpack_from 4, 'l>' - - raise InternalError, 'Unexpected negative byte string length' if size.negative? - - read(size).force_encoding(Encoding::BINARY) - end - - # The Object type BitVec. - - def readTypeBitVec - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return BitVec._uniffi_allocate(pointer) - end - - # The Object type Bitmap. - - def readTypeBitmap - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return Bitmap._uniffi_allocate(pointer) - end - - # The Object type BrightnessGrid. - - def readTypeBrightnessGrid - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return BrightnessGrid._uniffi_allocate(pointer) - end - - # The Object type CharGrid. - - def readTypeCharGrid - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return CharGrid._uniffi_allocate(pointer) - end - - # The Object type Command. - - def readTypeCommand - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return Command._uniffi_allocate(pointer) - end - - # The Object type Connection. - - def readTypeConnection - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return Connection._uniffi_allocate(pointer) - end - - # The Object type Cp437Grid. - - def readTypeCp437Grid - pointer = FFI::Pointer.new unpack_from 8, 'Q>' - return Cp437Grid._uniffi_allocate(pointer) - end - - # The Record type Constants. - - def readTypeConstants - Constants.new( - readU64, - readU64, - readU64, - readU64, - readU64, - readU64 - ) - end - - - - - - # The Error type CharGridError - - def readTypeCharGridError - variant = unpack_from 4, 'l>' - - if variant == 1 - return CharGridError::StringNotOneChar.new( - readString() - ) - end - if variant == 2 - return CharGridError::InvalidSeriesLength.new( - readU64(), - readU64() - ) - end - if variant == 3 - return CharGridError::OutOfBounds.new( - readU64(), - readU64() - ) - end - - raise InternalError, 'Unexpected variant tag for TypeCharGridError' - end - - - - - # The Enum type CompressionCode. - - def readTypeCompressionCode - variant = unpack_from 4, 'l>' - - if variant == 1 - return CompressionCode::UNCOMPRESSED - end - if variant == 2 - return CompressionCode::ZLIB - end - if variant == 3 - return CompressionCode::BZIP2 - end - if variant == 4 - return CompressionCode::LZMA - end - if variant == 5 - return CompressionCode::ZSTD - end - - raise InternalError, 'Unexpected variant tag for TypeCompressionCode' - end - - - - - - - - # The Error type ServicePointError - - def readTypeServicePointError - variant = unpack_from 4, 'l>' - - if variant == 1 - return ServicePointError::IoError.new( - readString() - ) - end - if variant == 2 - return ServicePointError::InvalidBrightness.new( - readU8() - ) - end - - raise InternalError, 'Unexpected variant tag for TypeServicePointError' - end - - - - - def unpack_from(size, format) - raise InternalError, 'read past end of rust buffer' if @offset + size > @rbuf.len - - value = @rbuf.data.get_bytes(@offset, size).unpack format - - @offset += size - - # TODO: verify this - raise 'more than one element!!!' if value.size > 1 - - value[0] - end -end - -private_constant :RustBufferStream - -# Helper for structured writing of values into a RustBuffer. -class RustBufferBuilder - def initialize - @rust_buf = RustBuffer.alloc 16 - @rust_buf.len = 0 - end - - def finalize - rbuf = @rust_buf - - @rust_buf = nil - - rbuf - end - - def discard - return if @rust_buf.nil? - - rbuf = finalize - rbuf.free - end - - def write(value) - reserve(value.bytes.size) do - @rust_buf.data.put_array_of_char @rust_buf.len, value.bytes - end - end - - def write_U8(v) - v = ServicepointBindingUniffi::uniffi_in_range(v, "u8", 0, 2**8) - pack_into(1, 'c', v) - end - - def write_U64(v) - v = ServicepointBindingUniffi::uniffi_in_range(v, "u64", 0, 2**64) - pack_into(8, 'Q>', v) - end - - def write_Bool(v) - pack_into(1, 'c', v ? 1 : 0) - end - - def write_String(v) - v = ServicepointBindingUniffi::uniffi_utf8(v) - pack_into 4, 'l>', v.bytes.size - write v - end - - def write_Bytes(v) - v = ServicepointBindingUniffi::uniffi_bytes(v) - pack_into 4, 'l>', v.bytes.size - write v - end - - # The Object type BitVec. - - def write_TypeBitVec(obj) - pointer = BitVec._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type Bitmap. - - def write_TypeBitmap(obj) - pointer = Bitmap._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type BrightnessGrid. - - def write_TypeBrightnessGrid(obj) - pointer = BrightnessGrid._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type CharGrid. - - def write_TypeCharGrid(obj) - pointer = CharGrid._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type Command. - - def write_TypeCommand(obj) - pointer = Command._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type Connection. - - def write_TypeConnection(obj) - pointer = Connection._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Object type Cp437Grid. - - def write_TypeCp437Grid(obj) - pointer = Cp437Grid._uniffi_lower obj - pack_into(8, 'Q>', pointer.address) - end - - # The Record type Constants. - - def write_TypeConstants(v) - self.write_U64(v.tile_size) - self.write_U64(v.tile_width) - self.write_U64(v.tile_height) - self.write_U64(v.pixel_width) - self.write_U64(v.pixel_height) - self.write_U64(v.pixel_count) - end - - - - # The Enum type CompressionCode. - - def write_TypeCompressionCode(v) - pack_into(4, 'l>', v) - end - - - - - - - private - - def reserve(num_bytes) - if @rust_buf.len + num_bytes > @rust_buf.capacity - @rust_buf = RustBuffer.reserve(@rust_buf, num_bytes) - end - - yield - - @rust_buf.len += num_bytes - end - - def pack_into(size, format, value) - reserve(size) do - @rust_buf.data.put_array_of_char @rust_buf.len, [value].pack(format).bytes - end - end -end - -private_constant :RustBufferBuilder - - # Error definitions - class RustCallStatus < FFI::Struct - layout :code, :int8, - :error_buf, RustBuffer - - def code - self[:code] - end - - def error_buf - self[:error_buf] - end - - def to_s - "RustCallStatus(code=#{self[:code]})" - end -end - -# These match the values from the uniffi::rustcalls module -CALL_SUCCESS = 0 -CALL_ERROR = 1 -CALL_PANIC = 2 - - -module CharGridError - class StringNotOneChar < StandardError - def initialize(value) - @value = value - super() - end - - attr_reader :value - - - def to_s - "#{self.class.name}(value=#{@value.inspect})" - end - end - class InvalidSeriesLength < StandardError - def initialize(actual, expected) - @actual = actual - @expected = expected - super() - end - - attr_reader :actual, :expected - - - def to_s - "#{self.class.name}(actual=#{@actual.inspect}, expected=#{@expected.inspect})" - end - end - class OutOfBounds < StandardError - def initialize(index, size) - @index = index - @size = size - super() - end - - attr_reader :index, :size - - - def to_s - "#{self.class.name}(index=#{@index.inspect}, size=#{@size.inspect})" - end - end - -end - - - - -module ServicePointError - class IoError < StandardError - def initialize(error) - @error = error - super() - end - - attr_reader :error - - - def to_s - "#{self.class.name}(error=#{@error.inspect})" - end - end - class InvalidBrightness < StandardError - def initialize(value) - @value = value - super() - end - - attr_reader :value - - - def to_s - "#{self.class.name}(value=#{@value.inspect})" - end - end - -end - - -# Map error modules to the RustBuffer method name that reads them -ERROR_MODULE_TO_READER_METHOD = { - - CharGridError => :readTypeCharGridError, - - - - ServicePointError => :readTypeServicePointError, - -} - -private_constant :ERROR_MODULE_TO_READER_METHOD, :CALL_SUCCESS, :CALL_ERROR, :CALL_PANIC, - :RustCallStatus - -def self.consume_buffer_into_error(error_module, rust_buffer) - rust_buffer.consumeWithStream do |stream| - reader_method = ERROR_MODULE_TO_READER_METHOD[error_module] - return stream.send(reader_method) - end -end - -class InternalError < StandardError -end - -def self.rust_call(fn_name, *args) - # Call a rust function - rust_call_with_error(nil, fn_name, *args) -end - -def self.rust_call_with_error(error_module, fn_name, *args) - # Call a rust function and handle errors - # - # Use this when the rust function returns a Result<>. error_module must be the error_module that corresponds to that Result. - - - # Note: RustCallStatus.new zeroes out the struct, which is exactly what we - # want to pass to Rust (code=0, error_buf=RustBuffer(len=0, capacity=0, - # data=NULL)) - status = RustCallStatus.new - args << status - - result = UniFFILib.public_send(fn_name, *args) - - case status.code - when CALL_SUCCESS - result - when CALL_ERROR - if error_module.nil? - status.error_buf.free - raise InternalError, "CALL_ERROR with no error_module set" - else - raise consume_buffer_into_error(error_module, status.error_buf) - end - when CALL_PANIC - # When the rust code sees a panic, it tries to construct a RustBuffer - # with the message. But if that code panics, then it just sends back - # an empty buffer. - if status.error_buf.len > 0 - raise InternalError, status.error_buf.consumeIntoString() - else - raise InternalError, "Rust panic" - end - else - raise InternalError, "Unknown call status: #{status.code}" - end -end - -private_class_method :consume_buffer_into_error - - # This is how we find and load the dynamic library provided by the component. -# For now we just look it up by name. -module UniFFILib - extend FFI::Library - - - ffi_lib 'servicepoint_binding_uniffi' - - - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_bitvec, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_load, - [RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_new, - [:uint64, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_copy_raw, - [:pointer, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_fill, - [:pointer, :int8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_get, - [:pointer, :uint64, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_len, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitvec_set, - [:pointer, :uint64, :int8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_bitmap, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_load, - [:uint64, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_new, - [:uint64, :uint64, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_new_max_sized, - [RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_copy_raw, - [:pointer, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_fill, - [:pointer, :int8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_get, - [:pointer, :uint64, :uint64, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_height, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_set, - [:pointer, :uint64, :uint64, :int8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_bitmap_width, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_brightnessgrid, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_load, - [:uint64, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_new, - [:uint64, :uint64, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_copy_raw, - [:pointer, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_fill, - [:pointer, :uint8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_get, - [:pointer, :uint64, :uint64, RustCallStatus.by_ref], - :uint8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_height, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_set, - [:pointer, :uint64, :uint64, :uint8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_width, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_chargrid, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_load, - [RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_new, - [:uint64, :uint64, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_as_string, - [:pointer, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_fill, - [:pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get, - [:pointer, :uint64, :uint64, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get_col, - [:pointer, :uint64, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get_row, - [:pointer, :uint64, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_height, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set, - [:pointer, :uint64, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set_col, - [:pointer, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set_row, - [:pointer, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_to_cp437, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_chargrid_width, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_command, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear, - [:uint64, :pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_and, - [:uint64, :pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_or, - [:uint64, :pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_win, - [:uint64, :uint64, :pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_xor, - [:uint64, :pointer, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_brightness, - [:uint8, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_char_brightness, - [:uint64, :uint64, :pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_clear, - [RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_cp437_data, - [:uint64, :uint64, :pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_fade_out, - [RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset, - [RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data, - [:uint64, :uint64, :pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_command_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_connection, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new, - [RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new_fake, - [RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_connection_send, - [:pointer, :pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_free_cp437grid, - [:pointer, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_clone, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_load, - [:uint64, :uint64, RustBuffer.by_value, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_new, - [:uint64, :uint64, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_copy_raw, - [:pointer, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_equals, - [:pointer, :pointer, RustCallStatus.by_ref], - :int8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_fill, - [:pointer, :uint8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_get, - [:pointer, :uint64, :uint64, RustCallStatus.by_ref], - :uint8 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_height, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_set, - [:pointer, :uint64, :uint64, :uint8, RustCallStatus.by_ref], - :void - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_to_utf8, - [:pointer, RustCallStatus.by_ref], - :pointer - attach_function :uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_width, - [:pointer, RustCallStatus.by_ref], - :uint64 - attach_function :uniffi_servicepoint_binding_uniffi_fn_func_get_constants, - [RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :ffi_servicepoint_binding_uniffi_rustbuffer_alloc, - [:int32, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :ffi_servicepoint_binding_uniffi_rustbuffer_from_bytes, - [ForeignBytes, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :ffi_servicepoint_binding_uniffi_rustbuffer_free, - [RustBuffer.by_value, RustCallStatus.by_ref], - :void - attach_function :ffi_servicepoint_binding_uniffi_rustbuffer_reserve, - [RustBuffer.by_value, :int32, RustCallStatus.by_ref], - RustBuffer.by_value - attach_function :uniffi_servicepoint_binding_uniffi_checksum_func_get_constants, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_copy_raw, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_fill, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_get, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_len, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitvec_set, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_copy_raw, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_fill, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_get, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_height, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_set, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_bitmap_width, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_copy_raw, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_fill, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_get, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_height, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_set, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_brightnessgrid_width, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_as_string, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_fill, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_get, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_get_col, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_get_row, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_height, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_set, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_set_col, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_set_row, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_to_cp437, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_chargrid_width, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_command_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_connection_send, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_copy_raw, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_equals, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_fill, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_get, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_height, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_set, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_to_utf8, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_method_cp437grid_width, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitvec_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitvec_load, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitvec_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitmap_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitmap_load, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitmap_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_bitmap_new_max_sized, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_brightnessgrid_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_brightnessgrid_load, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_brightnessgrid_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_chargrid_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_chargrid_load, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_chargrid_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_bitmap_linear, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_bitmap_linear_and, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_bitmap_linear_or, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_bitmap_linear_win, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_bitmap_linear_xor, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_brightness, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_char_brightness, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_clear, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_cp437_data, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_fade_out, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new_fake, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_cp437grid_clone, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_cp437grid_load, - [RustCallStatus.by_ref], - :uint16 - attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_cp437grid_new, - [RustCallStatus.by_ref], - :uint16 - attach_function :ffi_servicepoint_binding_uniffi_uniffi_contract_version, - [RustCallStatus.by_ref], - :uint32 - -end - - # Public interface members begin here. - - - - - - -class CompressionCode - UNCOMPRESSED = 1 - ZLIB = 2 - BZIP2 = 3 - LZMA = 4 - ZSTD = 5 - -end - - - - - # Record type Constants -class Constants - attr_reader :tile_size, :tile_width, :tile_height, :pixel_width, :pixel_height, :pixel_count - - def initialize(tile_size, tile_width, tile_height, pixel_width, pixel_height, pixel_count) - @tile_size = tile_size - @tile_width = tile_width - @tile_height = tile_height - @pixel_width = pixel_width - @pixel_height = pixel_height - @pixel_count = pixel_count - end - - def ==(other) - if @tile_size != other.tile_size - return false - end - if @tile_width != other.tile_width - return false - end - if @tile_height != other.tile_height - return false - end - if @pixel_width != other.pixel_width - return false - end - if @pixel_height != other.pixel_height - return false - end - if @pixel_count != other.pixel_count - return false - end - - true - end -end - - - - - -def self.get_constants() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_func_get_constants,) - return result.consumeIntoTypeConstants -end - - - - - - class BitVec - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_bitvec, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a BitVec instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(size) - size = ServicepointBindingUniffi::uniffi_in_range(size, "u64", 0, 2**64) - pointer = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_new,size) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_clone,(BitVec._uniffi_lower other))) - end - def self.load(data) - data = ServicepointBindingUniffi::uniffi_bytes(data) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitvec_load,RustBuffer.allocFromBytes(data))) - end - - - def copy_raw() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_copy_raw,@pointer,) - return result.consumeIntoBytes - end - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_equals,@pointer,(BitVec._uniffi_lower other)) - return 1 == result - end - def fill(value) - value = value ? true : false - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_fill,@pointer,(value ? 1 : 0)) - end - - def get(index) - index = ServicepointBindingUniffi::uniffi_in_range(index, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_get,@pointer,index) - return 1 == result - end - def len() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_len,@pointer,) - return result.to_i - end - def set(index, value) - index = ServicepointBindingUniffi::uniffi_in_range(index, "u64", 0, 2**64) - value = value ? true : false - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitvec_set,@pointer,index,(value ? 1 : 0)) - end - - -end - - class Bitmap - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_bitmap, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a Bitmap instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(width, height) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - pointer = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_new,width,height) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_clone,(Bitmap._uniffi_lower other))) - end - def self.load(width, height, data) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - data = ServicepointBindingUniffi::uniffi_bytes(data) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_load,width,height,RustBuffer.allocFromBytes(data))) - end - def self.new_max_sized() - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_bitmap_new_max_sized,)) - end - - - def copy_raw() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_copy_raw,@pointer,) - return result.consumeIntoBytes - end - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_equals,@pointer,(Bitmap._uniffi_lower other)) - return 1 == result - end - def fill(value) - value = value ? true : false - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_fill,@pointer,(value ? 1 : 0)) - end - - def get(x, y) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_get,@pointer,x,y) - return 1 == result - end - def height() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_height,@pointer,) - return result.to_i - end - def set(x, y, value) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - value = value ? true : false - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_set,@pointer,x,y,(value ? 1 : 0)) - end - - def width() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_bitmap_width,@pointer,) - return result.to_i - end - -end - - class BrightnessGrid - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_brightnessgrid, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a BrightnessGrid instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(width, height) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - pointer = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_new,width,height) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_clone,(BrightnessGrid._uniffi_lower other))) - end - def self.load(width, height, data) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - data = ServicepointBindingUniffi::uniffi_bytes(data) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_brightnessgrid_load,width,height,RustBuffer.allocFromBytes(data))) - end - - - def copy_raw() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_copy_raw,@pointer,) - return result.consumeIntoBytes - end - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_equals,@pointer,(BrightnessGrid._uniffi_lower other)) - return 1 == result - end - def fill(value) - value = ServicepointBindingUniffi::uniffi_in_range(value, "u8", 0, 2**8) - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_fill,@pointer,value) - end - - def get(x, y) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_get,@pointer,x,y) - return result.to_i - end - def height() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_height,@pointer,) - return result.to_i - end - def set(x, y, value) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - value = ServicepointBindingUniffi::uniffi_in_range(value, "u8", 0, 2**8) - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_set,@pointer,x,y,value) - end - - def width() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_brightnessgrid_width,@pointer,) - return result.to_i - end - -end - - class CharGrid - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_chargrid, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a CharGrid instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(width, height) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - pointer = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_new,width,height) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_clone,(CharGrid._uniffi_lower other))) - end - def self.load(data) - data = ServicepointBindingUniffi::uniffi_utf8(data) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_chargrid_load,RustBuffer.allocFromString(data))) - end - - - def as_string() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_as_string,@pointer,) - return result.consumeIntoString - end - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_equals,@pointer,(CharGrid._uniffi_lower other)) - return 1 == result - end - def fill(value) - value = ServicepointBindingUniffi::uniffi_utf8(value) - ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_fill,@pointer,RustBuffer.allocFromString(value)) - end - - def get(x, y) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get,@pointer,x,y) - return result.consumeIntoString - end - def get_col(x) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get_col,@pointer,x) - return result.consumeIntoString - end - def get_row(y) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_get_row,@pointer,y) - return result.consumeIntoString - end - def height() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_height,@pointer,) - return result.to_i - end - def set(x, y, value) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - value = ServicepointBindingUniffi::uniffi_utf8(value) - ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set,@pointer,x,y,RustBuffer.allocFromString(value)) - end - - def set_col(x, col) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - col = ServicepointBindingUniffi::uniffi_utf8(col) - ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set_col,@pointer,x,RustBuffer.allocFromString(col)) - end - - def set_row(y, row) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - row = ServicepointBindingUniffi::uniffi_utf8(row) - ServicepointBindingUniffi.rust_call_with_error(CharGridError,:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_set_row,@pointer,y,RustBuffer.allocFromString(row)) - end - - def to_cp437() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_to_cp437,@pointer,) - return Cp437Grid._uniffi_allocate(result) - end - def width() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_chargrid_width,@pointer,) - return result.to_i - end - -end - - class Command - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_command, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a Command instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - - def self.bitmap_linear(offset, bitmap, compression) - offset = ServicepointBindingUniffi::uniffi_in_range(offset, "u64", 0, 2**64) - bitmap = bitmap - compression = compression - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear,offset,(BitVec._uniffi_lower bitmap),RustBuffer.alloc_from_TypeCompressionCode(compression))) - end - def self.bitmap_linear_and(offset, bitmap, compression) - offset = ServicepointBindingUniffi::uniffi_in_range(offset, "u64", 0, 2**64) - bitmap = bitmap - compression = compression - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_and,offset,(BitVec._uniffi_lower bitmap),RustBuffer.alloc_from_TypeCompressionCode(compression))) - end - def self.bitmap_linear_or(offset, bitmap, compression) - offset = ServicepointBindingUniffi::uniffi_in_range(offset, "u64", 0, 2**64) - bitmap = bitmap - compression = compression - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_or,offset,(BitVec._uniffi_lower bitmap),RustBuffer.alloc_from_TypeCompressionCode(compression))) - end - def self.bitmap_linear_win(offset_x, offset_y, bitmap, compression) - offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64) - offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64) - bitmap = bitmap - compression = compression - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_win,offset_x,offset_y,(Bitmap._uniffi_lower bitmap),RustBuffer.alloc_from_TypeCompressionCode(compression))) - end - def self.bitmap_linear_xor(offset, bitmap, compression) - offset = ServicepointBindingUniffi::uniffi_in_range(offset, "u64", 0, 2**64) - bitmap = bitmap - compression = compression - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_bitmap_linear_xor,offset,(BitVec._uniffi_lower bitmap),RustBuffer.alloc_from_TypeCompressionCode(compression))) - end - def self.brightness(brightness) - brightness = ServicepointBindingUniffi::uniffi_in_range(brightness, "u8", 0, 2**8) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call_with_error(ServicePointError,:uniffi_servicepoint_binding_uniffi_fn_constructor_command_brightness,brightness)) - end - def self.char_brightness(offset_x, offset_y, grid) - offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64) - offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64) - grid = grid - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_char_brightness,offset_x,offset_y,(BrightnessGrid._uniffi_lower grid))) - end - def self.clear() - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_clear,)) - end - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_clone,(Command._uniffi_lower other))) - end - def self.cp437_data(offset_x, offset_y, grid) - offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64) - offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64) - grid = grid - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_cp437_data,offset_x,offset_y,(Cp437Grid._uniffi_lower grid))) - end - def self.fade_out() - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_fade_out,)) - end - def self.hard_reset() - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,)) - end - def self.utf8_data(offset_x, offset_y, grid) - offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64) - offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64) - grid = grid - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data,offset_x,offset_y,(CharGrid._uniffi_lower grid))) - end - - - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_command_equals,@pointer,(Command._uniffi_lower other)) - return 1 == result - end - -end - - class Connection - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_connection, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a Connection instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(host) - host = ServicepointBindingUniffi::uniffi_utf8(host) - pointer = ServicepointBindingUniffi.rust_call_with_error(ServicePointError,:uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new,RustBuffer.allocFromString(host)) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.new_fake() - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_connection_new_fake,)) - end - - - def send(command) - command = command - ServicepointBindingUniffi.rust_call_with_error(ServicePointError,:uniffi_servicepoint_binding_uniffi_fn_method_connection_send,@pointer,(Command._uniffi_lower command)) - end - - -end - - class Cp437Grid - - # A private helper for initializing instances of the class from a raw pointer, - # bypassing any initialization logic and ensuring they are GC'd properly. - def self._uniffi_allocate(pointer) - pointer.autorelease = false - inst = allocate - inst.instance_variable_set :@pointer, pointer - ObjectSpace.define_finalizer(inst, _uniffi_define_finalizer_by_pointer(pointer, inst.object_id)) - return inst - end - - # A private helper for registering an object finalizer. - # N.B. it's important that this does not capture a reference - # to the actual instance, only its underlying pointer. - def self._uniffi_define_finalizer_by_pointer(pointer, object_id) - Proc.new do |_id| - ServicepointBindingUniffi.rust_call( - :uniffi_servicepoint_binding_uniffi_fn_free_cp437grid, - pointer - ) - end - end - - # A private helper for lowering instances into a raw pointer. - # This does an explicit typecheck, because accidentally lowering a different type of - # object in a place where this type is expected, could lead to memory unsafety. - def self._uniffi_lower(inst) - if not inst.is_a? self - raise TypeError.new "Expected a Cp437Grid instance, got #{inst}" - end - return inst.instance_variable_get :@pointer - end - def initialize(width, height) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - pointer = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_new,width,height) - @pointer = pointer - ObjectSpace.define_finalizer(self, self.class._uniffi_define_finalizer_by_pointer(pointer, self.object_id)) - end - - def self.clone(other) - other = other - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_clone,(Cp437Grid._uniffi_lower other))) - end - def self.load(width, height, data) - width = ServicepointBindingUniffi::uniffi_in_range(width, "u64", 0, 2**64) - height = ServicepointBindingUniffi::uniffi_in_range(height, "u64", 0, 2**64) - data = ServicepointBindingUniffi::uniffi_bytes(data) - # Call the (fallible) function before creating any half-baked object instances. - # Lightly yucky way to bypass the usual "initialize" logic - # and just create a new instance with the required pointer. - return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_cp437grid_load,width,height,RustBuffer.allocFromBytes(data))) - end - - - def copy_raw() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_copy_raw,@pointer,) - return result.consumeIntoBytes - end - def equals(other) - other = other - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_equals,@pointer,(Cp437Grid._uniffi_lower other)) - return 1 == result - end - def fill(value) - value = ServicepointBindingUniffi::uniffi_in_range(value, "u8", 0, 2**8) - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_fill,@pointer,value) - end - - def get(x, y) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_get,@pointer,x,y) - return result.to_i - end - def height() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_height,@pointer,) - return result.to_i - end - def set(x, y, value) - x = ServicepointBindingUniffi::uniffi_in_range(x, "u64", 0, 2**64) - y = ServicepointBindingUniffi::uniffi_in_range(y, "u64", 0, 2**64) - value = ServicepointBindingUniffi::uniffi_in_range(value, "u8", 0, 2**8) - ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_set,@pointer,x,y,value) - end - - def to_utf8() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_to_utf8,@pointer,) - return CharGrid._uniffi_allocate(result) - end - def width() - result = ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_method_cp437grid_width,@pointer,) - return result.to_i - end - -end - -end - diff --git a/crates/servicepoint_binding_uniffi/libraries/ruby/servicepoint.gemspec b/crates/servicepoint_binding_uniffi/libraries/ruby/servicepoint.gemspec deleted file mode 100644 index f558ad5..0000000 --- a/crates/servicepoint_binding_uniffi/libraries/ruby/servicepoint.gemspec +++ /dev/null @@ -1,13 +0,0 @@ -Gem::Specification.new do |s| - s.name = "servicepoint" - s.version = "0.13.1" - s.summary = "" - s.description = "" - s.authors = ["kaesaecracker"] - s.email = "" - s.files = ["lib/servicepoint_binding_uniffi.rb"] - s.homepage = - "https://rubygems.org/gems/hola" - s.license = "MIT" - s.add_dependency 'ffi' -end diff --git a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-go.rs b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-go.rs deleted file mode 100644 index 5f01856..0000000 --- a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-go.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi_bindgen_go::main().unwrap(); -} diff --git a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs b/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs deleted file mode 100644 index f6cff6c..0000000 --- a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi::uniffi_bindgen_main() -} diff --git a/crates/servicepoint_binding_uniffi/src/bitmap.rs b/crates/servicepoint_binding_uniffi/src/bitmap.rs deleted file mode 100644 index 1291f7b..0000000 --- a/crates/servicepoint_binding_uniffi/src/bitmap.rs +++ /dev/null @@ -1,77 +0,0 @@ -use servicepoint::{DataRef, Grid}; -use std::sync::{Arc, RwLock}; - -#[derive(uniffi::Object)] -pub struct Bitmap { - pub(crate) actual: RwLock, -} - -impl Bitmap { - fn internal_new(actual: servicepoint::Bitmap) -> Arc { - Arc::new(Self { - actual: RwLock::new(actual), - }) - } -} - -#[uniffi::export] -impl Bitmap { - #[uniffi::constructor] - pub fn new(width: u64, height: u64) -> Arc { - Self::internal_new(servicepoint::Bitmap::new( - width as usize, - height as usize, - )) - } - - #[uniffi::constructor] - pub fn new_max_sized() -> Arc { - Self::internal_new(servicepoint::Bitmap::max_sized()) - } - - #[uniffi::constructor] - pub fn load(width: u64, height: u64, data: Vec) -> Arc { - Self::internal_new(servicepoint::Bitmap::load( - width as usize, - height as usize, - &data, - )) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.read().unwrap().clone()) - } - - pub fn set(&self, x: u64, y: u64, value: bool) { - self.actual - .write() - .unwrap() - .set(x as usize, y as usize, value) - } - - pub fn get(&self, x: u64, y: u64) -> bool { - self.actual.read().unwrap().get(x as usize, y as usize) - } - - pub fn fill(&self, value: bool) { - self.actual.write().unwrap().fill(value) - } - pub fn width(&self) -> u64 { - self.actual.read().unwrap().width() as u64 - } - - pub fn height(&self) -> u64 { - self.actual.read().unwrap().height() as u64 - } - - pub fn equals(&self, other: &Bitmap) -> bool { - let a = self.actual.read().unwrap(); - let b = other.actual.read().unwrap(); - *a == *b - } - - pub fn copy_raw(&self) -> Vec { - self.actual.read().unwrap().data_ref().to_vec() - } -} diff --git a/crates/servicepoint_binding_uniffi/src/bitvec.rs b/crates/servicepoint_binding_uniffi/src/bitvec.rs deleted file mode 100644 index 1ad7751..0000000 --- a/crates/servicepoint_binding_uniffi/src/bitvec.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::sync::{Arc, RwLock}; - -#[derive(uniffi::Object)] -pub struct BitVec { - pub(crate) actual: RwLock, -} - -impl BitVec { - fn internal_new(actual: servicepoint::BitVec) -> Arc { - Arc::new(Self { - actual: RwLock::new(actual), - }) - } -} - -#[uniffi::export] -impl BitVec { - #[uniffi::constructor] - pub fn new(size: u64) -> Arc { - Self::internal_new(servicepoint::BitVec::repeat(false, size as usize)) - } - #[uniffi::constructor] - pub fn load(data: Vec) -> Arc { - Self::internal_new(servicepoint::BitVec::from_slice(&data)) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.read().unwrap().clone()) - } - - pub fn set(&self, index: u64, value: bool) { - self.actual.write().unwrap().set(index as usize, value) - } - - pub fn get(&self, index: u64) -> bool { - self.actual - .read() - .unwrap() - .get(index as usize) - .is_some_and(move |bit| *bit) - } - - pub fn fill(&self, value: bool) { - self.actual.write().unwrap().fill(value) - } - - pub fn len(&self) -> u64 { - self.actual.read().unwrap().len() as u64 - } - - pub fn equals(&self, other: &BitVec) -> bool { - let a = self.actual.read().unwrap(); - let b = other.actual.read().unwrap(); - *a == *b - } - - pub fn copy_raw(&self) -> Vec { - self.actual.read().unwrap().clone().into_vec() - } -} diff --git a/crates/servicepoint_binding_uniffi/src/brightness_grid.rs b/crates/servicepoint_binding_uniffi/src/brightness_grid.rs deleted file mode 100644 index afa0e3f..0000000 --- a/crates/servicepoint_binding_uniffi/src/brightness_grid.rs +++ /dev/null @@ -1,86 +0,0 @@ -use servicepoint::{Brightness, DataRef, Grid}; -use std::sync::{Arc, RwLock}; - -#[derive(uniffi::Object)] -pub struct BrightnessGrid { - pub(crate) actual: RwLock, -} - -impl BrightnessGrid { - fn internal_new(actual: servicepoint::BrightnessGrid) -> Arc { - Arc::new(Self { - actual: RwLock::new(actual), - }) - } -} - -#[uniffi::export] -impl BrightnessGrid { - #[uniffi::constructor] - pub fn new(width: u64, height: u64) -> Arc { - Self::internal_new(servicepoint::BrightnessGrid::new( - width as usize, - height as usize, - )) - } - - #[uniffi::constructor] - pub fn load(width: u64, height: u64, data: Vec) -> Arc { - Self::internal_new(servicepoint::BrightnessGrid::saturating_load( - width as usize, - height as usize, - &data, - )) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.read().unwrap().clone()) - } - - pub fn set(&self, x: u64, y: u64, value: u8) { - self.actual.write().unwrap().set( - x as usize, - y as usize, - Brightness::saturating_from(value), - ) - } - - pub fn get(&self, x: u64, y: u64) -> u8 { - self.actual - .read() - .unwrap() - .get(x as usize, y as usize) - .into() - } - - pub fn fill(&self, value: u8) { - self.actual - .write() - .unwrap() - .fill(Brightness::saturating_from(value)) - } - pub fn width(&self) -> u64 { - self.actual.read().unwrap().width() as u64 - } - - pub fn height(&self) -> u64 { - self.actual.read().unwrap().height() as u64 - } - - pub fn equals(&self, other: &BrightnessGrid) -> bool { - let a = self.actual.read().unwrap(); - let b = other.actual.read().unwrap(); - *a == *b - } - - pub fn copy_raw(&self) -> Vec { - self.actual - .read() - .unwrap() - .data_ref() - .iter() - .map(u8::from) - .collect() - } -} diff --git a/crates/servicepoint_binding_uniffi/src/char_grid.rs b/crates/servicepoint_binding_uniffi/src/char_grid.rs deleted file mode 100644 index e5d59a8..0000000 --- a/crates/servicepoint_binding_uniffi/src/char_grid.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::cp437_grid::Cp437Grid; -use servicepoint::{Grid, SetValueSeriesError}; -use std::convert::Into; -use std::sync::{Arc, RwLock}; - -#[derive(uniffi::Object)] -pub struct CharGrid { - pub(crate) actual: RwLock, -} - -#[derive(uniffi::Error, thiserror::Error, Debug)] -pub enum CharGridError { - #[error("Exactly one character was expected, but {value:?} was provided")] - StringNotOneChar { value: String }, - #[error("The provided series was expected to have a length of {expected}, but was {actual}")] - InvalidSeriesLength { actual: u64, expected: u64 }, - #[error("The index {index} was out of bounds for size {size}")] - OutOfBounds { index: u64, size: u64 }, -} - -#[uniffi::export] -impl CharGrid { - #[uniffi::constructor] - pub fn new(width: u64, height: u64) -> Arc { - Self::internal_new(servicepoint::CharGrid::new( - width as usize, - height as usize, - )) - } - - #[uniffi::constructor] - pub fn load(data: String) -> Arc { - Self::internal_new(servicepoint::CharGrid::from(&*data)) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.read().unwrap().clone()) - } - - pub fn set( - &self, - x: u64, - y: u64, - value: String, - ) -> Result<(), CharGridError> { - let value = Self::str_to_char(value)?; - self.actual - .write() - .unwrap() - .set(x as usize, y as usize, value); - Ok(()) - } - - pub fn get(&self, x: u64, y: u64) -> String { - self.actual - .read() - .unwrap() - .get(x as usize, y as usize) - .into() - } - - pub fn fill(&self, value: String) -> Result<(), CharGridError> { - let value = Self::str_to_char(value)?; - self.actual.write().unwrap().fill(value); - Ok(()) - } - - pub fn width(&self) -> u64 { - self.actual.read().unwrap().width() as u64 - } - - pub fn height(&self) -> u64 { - self.actual.read().unwrap().height() as u64 - } - - pub fn equals(&self, other: &CharGrid) -> bool { - let a = self.actual.read().unwrap(); - let b = other.actual.read().unwrap(); - *a == *b - } - - pub fn as_string(&self) -> String { - let grid = self.actual.read().unwrap(); - String::from(&*grid) - } - - pub fn set_row(&self, y: u64, row: String) -> Result<(), CharGridError> { - self.actual - .write() - .unwrap() - .set_row(y as usize, &row.chars().collect::>()) - .map_err(CharGridError::from) - } - - pub fn set_col(&self, x: u64, col: String) -> Result<(), CharGridError> { - self.actual - .write() - .unwrap() - .set_row(x as usize, &col.chars().collect::>()) - .map_err(CharGridError::from) - } - - pub fn get_row(&self, y: u64) -> Result { - self.actual - .read() - .unwrap() - .get_row(y as usize) - .map(String::from_iter) - .ok_or(CharGridError::OutOfBounds { - index: y, - size: self.height(), - }) - } - - pub fn get_col(&self, x: u64) -> Result { - self.actual - .read() - .unwrap() - .get_col(x as usize) - .map(String::from_iter) - .ok_or(CharGridError::OutOfBounds { - index: x, - size: self.width(), - }) - } - - pub fn to_cp437(&self) -> Arc { - Cp437Grid::internal_new(servicepoint::Cp437Grid::from( - &*self.actual.read().unwrap(), - )) - } -} - -impl CharGrid { - pub(crate) fn internal_new(actual: servicepoint::CharGrid) -> Arc { - Arc::new(Self { - actual: RwLock::new(actual), - }) - } - - fn str_to_char(value: String) -> Result { - if value.len() != 1 { - return Err(CharGridError::StringNotOneChar { value }); - } - - let value = value.chars().nth(0).unwrap(); - Ok(value) - } -} - -impl From for CharGridError { - fn from(e: SetValueSeriesError) -> Self { - match e { - SetValueSeriesError::OutOfBounds { index, size } => { - CharGridError::OutOfBounds { - index: index as u64, - size: size as u64, - } - } - SetValueSeriesError::InvalidLength { actual, expected } => { - CharGridError::InvalidSeriesLength { - actual: actual as u64, - expected: expected as u64, - } - } - } - } -} diff --git a/crates/servicepoint_binding_uniffi/src/command.rs b/crates/servicepoint_binding_uniffi/src/command.rs deleted file mode 100644 index bb479ae..0000000 --- a/crates/servicepoint_binding_uniffi/src/command.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::bitmap::Bitmap; -use crate::bitvec::BitVec; -use crate::brightness_grid::BrightnessGrid; -use crate::char_grid::CharGrid; -use crate::compression_code::CompressionCode; -use crate::cp437_grid::Cp437Grid; -use crate::errors::ServicePointError; -use servicepoint::Origin; -use std::sync::Arc; - -#[derive(uniffi::Object)] -pub struct Command { - pub(crate) actual: servicepoint::Command, -} - -impl Command { - fn internal_new(actual: servicepoint::Command) -> Arc { - Arc::new(Command { actual }) - } -} - -#[uniffi::export] -impl Command { - #[uniffi::constructor] - pub fn clear() -> Arc { - Self::internal_new(servicepoint::Command::Clear) - } - - #[uniffi::constructor] - pub fn brightness(brightness: u8) -> Result, ServicePointError> { - servicepoint::Brightness::try_from(brightness) - .map_err(move |value| ServicePointError::InvalidBrightness { - value, - }) - .map(servicepoint::Command::Brightness) - .map(Self::internal_new) - } - - #[uniffi::constructor] - pub fn fade_out() -> Arc { - Self::internal_new(servicepoint::Command::FadeOut) - } - - #[uniffi::constructor] - pub fn hard_reset() -> Arc { - Self::internal_new(servicepoint::Command::HardReset) - } - - #[uniffi::constructor] - pub fn bitmap_linear_win( - offset_x: u64, - offset_y: u64, - bitmap: &Arc, - compression: CompressionCode, - ) -> Arc { - let origin = Origin::new(offset_x as usize, offset_y as usize); - let bitmap = bitmap.actual.read().unwrap().clone(); - let actual = servicepoint::Command::BitmapLinearWin( - origin, - bitmap, - servicepoint::CompressionCode::try_from(compression as u16) - .unwrap(), - ); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn char_brightness( - offset_x: u64, - offset_y: u64, - grid: &Arc, - ) -> Arc { - let origin = Origin::new(offset_x as usize, offset_y as usize); - let grid = grid.actual.read().unwrap().clone(); - let actual = servicepoint::Command::CharBrightness(origin, grid); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn bitmap_linear( - offset: u64, - bitmap: &Arc, - compression: CompressionCode, - ) -> Arc { - let bitmap = bitmap.actual.read().unwrap().clone(); - let actual = servicepoint::Command::BitmapLinear( - offset as usize, - bitmap, - servicepoint::CompressionCode::try_from(compression as u16) - .unwrap(), - ); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn bitmap_linear_and( - offset: u64, - bitmap: &Arc, - compression: CompressionCode, - ) -> Arc { - let bitmap = bitmap.actual.read().unwrap().clone(); - let actual = servicepoint::Command::BitmapLinearAnd( - offset as usize, - bitmap, - servicepoint::CompressionCode::try_from(compression as u16) - .unwrap(), - ); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn bitmap_linear_or( - offset: u64, - bitmap: &Arc, - compression: CompressionCode, - ) -> Arc { - let bitmap = bitmap.actual.read().unwrap().clone(); - let actual = servicepoint::Command::BitmapLinearOr( - offset as usize, - bitmap, - servicepoint::CompressionCode::try_from(compression as u16) - .unwrap(), - ); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn bitmap_linear_xor( - offset: u64, - bitmap: &Arc, - compression: CompressionCode, - ) -> Arc { - let bitmap = bitmap.actual.read().unwrap().clone(); - let actual = servicepoint::Command::BitmapLinearXor( - offset as usize, - bitmap, - servicepoint::CompressionCode::try_from(compression as u16) - .unwrap(), - ); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn cp437_data( - offset_x: u64, - offset_y: u64, - grid: &Arc, - ) -> Arc { - let origin = Origin::new(offset_x as usize, offset_y as usize); - let grid = grid.actual.read().unwrap().clone(); - let actual = servicepoint::Command::Cp437Data(origin, grid); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn utf8_data( - offset_x: u64, - offset_y: u64, - grid: &Arc, - ) -> Arc { - let origin = Origin::new(offset_x as usize, offset_y as usize); - let grid = grid.actual.read().unwrap().clone(); - let actual = servicepoint::Command::Utf8Data(origin, grid); - Self::internal_new(actual) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.clone()) - } - - pub fn equals(&self, other: &Command) -> bool { - self.actual == other.actual - } -} diff --git a/crates/servicepoint_binding_uniffi/src/compression_code.rs b/crates/servicepoint_binding_uniffi/src/compression_code.rs deleted file mode 100644 index 078230a..0000000 --- a/crates/servicepoint_binding_uniffi/src/compression_code.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[repr(u16)] -#[derive(Debug, Clone, Copy, PartialEq, uniffi::Enum)] -pub enum CompressionCode { - /// no compression - Uncompressed = 0x0, - /// compress using flate2 with zlib header - Zlib = 0x677a, - /// compress using bzip2 - Bzip2 = 0x627a, - /// compress using lzma - Lzma = 0x6c7a, - /// compress using Zstandard - Zstd = 0x7a73, -} diff --git a/crates/servicepoint_binding_uniffi/src/connection.rs b/crates/servicepoint_binding_uniffi/src/connection.rs deleted file mode 100644 index 6ba4e07..0000000 --- a/crates/servicepoint_binding_uniffi/src/connection.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::sync::Arc; - -use crate::command::Command; -use crate::errors::ServicePointError; - -#[derive(uniffi::Object)] -pub struct Connection { - actual: servicepoint::Connection, -} - -#[uniffi::export] -impl Connection { - #[uniffi::constructor] - pub fn new(host: String) -> Result, ServicePointError> { - servicepoint::Connection::open(host) - .map(|actual| Arc::new(Connection { actual })) - .map_err(|err| ServicePointError::IoError { - error: err.to_string(), - }) - } - - #[uniffi::constructor] - pub fn new_fake() -> Arc { - Arc::new(Self { - actual: servicepoint::Connection::Fake, - }) - } - - pub fn send(&self, command: Arc) -> Result<(), ServicePointError> { - self.actual.send(command.actual.clone()).map_err(|err| { - ServicePointError::IoError { - error: format!("{err:?}"), - } - }) - } -} diff --git a/crates/servicepoint_binding_uniffi/src/constants.rs b/crates/servicepoint_binding_uniffi/src/constants.rs deleted file mode 100644 index 3d0cfcb..0000000 --- a/crates/servicepoint_binding_uniffi/src/constants.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record, -)] -pub struct Constants { - pub tile_size: u64, - pub tile_width: u64, - pub tile_height: u64, - pub pixel_width: u64, - pub pixel_height: u64, - pub pixel_count: u64, -} - -#[uniffi::export] -fn get_constants() -> Constants { - Constants { - tile_size: servicepoint::TILE_SIZE as u64, - tile_width: servicepoint::TILE_WIDTH as u64, - tile_height: servicepoint::TILE_HEIGHT as u64, - pixel_width: servicepoint::PIXEL_WIDTH as u64, - pixel_height: servicepoint::PIXEL_HEIGHT as u64, - pixel_count: servicepoint::PIXEL_COUNT as u64, - } -} diff --git a/crates/servicepoint_binding_uniffi/src/cp437_grid.rs b/crates/servicepoint_binding_uniffi/src/cp437_grid.rs deleted file mode 100644 index b201238..0000000 --- a/crates/servicepoint_binding_uniffi/src/cp437_grid.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::char_grid::CharGrid; -use servicepoint::{DataRef, Grid}; -use std::sync::{Arc, RwLock}; - -#[derive(uniffi::Object)] -pub struct Cp437Grid { - pub(crate) actual: RwLock, -} - -impl Cp437Grid { - pub(crate) fn internal_new(actual: servicepoint::Cp437Grid) -> Arc { - Arc::new(Self { - actual: RwLock::new(actual), - }) - } -} - -#[uniffi::export] -impl Cp437Grid { - #[uniffi::constructor] - pub fn new(width: u64, height: u64) -> Arc { - Self::internal_new(servicepoint::Cp437Grid::new( - width as usize, - height as usize, - )) - } - - #[uniffi::constructor] - pub fn load(width: u64, height: u64, data: Vec) -> Arc { - Self::internal_new(servicepoint::Cp437Grid::load( - width as usize, - height as usize, - &data, - )) - } - - #[uniffi::constructor] - pub fn clone(other: &Arc) -> Arc { - Self::internal_new(other.actual.read().unwrap().clone()) - } - - pub fn set(&self, x: u64, y: u64, value: u8) { - self.actual - .write() - .unwrap() - .set(x as usize, y as usize, value) - } - - pub fn get(&self, x: u64, y: u64) -> u8 { - self.actual.read().unwrap().get(x as usize, y as usize) - } - - pub fn fill(&self, value: u8) { - self.actual.write().unwrap().fill(value) - } - pub fn width(&self) -> u64 { - self.actual.read().unwrap().width() as u64 - } - - pub fn height(&self) -> u64 { - self.actual.read().unwrap().height() as u64 - } - - pub fn equals(&self, other: &Cp437Grid) -> bool { - let a = self.actual.read().unwrap(); - let b = other.actual.read().unwrap(); - *a == *b - } - - pub fn copy_raw(&self) -> Vec { - self.actual.read().unwrap().data_ref().to_vec() - } - - pub fn to_utf8(&self) -> Arc { - CharGrid::internal_new(servicepoint::CharGrid::from( - &*self.actual.read().unwrap(), - )) - } -} diff --git a/crates/servicepoint_binding_uniffi/src/errors.rs b/crates/servicepoint_binding_uniffi/src/errors.rs deleted file mode 100644 index 9c28f5f..0000000 --- a/crates/servicepoint_binding_uniffi/src/errors.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[derive(uniffi::Error, thiserror::Error, Debug)] -pub enum ServicePointError { - #[error("An IO error occurred: {error}")] - IoError { error: String }, - #[error("The specified brightness value {value} is out of range")] - InvalidBrightness { value: u8 }, -} diff --git a/crates/servicepoint_binding_uniffi/src/lib.rs b/crates/servicepoint_binding_uniffi/src/lib.rs deleted file mode 100644 index 2ecad45..0000000 --- a/crates/servicepoint_binding_uniffi/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -uniffi::setup_scaffolding!(); - -mod bitmap; -mod bitvec; -mod brightness_grid; -mod char_grid; -mod command; -mod compression_code; -mod connection; -mod constants; -mod cp437_grid; -mod errors; diff --git a/crates/servicepoint_binding_uniffi/libraries/csharp/csharp.sln b/csharp.sln similarity index 100% rename from crates/servicepoint_binding_uniffi/libraries/csharp/csharp.sln rename to csharp.sln diff --git a/flake.lock b/flake.lock index b07ad46..9e70e85 100644 --- a/flake.lock +++ b/flake.lock @@ -1,25 +1,5 @@ { "nodes": { - "naersk": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1736429655, - "narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=", - "owner": "nix-community", - "repo": "naersk", - "rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "naersk", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1739357830, @@ -38,7 +18,6 @@ }, "root": { "inputs": { - "naersk": "naersk", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index d903f36..2b8da61 100644 --- a/flake.nix +++ b/flake.nix @@ -3,17 +3,12 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; - naersk = { - url = "github:nix-community/naersk"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; outputs = inputs@{ self, nixpkgs, - naersk, }: let lib = nixpkgs.lib; @@ -33,98 +28,11 @@ } ); in - rec { - packages = forAllSystems ( - { pkgs, ... }: - let - naersk' = pkgs.callPackage naersk { }; - nativeBuildInputs = with pkgs; [ - pkg-config - makeWrapper - ]; - buildInputs = with pkgs; [ - xe - xz - ]; - makeExample = - { - package, - example, - features ? "", - }: - naersk'.buildPackage { - pname = example; - cargoBuildOptions = - x: - x - ++ [ - "--package" - package - ]; - src = ./.; - inherit nativeBuildInputs buildInputs; - strictDeps = true; - gitSubmodules = true; - overrideMain = old: { - preConfigure = '' - cargo_build_options="$cargo_build_options --example ${example} ${ - if features == "" then "" else "--features " + features - }" - ''; - }; - }; - makePackage = - package: - let - package-param = [ - "--package" - package - ]; - in - naersk'.buildPackage { - pname = package; - cargoBuildOptions = x: x ++ package-param; - cargoTestOptions = x: x ++ package-param; - src = ./.; - doCheck = true; - strictDeps = true; - inherit nativeBuildInputs buildInputs; - }; - in - rec { - servicepoint = makePackage "servicepoint"; - announce = makeExample { - package = "servicepoint"; - example = "announce"; - }; - game-of-life = makeExample { - package = "servicepoint"; - example = "game_of_life"; - features = "rand"; - }; - moving-line = makeExample { - package = "servicepoint"; - example = "moving_line"; - }; - random-brightness = makeExample { - package = "servicepoint"; - example = "random_brightness"; - features = "rand"; - }; - wiping-clear = makeExample { - package = "servicepoint"; - example = "wiping_clear"; - }; - } - ); - - legacyPackages = packages; - + { devShells = forAllSystems ( { pkgs, system }: { default = pkgs.mkShell rec { - inputsFrom = [ self.packages.${system}.servicepoint ]; packages = with pkgs; [ (pkgs.symlinkJoin { @@ -139,12 +47,13 @@ cargo-tarpaulin ]; }) - ruby dotnet-sdk_8 gcc gnumake + xe + xz + pkg-config ]; - LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; }; } diff --git a/generate-binding.sh b/generate-binding.sh new file mode 100755 index 0000000..fffdd7f --- /dev/null +++ b/generate-binding.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +cd "$SCRIPT_PATH" +cargo build --release + +TARGET_PATH="$(realpath "$SCRIPT_PATH"/target/release)" +SERVICEPOINT_SO="$TARGET_PATH/libservicepoint_binding_uniffi.so" + +echo "Source: $SERVICEPOINT_SO" +echo "Output: $LIBRARIES_PATH" + +"$TARGET_PATH/uniffi-bindgen-cs" --library "$SERVICEPOINT_SO" --out-dir "$SCRIPT_PATH/ServicePoint" diff --git a/servicepoint-binding-uniffi b/servicepoint-binding-uniffi new file mode 160000 index 0000000..c6d2ee6 --- /dev/null +++ b/servicepoint-binding-uniffi @@ -0,0 +1 @@ +Subproject commit c6d2ee6fe1edcbc630ce654e93cf94f510b19f7a diff --git a/uniffi-bindgen-cs/Cargo.toml b/uniffi-bindgen-cs/Cargo.toml new file mode 100644 index 0000000..93884b4 --- /dev/null +++ b/uniffi-bindgen-cs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "uniffi-bindgen-cs" +version = "0.13.1" +publish = false +edition = "2021" +license = "GPL-3.0-or-later" +#readme = "README.md" +keywords = ["cccb", "cccb-servicepoint", "uniffi"] + +[dependencies.uniffi-bindgen-cs] +git = "https://github.com/NordSecurity/uniffi-bindgen-cs" +# tag="v0.8.3+v0.25.0" +rev = "f68639fbc720b50ebe561ba75c66c84dc456bdce" diff --git a/crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-cs.rs b/uniffi-bindgen-cs/src/main.rs similarity index 100% rename from crates/servicepoint_binding_uniffi/src/bin/uniffi-bindgen-cs.rs rename to uniffi-bindgen-cs/src/main.rs diff --git a/crates/servicepoint_binding_uniffi/uniffi.toml b/uniffi.toml similarity index 100% rename from crates/servicepoint_binding_uniffi/uniffi.toml rename to uniffi.toml