diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2d347ba..e9947d5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,10 +30,15 @@ jobs: - name: build run: cargo build + - name: generate bindings + run: ./generate-binding.sh + - name: check that generated files did not change + run: output=$(git status --porcelain) && [ -z "$output" ] + - name: build example -- glibc release - run: cd example && make -r clean-all && make -r LIBC=gnu LINK=dynamic PROFILE=release + run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=release - name: build example -- glibc debug - run: cd example && make -r clean-all && make -r LIBC=gnu LINK=dynamic PROFILE=debug + run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=debug build-size-gnu-unstable: runs-on: ubuntu-latest @@ -47,6 +52,5 @@ jobs: - name: install rust targets run: rustup toolchain install nightly -t aarch64-unknown-linux-gnu -c rust-src --no-self-update - - name: build example -- glibc size_optimized - run: cd example && make clean-all -r - && make -r LIBC=gnu LINK=dynamic PROFILE=size_optimized CARGO="rustup run nightly cargo" LTO=1 + - name: build example -- glibc size-optimized + run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=size-optimized CARGO="rustup run nightly cargo" LTO=1 diff --git a/.gitignore b/.gitignore index 6f84fcd..f48287d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ out .direnv .envrc result -mutants.* -_out_* \ No newline at end of file +mutants.* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 63012a0..78e2169 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,24 +4,15 @@ version = 4 [[package]] name = "adler2" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -34,44 +25,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "once_cell_polyfill", + "once_cell", "windows-sys", ] [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitvec" @@ -105,10 +96,29 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.2.27" +name = "cbindgen" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -117,15 +127,42 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[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.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "crc32fast" @@ -137,33 +174,32 @@ dependencies = [ ] [[package]] -name = "env_filter" -version = "0.1.3" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ - "log", - "regex", + "libc", + "windows-sys", ] [[package]] -name = "env_logger" -version = "0.11.8" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -177,9 +213,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", @@ -188,14 +224,25 @@ dependencies = [ ] [[package]] -name = "inherent" -version = "1.0.12" +name = "hashbrown" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "equivalent", + "hashbrown", ] [[package]] @@ -205,28 +252,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "jiff" -version = "0.2.15" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", -] - -[[package]] -name = "jiff-static" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" @@ -240,9 +269,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "log" @@ -252,15 +287,15 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -271,39 +306,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -324,9 +332,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.3.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "radium" @@ -334,35 +342,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "rust-lzma" version = "0.6.0" @@ -373,6 +352,25 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "serde" version = "1.0.219" @@ -394,15 +392,35 @@ dependencies = [ ] [[package]] -name = "servicepoint" -version = "0.15.1" +name = "serde_json" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2800caad491cb44f67e5dd5b8c61ece368eecfe588155d03c7d9864acbad6919" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "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.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" dependencies = [ "bitvec", "bzip2", "flate2", - "inherent", "log", "once_cell", "rust-lzma", @@ -412,10 +430,9 @@ dependencies = [ [[package]] name = "servicepoint_binding_c" -version = "0.15.0" +version = "0.14.1" dependencies = [ - "env_logger", - "paste", + "cbindgen", "servicepoint", ] @@ -426,10 +443,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "syn" -version = "2.0.104" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -442,6 +465,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "2.0.12" @@ -462,6 +498,47 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -562,6 +639,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index f6bef14..0eba069 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "servicepoint_binding_c" -version = "0.15.0" +version = "0.14.1" publish = true edition = "2021" license = "GPL-3.0-or-later" @@ -8,26 +8,23 @@ 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.28.0" + [dependencies.servicepoint] -version = "0.15.1" +package = "servicepoint" +version = "0.14.1" default-features = false -[dependencies.env_logger] -version = "0.11.8" -optional = true - -[dependencies] -paste = "1.0.15" - [features] all_compressions = ["servicepoint/all_compressions"] -default = ["all_compressions", "servicepoint/default", "env_logger"] -env_logger = ["dep:env_logger"] +default = ["all_compressions", "servicepoint/default"] [lints.rust] missing-docs = "warn" @@ -39,7 +36,7 @@ missing_safety_doc = "allow" [package.metadata.docs.rs] all-features = true -[profile.size_optimized] +[profile.size-optimized] inherits = "release" opt-level = 'z' # Optimize for size lto = true # Enable link-time optimization diff --git a/README.md b/README.md index fa0a49b..4788c75 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ # servicepoint_binding_c -[![Release](https://git.berlin.ccc.de/servicepoint/servicepoint_binding_c/badges/release.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint_binding_c/releases) [![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) -[![CI](https://git.berlin.ccc.de/servicepoint/servicepoint_binding_c/badges/workflows/rust.yml/badge.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint_binding_c) 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". @@ -14,7 +12,7 @@ This crate contains C bindings for the [servicepoint](https://git.berlin.ccc.de/ ## Examples -```c +```c++ #include #include "servicepoint.h" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..83e9641 --- /dev/null +++ b/build.rs @@ -0,0 +1,35 @@ +//! 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}; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + println!("cargo::rerun-if-changed={crate_dir}"); + + let config = + cbindgen::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"; + + if let Ok(bindings) = cbindgen::generate_with_config(crate_dir, config) { + bindings.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}"); + } + } else { + eprintln!("cargo:warning=Servicepoint header could not be generated"); + } +} diff --git a/cbindgen.toml b/cbindgen.toml index 6825722..221e915 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -16,28 +16,27 @@ line_endings = "LF" ############################# Codegen Options ################################## -style = "both" +style = "type" usize_is_size_t = true # this is needed because otherwise the order in the C bindings is different on different machines sort_by = "Name" -include_guard = "SERVICEPOINT_BINDINGS_C" - [parse] parse_deps = true include = ["servicepoint", "std"] -extra_bindings = ["servicepoint", "servicepoint_binding_c"] +extra_bindings = ["servicepoint"] [parse.expand] -crates = ["servicepoint_binding_c", "paste"] -features = [] +features = ["full"] [export] include = [] -exclude = ["BitVec"] +exclude = [] [export.rename] +"SpBitVec" = "BitVec" +"SpByteSlice" = "ByteSlice" [enum] rename_variants = "QualifiedScreamingSnakeCase" diff --git a/devShells.nix b/devShells.nix deleted file mode 100644 index de4f709..0000000 --- a/devShells.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ - pkgs, - fenix, - selfPkgs, - ... -}: -let - common = { - RUST_BACKTRACE = 1; - RUST_LOG = "all"; - packages = with pkgs; [ - gdb - nix-output-monitor - ]; - }; -in rec { - nightly = pkgs.mkShell ( common // { - inputsFrom = [ - selfPkgs.servicepoint-binding-c-nightly-release - ]; - packages = with pkgs; [ - cargo-expand - cargo-tarpaulin - nix-output-monitor - gcc - gnumake - rustfmt - xe - libgcc - libunwind - pkgsStatic.gcc - pkgsStatic.libgcc - pkgsStatic.musl - rust-cbindgen - ]; - }); - stable = pkgs.mkShell (common // { - inputsFrom = [ - selfPkgs.servicepoint-binding-c - selfPkgs.announce - ]; - packages = with pkgs; [ - (pkgs.symlinkJoin { - name = "rust-toolchain"; - paths = with pkgs; [ - rustc - cargo - rustPlatform.rustcSrc - rustPlatform.rustLibSrc - rustfmt - clippy - cargo-expand - cargo-tarpaulin - ]; - }) - cargo-expand - cargo-tarpaulin - gcc - gnumake - xe - libgcc - libunwind - pkgsStatic.gcc - pkgsStatic.libgcc - pkgsStatic.musl - ]; - - RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; - }); - default = stable; -} diff --git a/example/Makefile b/example/Makefile index 15e53d1..32e6091 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,121 +1,129 @@ -# based on https://make.mad-scientist.net/papers/multi-architecture-builds/ - -_libc := $(if $(LIBC),$(LIBC),gnu) -ifeq (,$(filter gnu musl,$(_libc))) - _link_type := $(error "LIBC has to be set to one of: gnu, musl") -endif - -_link_type := $(if $(LINK),$(LINK),dynamic) -ifeq (,$(filter dynamic static,$(_link_type))) - _link_type := $(error "LINK has to be set to one of: dynamic, static") -endif - -_profile := $(if $(PROFILE),$(PROFILE),release) -ifeq (,$(filter release debug size_optimized,$(_profile))) - _profile := $(error "PROFILE has to be set to one of: debug, release, size_optimized") -endif - -ARCH ?= $(shell uname -m) -RUST_TARGET := $(ARCH)-unknown-linux-$(_libc) - -#----- Begin Boilerplate -ifeq (,$(filter _out_%,$(notdir $(CURDIR)))) - include target.mk -else -#----- End Boilerplate - -VPATH = $(SRCDIR) - CARGO ?= cargo STRIP ?= strip -CC ?= gcc -FEATURES := ""#"env_logger" +FEATURES := "" THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) REPO_ROOT := $(realpath $(THIS_DIR)/..) -CARGO_OBJDIR := cargo/$(RUST_TARGET)/$(_profile) +export SERVICEPOINT_HEADER_OUT := $(REPO_ROOT)/include -_rust_cli_profile := $(if $(filter $(_profile),debug),dev,$(_profile)) +override CFG_MUSL := $(if $(CFG_MUSL),$(CFG_MUSL),$(if $(MUSL),$(MUSL),0)) +override CFG_PROFILE := $(if $(CFG_PROFILE),$(CFG_PROFILE),$(if $(PROFILE),$(PROFILE),release)) -STRIPFLAGS += -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag +CCFLAGS += -Wall -fwhole-program -fPIE -pie + +STRIPFLAGS := -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag + +ifeq ($(CFG_MUSL), 1) + TARGET ?= x86_64-unknown-linux-musl + CC ?= musl-gcc + CCFLAGS += -static -lservicepoint_binding_c + RUSTFLAGS += --crate-type=staticlib -Ctarget-feature=-crt-static +else + TARGET ?= x86_64-unknown-linux-gnu + CC ?= gcc + #CCFLAGS += -shared + CCFLAGS += -Wl,-Bstatic -lservicepoint_binding_c -Wl,-Bdynamic +endif + +#ifeq ($(CFG_PROFILE), size-optimized) +# CCFLAGS += -nodefaultlibs -lc +#endif + +RUST_TARGET_DIR := $(REPO_ROOT)/target/$(TARGET)/$(CFG_PROFILE) + +ifeq ($(CFG_PROFILE), size-optimized) + CARGO_PROFILE := size-optimized + CCFLAGS += -Oz \ + -fwrapv -fomit-frame-pointer -fno-stack-protector\ + -fno-unroll-loops \ + -fno-unwind-tables -fno-asynchronous-unwind-tables \ + -fmerge-all-constants \ + -Wl,-z,norelro \ + -Wl,--hash-style=gnu \ + -fvisibility=hidden \ + -Bsymbolic \ + -Wl,--exclude-libs,ALL \ + -fno-ident \ + -fno-exceptions + CARGOFLAGS += -Zbuild-std="core,std,alloc,proc_macro,panic_abort" \ + -Zbuild-std-features="panic_immediate_abort" + RUSTFLAGS += -Zlocation-detail=none \ + -Zfmt-debug=none \ + -C link-arg=-z,norelro \ + -C panic=abort + #-C link-arg=--hash-style=gnu +else ifeq ($(CFG_PROFILE), release) + CARGO_PROFILE := release + CCFLAGS += -O2 +else ifeq ($(CFG_PROFILE), debug) + CCFLAGS += -Og + CARGO_PROFILE := dev +else + CFG_PROFILE := $(error "PROFILE has to be set to one of: debug, release, size-optimized") +endif CARGOFLAGS += --manifest-path=$(REPO_ROOT)/Cargo.toml \ - --profile=$(_rust_cli_profile) \ + --profile=$(CARGO_PROFILE) \ --no-default-features \ --features=$(FEATURES) \ - --target=$(RUST_TARGET) \ - --target-dir=cargo + --target=$(TARGET) -STATIC_LINK_LIBS += -lservicepoint_binding_c -_servicepoint_cflags := -I $(REPO_ROOT)/include -L $(CARGO_OBJDIR) -CFLAGS += -Wall -Wextra -pedantic -fwhole-program -fPIE -pie -_no_debug_cflags := -ffunction-sections -fdata-sections -Wl,--gc-sections -size_optimized_CFLAGS += -Oz \ - -fwrapv -fomit-frame-pointer -fno-stack-protector \ - -fno-unroll-loops \ - -fno-unwind-tables -fno-asynchronous-unwind-tables \ - -fmerge-all-constants \ - -Wl,-z,norelro \ - -Wl,--hash-style=gnu \ - -fvisibility=hidden \ - -Bsymbolic \ - -Wl,--exclude-libs,ALL \ - -fno-ident \ - -fno-exceptions \ - $(_no_debug_cflags) - -release_CFLAGS += -O2 \ - $(_no_debug_cflags) -debug_CFLAGS += -Og -static_CFLAGS += -static $(STATIC_LINK_LIBS) -dynamic_CFLAGS += -Wl,-Bstatic $(STATIC_LINK_LIBS) -Wl,-Bdynamic - -_servicepoint_cflags := $(shell pkg-config --libs servicepoint --cflags || echo -I $(REPO_ROOT)/include -L $(CARGO_OBJDIR)) -CFLAGS += $($(_libc)_CFLAGS) $($(_profile)_CFLAGS) $($(_link_type)_CFLAGS) $(_servicepoint_cflags) -ifeq ($(LTO), 1) - CFLAGS += -flto -endif -ifeq ($(_libc),gnu) - ifeq ($(_link_type),static) - CFLAGS += $(error "statically linking glibc is known to be broken") - endif -endif - -size_optimized_RUSTFLAGS += -Zlocation-detail=none \ - -Zfmt-debug=none \ - -C link-arg=-z,norelro \ - -C panic=abort \ - -C link-arg=-Wl,--hash-style=gnu -static_RUSTFLAGS += --crate-type=staticlib -Ctarget-feature=+crt-static - -RUSTFLAGS += $($(_libc)_RUSTFLAGS) $($(_profile)_RUSTFLAGS) $($(_link_type)_RUSTFLAGS) -ifneq ($(_profile), debug) +ifneq ($(CFG_PROFILE), debug) + CCFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections RUSTFLAGS += -C link-arg=-s -C link-arg=-Wl,--gc-sections endif -# ADD NEW EXAMPLES HERE -_c_src := src/announce.c src/brightness_tester.c src/header_logger.c \ - src/moving_line.c src/random_stuff.c src/wiping_clear.c src/undefined.c +ifeq ($(LTO), 1) + CCFLAGS += -flto +endif + +_c_src := $(wildcard ./src/*.c) _programs := $(basename $(notdir $(_c_src))) -_bins := $(_programs) +_bins := $(addprefix out/, $(_programs)) _unstripped_bins := $(addsuffix _unstripped, $(_bins)) +_run_programs := $(addprefix run_, $(_programs)) +_rs_src := $(wildcard ../src/**.rs) ../Cargo.lock ../Cargo.toml ../cbindgen.toml +_sp_artifacts := $(SERVICEPOINT_HEADER_OUT)/servicepoint.h $(RUST_TARGET_DIR)/libservicepoint_binding_c.a $(RUST_TARGET_DIR)/libservicepoint_binding_c.so -.SUFFIXES: +all: $(_bins) -.PHONY: all build-rust +clean: clean-c clean-rust -all: build-rust $(_bins) +clean-c: + rm -r out || true -$(_unstripped_bins): %_unstripped : src/%.c src/helpers.h build-rust - $(CC) $< $(CFLAGS) -o $@ +clean-rust: + rm $(SERVICEPOINT_HEADER_OUT)/servicepoint.h || true + cargo clean -$(_bins): %: %_unstripped +.PHONY: all clean sizes $(_run_programs) clean-c clean-rust + +$(_unstripped_bins): out/%_unstripped: src/%.c $(SERVICEPOINT_HEADER_OUT)/servicepoint.h $(_sp_artifacts) + mkdir -p out || true + $(CC) $< \ + -I $(SERVICEPOINT_HEADER_OUT) \ + -L $(RUST_TARGET_DIR) \ + $(CCFLAGS) \ + -o $@ + +$(_bins): out/%: out/%_unstripped $(STRIP) $(STRIPFLAGS) $^ -o $@ -build-rust: +$(_sp_artifacts): $(_rs_src) + mkdir -p $(SERVICEPOINT_HEADER_OUT) || true # generate servicepoint header and library to link against $(CARGO) rustc $(CARGOFLAGS) -- $(RUSTFLAGS) -#----- Begin Boilerplate -endif +$(_run_programs): run_%: out/% FORCE + ./$< + +sizes: $(_bins) + ls -lB out + +analyze-size: out/$(BIN)_unstripped + nm --print-size --size-sort --reverse-sort --radix=d --demangle out/$(BIN)_unstripped \ + | awk '{size=$$2+0; print size "\t" $$4}' \ + | less + +FORCE: ; diff --git a/example/src/announce.c b/example/src/announce.c index dda1f67..d256cff 100644 --- a/example/src/announce.c +++ b/example/src/announce.c @@ -1,9 +1,16 @@ -#include "helpers.h" +#include +#include "servicepoint.h" + int main(void) { - sock_init(); + printf("test\n"); - sp_udp_socket_send_header(sock, (Header) {.command_code = COMMAND_CODE_CLEAR}); + UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); + //UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); + if (connection == NULL) + return 1; + + sp_udp_send_header(connection, (Header) {.command_code = COMMAND_CODE_CLEAR}); CharGrid *grid = sp_char_grid_new(5, 2); if (grid == NULL) @@ -20,10 +27,11 @@ int main(void) { sp_char_grid_set(grid, 3, 1, 'l'); sp_char_grid_set(grid, 4, 1, 'd'); - Packet *packet = sp_char_grid_try_into_packet(grid, 0, 0); + Packet *packet = sp_char_grid_into_packet(grid, 0, 0); if (packet == NULL) return 1; - sp_udp_socket_send_packet(sock, packet); + sp_udp_send_packet(connection, packet); + sp_udp_free(connection); return 0; } diff --git a/example/src/brightness_tester.c b/example/src/brightness_tester.c index cdd2a05..c3e42ba 100644 --- a/example/src/brightness_tester.c +++ b/example/src/brightness_tester.c @@ -1,35 +1,30 @@ #include "servicepoint.h" -#include "helpers.h" -void enable_all_pixels(void) { +int main(void) { + UdpSocket *connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); + //UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); + if (connection == NULL) + return -1; + Bitmap *all_on = sp_bitmap_new_max_sized(); sp_bitmap_fill(all_on, true); - BitmapCommand *bitmapCommand = sp_bitmap_command_from_bitmap(all_on); - Packet *packet = sp_bitmap_command_try_into_packet(bitmapCommand); - if (packet != NULL) - sp_udp_socket_send_packet(sock, packet); -} + Packet *packet = sp_bitmap_into_packet(all_on, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); + if (packet == NULL) + return -1; -void make_brightness_pattern(BrightnessGrid *grid) { - ByteSlice slice = sp_brightness_grid_data_ref_mut(grid); - for (size_t index = 0; index < slice.length; index++) { - slice.start[index] = (uint8_t)(index % ((size_t) Brightness_MAX)); - } -} - -int main(void) { - sock_init(); - - enable_all_pixels(); + sp_udp_send_packet(connection, packet); BrightnessGrid *grid = sp_brightness_grid_new(TILE_WIDTH, TILE_HEIGHT); - make_brightness_pattern(grid); - Packet *packet = sp_brightness_grid_command_try_into_packet(sp_brightness_grid_command_from_grid(grid)); - if (packet == NULL) - return -2; + ByteSlice slice = sp_brightness_grid_unsafe_data_ref(grid); + for (size_t index = 0; index < slice.length; index++) { + slice.start[index] = (uint8_t) (index % ((size_t) Brightness_MAX)); + } - sp_udp_socket_send_packet(sock, packet); + packet = sp_brightness_grid_into_packet(grid, 0, 0); + sp_udp_send_packet(connection, packet); + + sp_udp_free(connection); return 0; } diff --git a/example/src/header_logger.c b/example/src/header_logger.c deleted file mode 100644 index 82b8566..0000000 --- a/example/src/header_logger.c +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "servicepoint.h" - -#define DEFAULT_LISTEN_IP "127.0.0.1" - -void handle_error(const char *msg) { - perror(msg); - exit(EXIT_FAILURE); -} - -bool log_command(struct GenericCommand *command) { - switch (command->tag) { - case COMMAND_TAG_INVALID: { - printf("-> this is an invalid command\n"); - break; - } - case COMMAND_TAG_HARD_RESET: { - printf("-> HardReset command - exiting\n"); - return true; - } - case COMMAND_TAG_BITMAP: { - BitmapCommand *bitmapCommand = command->data.bitmap; - - CompressionCode compression = sp_bitmap_command_get_compression(bitmapCommand); - - size_t x, y; - sp_bitmap_command_get_origin(bitmapCommand, &x, &y); - - Bitmap *bitmap = sp_bitmap_command_get_bitmap_mut(bitmapCommand); - size_t w = sp_bitmap_width(bitmap); - size_t h = sp_bitmap_height(bitmap); - - printf("-> BitmapCommand with params: x=%zu, y=%zu, w=%zu, h=%zu, compression=%hu\n", - x, y, w, h, compression); - break; - } - case COMMAND_TAG_BRIGHTNESS_GRID: { - BrightnessGridCommand *gridCommand = command->data.brightness_grid; - - size_t x, y; - sp_brightness_grid_command_get_origin(gridCommand, &x, &y); - - BrightnessGrid *grid = sp_brightness_grid_command_get_grid_mut(gridCommand); - size_t w = sp_brightness_grid_width(grid); - size_t h = sp_brightness_grid_height(grid); - - printf("-> BrightnessGridCommand with params: x=%zu, y=%zu, w=%zu, h=%zu\n", - x, y, w, h); - break; - } - case COMMAND_TAG_CHAR_GRID: { - CharGridCommand *gridCommand = command->data.char_grid; - - size_t x, y; - sp_char_grid_command_get_origin(gridCommand, &x, &y); - - CharGrid *grid = sp_char_grid_command_get_grid_mut(gridCommand); - size_t w = sp_char_grid_width(grid); - size_t h = sp_char_grid_height(grid); - - printf("-> CharGridCommand with params: x=%zu, y=%zu, w=%zu, h=%zu\n", - x, y, w, h); - break; - } - case COMMAND_TAG_CP437_GRID: { - Cp437GridCommand *gridCommand = command->data.cp437_grid; - - size_t x, y; - sp_cp437_grid_command_get_origin(gridCommand, &x, &y); - - Cp437Grid *grid = sp_cp437_grid_command_get_grid_mut(gridCommand); - size_t w = sp_cp437_grid_width(grid); - size_t h = sp_cp437_grid_height(grid); - - printf("-> Cp437GridCommand with params: x=%zu, y=%zu, w=%zu, h=%zu\n", - x, y, w, h); - break; - } - case COMMAND_TAG_BIT_VEC: { - BitVecCommand *bitvecCommand = command->data.bit_vec; - - size_t offset = sp_bit_vec_command_get_offset(bitvecCommand); - CompressionCode compression = sp_bit_vec_command_get_compression(bitvecCommand); - - BinaryOperation operation = sp_bit_vec_command_get_operation(bitvecCommand); - char *operationText; - switch (operation) { - case BINARY_OPERATION_AND: - operationText = "and"; - break; - case BINARY_OPERATION_OR: - operationText = "or"; - break; - case BINARY_OPERATION_XOR: - operationText = "xor"; - break; - case BINARY_OPERATION_OVERWRITE: - operationText ="overwrite"; - break; - default: - operationText ="unknown"; - break; - } - - DisplayBitVec *bitvec = sp_bit_vec_command_get_bitvec_mut(bitvecCommand); - size_t len = sp_display_bit_vec_len(bitvec); - - printf("-> BitVecCommand with params: offset=%zu, length=%zu, compression=%hu, operation=%s\n", - offset, len, compression, operationText); - break; - } - case COMMAND_TAG_CLEAR: { - printf("-> ClearCommand\n"); - break; - } - case COMMAND_TAG_BITMAP_LEGACY: { - printf("-> BitmapLinearLegacy\n"); - break; - } - case COMMAND_TAG_FADE_OUT:{ - printf("-> FadeOutCommand\n"); - break; - } - case COMMAND_TAG_GLOBAL_BRIGHTNESS: { - Brightness brightness = sp_global_brightness_command_get_brightness(command->data.global_brightness); - printf("-> GlobalBrightnessCommand with params: brightness=%hu\n", brightness); - break; - } - default: { - printf("-> unknown command tag %d\n", command->tag); - break; - } - } - - return false; -} - -int main(int argc, char **argv) { - //init_env_logger(); - - int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (udp_socket == -1) - handle_error("socket could not be created\n"); - - char *listen_addr_arg; - if (argc > 1) { - listen_addr_arg = argv[1]; - } else { - listen_addr_arg = DEFAULT_LISTEN_IP; - } - - struct in_addr addr; - if (inet_aton(listen_addr_arg, &addr) == 0) - handle_error("listen ip could not be parsed\n"); - - struct sockaddr_in sockaddrIn = { - .sin_addr = addr, - .sin_family = AF_INET, - .sin_port = htons(2342), - }; - memset(sockaddrIn.sin_zero, 0, sizeof(sockaddrIn.sin_zero)); - - if (bind(udp_socket, (struct sockaddr *) &sockaddrIn, sizeof(sockaddrIn)) == -1) - handle_error("could not bind socket\n"); - - printf("socket ready to receive on %s\n", listen_addr_arg); - - uint8_t buffer[10000]; - bool done = false; - while (!done) { - memset(buffer, 0, sizeof(buffer)); - printf("\n"); - - ssize_t num_bytes = recv(udp_socket, (void *) buffer, sizeof(buffer), 0); - if (num_bytes == -1) - handle_error("could not read from client"); - - Packet *packet = sp_packet_try_load((ByteSlice) { - .start = buffer, - .length = num_bytes, - }); - if (packet == NULL) { - printf("received invalid packet\n"); - continue; - } - - struct Header *header = sp_packet_get_header_mut(packet); - - ByteSlice payload = sp_packet_get_payload(packet); - printf("Received packet: cc=%d, a=%d, b=%d, c=%d, d=%d, payload=%p (len %zu)\n", - header->command_code, header->a, header->b, header->c, header->d, - payload.start, payload.length); - - struct GenericCommand *command = sp_generic_command_try_from_packet(packet); - done = log_command(command); - - sp_generic_command_free(command); - } - - close(udp_socket); - exit(EXIT_SUCCESS); -} diff --git a/example/src/helpers.h b/example/src/helpers.h deleted file mode 100644 index 96a0b0a..0000000 --- a/example/src/helpers.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#ifndef SERVICEPOINT_BINDING_C_MSLEEP_H -#define SERVICEPOINT_BINDING_C_MSLEEP_H - -#include -#include -#include -#include "servicepoint.h" - -static UdpSocket *sock = NULL; - -void sock_free() { - sp_udp_socket_free(sock); -} - -void sock_init() { - sock = sp_udp_socket_open_ipv4(127, 0, 0, 1, 2342); - //sock = sp_udp_open_ipv4(172, 23, 42, 29, 2342); - if (sock == NULL) - exit(-1); - atexit(sock_free); -} - -/// TODO: all of this for sleeping n ms? There should be a better way! -int msleep(long msec) { - if (msec < 0) { - errno = EINVAL; - return -1; - } - - struct timespec ts = { - .tv_sec = msec / 1000, - .tv_nsec = (msec % 1000) * 1000000, - }; - - int res; - do { - res = nanosleep(&ts, &ts); - } while (res && errno == EINTR); - - return res; -} - -#endif //SERVICEPOINT_BINDING_C_MSLEEP_H diff --git a/example/src/moving_line.c b/example/src/moving_line.c deleted file mode 100644 index 1aacceb..0000000 --- a/example/src/moving_line.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "servicepoint.h" -#include "helpers.h" - -int main(void) { - sock_init(); - - int result = 0; - Bitmap *bitmap = sp_bitmap_new_max_sized(); - for (size_t x = 0; x < sp_bitmap_width(bitmap); x++) { - sp_bitmap_fill(bitmap, false); - - for (size_t y = 0; y < sp_bitmap_height(bitmap); y++) { - sp_bitmap_set(bitmap, (y + x) % PIXEL_WIDTH, y, true); - } - - BitmapCommand *command = sp_bitmap_command_from_bitmap(sp_bitmap_clone(bitmap)); - Packet *packet = sp_bitmap_command_try_into_packet(command); - if (packet == NULL) { - result = -2; - goto exit; - } - - if (!sp_udp_socket_send_packet(sock, packet)) { - result = -3; - goto exit; - } - - msleep(SP_FRAME_PACING_MS); - } - - exit: - sp_bitmap_free(bitmap); - return result; -} diff --git a/example/src/random_stuff.c b/example/src/random_stuff.c index 417899a..2ebd485 100644 --- a/example/src/random_stuff.c +++ b/example/src/random_stuff.c @@ -1,9 +1,10 @@ #include #include "servicepoint.h" -#include "helpers.h" int main(void) { - sock_init(); + UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); + if (connection == NULL) + return 1; Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT); if (pixels == NULL) @@ -11,13 +12,15 @@ int main(void) { sp_bitmap_fill(pixels, true); - Packet *packet = sp_bitmap_try_into_packet(pixels, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); + Packet *packet = sp_bitmap_into_packet(pixels, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); if (packet == NULL) return 1; - Header *header = sp_packet_get_header_mut(packet); + Header *header = sp_packet_get_header(packet); printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d); - sp_udp_socket_send_packet(sock, packet); + sp_udp_send_packet(connection, packet); + + sp_udp_free(connection); return 0; } diff --git a/example/src/undefined.c b/example/src/undefined.c deleted file mode 100644 index dfa44d8..0000000 --- a/example/src/undefined.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include "servicepoint.h" -#include "helpers.h" - -/// DO NOT DO ANY OF THIS! -int main(void) { - BitmapCommand *bmcmd = sp_bitmap_command_new(sp_bitmap_new_max_sized(), 0, 0, COMPRESSION_CODE_UNCOMPRESSED); - BitVecCommand *bvcmd = (BitVecCommand *) bmcmd; - sp_bit_vec_command_free(bvcmd); - - uint8_t *data = calloc(1024, 1); - struct GenericCommand generic = { - .tag = COMMAND_TAG_BRIGHTNESS_GRID, - .data = {.null = data}, - }; - - sock_init(); - - sp_udp_socket_send_command(sock, &generic); - - return 0; -} diff --git a/example/src/wiping_clear.c b/example/src/wiping_clear.c deleted file mode 100644 index dc76604..0000000 --- a/example/src/wiping_clear.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "servicepoint.h" -#include "helpers.h" - -int main() { - sock_init(); - - Bitmap *enabled_pixels = sp_bitmap_new_max_sized(); - sp_bitmap_fill(enabled_pixels, true); - - int result = 0; - for (int x = 0; x < PIXEL_WIDTH; x++) { - for (int y = 0; y < PIXEL_HEIGHT; y++) { - sp_bitmap_set(enabled_pixels, x, y, false); - } - - DisplayBitVec *bitvec = sp_bitmap_into_bitvec(sp_bitmap_clone(enabled_pixels)); - BitVecCommand *command = sp_bit_vec_command_new(bitvec, 0, BINARY_OPERATION_AND, COMPRESSION_CODE_LZMA); - Packet *packet = sp_bit_vec_command_try_into_packet(command); - if (packet == NULL) { - result = -2; - goto exit; - } - if (!sp_udp_socket_send_packet(sock, packet)) { - result = -3; - goto exit; - } - - msleep(SP_FRAME_PACING_MS); - } - - exit: - sp_bitmap_free(enabled_pixels); - return result; -} diff --git a/example/target.mk b/example/target.mk deleted file mode 100644 index b7e743c..0000000 --- a/example/target.mk +++ /dev/null @@ -1,56 +0,0 @@ -# Based on https://make.mad-scientist.net/papers/multi-architecture-builds/ - -.SUFFIXES: - -OBJDIR := _out_$(RUST_TARGET)-$(_link_type)-$(_profile) - -# Define the rules to build in the target subdirectories. -# -MAKETARGET = $(MAKE) --no-print-directory -C $@ -f $(CURDIR)/Makefile \ - SRCDIR=$(CURDIR) $(MAKECMDGOALS) - -.PHONY: $(OBJDIR) -$(OBJDIR): - +@[ -d "$@" ] || mkdir -p "$@" - +@$(MAKETARGET) - -# These rules keep make from trying to use the match-anything rule below to -# rebuild the makefiles--ouch! Obviously, if you don't follow my convention -# of using a `.mk' suffix on all non-standard makefiles you'll need to change -# the pattern rule. -# -Makefile : ; -%.mk :: ; - -# Anything we don't know how to build will use this rule. The command is a -# do-nothing command, but the prerequisites ensure that the appropriate -# recursive invocations of make will occur. -# -% :: $(OBJDIR) ; - -# The clean rule is best handled from the source directory: since we're -# rigorous about keeping the target directories containing only target files -# and the source directory containing only source files, `clean' is as trivial -# as removing the target directories! -# -.PHONY: clean clean-all help -clean: - rm -rf $(OBJDIR) -clean-all: - rm -rf _out_* - -help: - @echo "You have the choice of the following parameters:" - @echo "" - @echo "Variable | Description | Default | Values" - @echo "---------+----------------------+-----------+---------------------------" - @echo "LIBC | libc to link against | 'gnu' | 'gnu' or 'musl'" - @echo "PROFILE | Optimization profile | 'release' | 'debug' or 'size_optimized'" - @echo "LINK | | 'dynamic' | 'dynamic' or 'static'" - @echo "CARGO | cargo binary to use | 'cargo' | 'rustup run nightly cargo'" - @echo "CC | C compiler to use | 'gcc' | 'musl-gcc'" - @echo "STRIP | strip command to use | 'strip' | -" - @echo "" - @echo "When building this project, each configuration will result in a separate output directory." - @echo "The target clean only removes the output directory of the specified configuration." - @echo "The target clean-all can be used to remove the builds of all configurations." diff --git a/flake.lock b/flake.lock index 4db936e..9e70e85 100644 --- a/flake.lock +++ b/flake.lock @@ -1,80 +1,25 @@ { "nodes": { - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1751006353, - "narHash": "sha256-icKFXb83uv2ezRCfuq5G8QSwCuaoLywLljSL+UGmPPI=", - "owner": "nix-community", - "repo": "fenix", - "rev": "b37f026b49ecb295a448c96bcbb0c174c14fc91b", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, - "nix-filter": { - "locked": { - "lastModified": 1731533336, - "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=", - "owner": "numtide", - "repo": "nix-filter", - "rev": "f7653272fd234696ae94229839a99b73c9ab7de0", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "nix-filter", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1750838302, - "narHash": "sha256-aVkL3/yu50oQzi2YuKo0ceiCypVZpZXYd2P2p1FMJM4=", + "lastModified": 1739357830, + "narHash": "sha256-9xim3nJJUFbVbJCz48UP4fGRStVW5nv4VdbimbKxJ3I=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7284e2decc982b81a296ab35aa46e804baaa1cfe", + "rev": "0ff09db9d034a04acd4e8908820ba0b410d7a33a", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-25.05", + "ref": "nixos-24.11", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { - "fenix": "fenix", - "nix-filter": "nix-filter", "nixpkgs": "nixpkgs" } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1750871759, - "narHash": "sha256-hMNZXMtlhfjQdu1F4Fa/UFiMoXdZag4cider2R9a648=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "317542c1e4a3ec3467d21d1c25f6a43b80d83e7d", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 1501caa..82983f7 100644 --- a/flake.nix +++ b/flake.nix @@ -2,22 +2,16 @@ description = "Flake for the servicepoint library."; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; - nix-filter.url = "github:numtide/nix-filter"; - fenix = { - url = "github:nix-community/fenix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; }; outputs = inputs@{ self, nixpkgs, - fenix, - nix-filter }: let + lib = nixpkgs.lib; supported-systems = [ "x86_64-linux" "aarch64-linux" @@ -26,20 +20,52 @@ ]; forAllSystems = f: - nixpkgs.lib.genAttrs supported-systems ( + lib.genAttrs supported-systems ( system: f rec { - inherit system self; pkgs = nixpkgs.legacyPackages.${system}; - lib = pkgs.lib // nix-filter.lib; - fenix = inputs.fenix.packages.${system}; - selfPkgs = self.packages.${system}; + inherit system; } ); in { - packages = forAllSystems (import ./packages.nix); - devShells = forAllSystems (import ./devShells.nix); + devShells = forAllSystems ( + { pkgs, system }: + { + default = pkgs.mkShell rec { + buildInputs = with pkgs; [ + xe + xz + libgcc + libunwind + pkgsStatic.musl + ]; + + nativeBuildInputs = with pkgs; [ + (pkgs.symlinkJoin { + name = "rust-toolchain"; + paths = with pkgs; [ + rustc + cargo + rustPlatform.rustcSrc + rustfmt + clippy + cargo-expand + cargo-tarpaulin + ]; + }) + gcc + gdb + pkgsStatic.gcc + gnumake + pkg-config + ]; + + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }; + } + ); + formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style); }; } diff --git a/generate-binding.sh b/generate-binding.sh index 744ad29..45301bd 100755 --- a/generate-binding.sh +++ b/generate-binding.sh @@ -3,4 +3,4 @@ set -e SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -cbindgen --config $SCRIPT_PATH/cbindgen.toml --crate servicepoint_binding_c --output $SCRIPT_PATH/include/servicepoint.h +SERVICEPOINT_HEADER_OUT="$SCRIPT_PATH/include" cargo build --release diff --git a/include/servicepoint.h b/include/servicepoint.h index e2beb27..5863ef5 100644 --- a/include/servicepoint.h +++ b/include/servicepoint.h @@ -1,6 +1,3 @@ -#ifndef SERVICEPOINT_BINDINGS_C -#define SERVICEPOINT_BINDINGS_C - /* Generated with cbindgen:0.28.0 */ /* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */ @@ -99,7 +96,7 @@ typedef uint8_t BinaryOperation; #endif // __cplusplus /** - * The u16 command codes used for the [`crate::Command`]s. + * The u16 command codes used for the [Command]s. */ enum CommandCode #ifdef __cplusplus @@ -128,59 +125,28 @@ enum CommandCode typedef uint16_t CommandCode; #endif // __cplusplus -/** - * Specifies the kind of command struct. - * - * This is _not_ equivalent to the [servicepoint::CommandCode]s. - */ -enum CommandTag -#ifdef __cplusplus - : uint8_t -#endif // __cplusplus - { - COMMAND_TAG_INVALID = 0, - COMMAND_TAG_BITMAP, - COMMAND_TAG_BIT_VEC, - COMMAND_TAG_BRIGHTNESS_GRID, - COMMAND_TAG_CHAR_GRID, - COMMAND_TAG_CP437_GRID, - COMMAND_TAG_GLOBAL_BRIGHTNESS, - COMMAND_TAG_CLEAR, - COMMAND_TAG_HARD_RESET, - COMMAND_TAG_FADE_OUT, - COMMAND_TAG_BITMAP_LEGACY, -}; -#ifndef __cplusplus -typedef uint8_t CommandTag; -#endif // __cplusplus - /** * Specifies the kind of compression to use. Availability depends on features. * * # Examples * - * create command without payload compression * ```rust * # use servicepoint::*; + * // create command without payload compression * # let pixels = Bitmap::max_sized(); * _ = BitmapCommand { * origin: Origin::ZERO, * bitmap: pixels, * compression: CompressionCode::Uncompressed * }; - * ``` * - * create command with payload compressed with lzma and appropriate header flags - * ```rust - * # use servicepoint::*; + * // create command with payload compressed with lzma and appropriate header flags * # let pixels = Bitmap::max_sized(); - * # #[cfg(feature = "compression_lzma")] { * _ = BitmapCommand { * origin: Origin::ZERO, * bitmap: pixels, * compression: CompressionCode::Lzma * }; - * # } * ``` */ enum CompressionCode @@ -213,22 +179,6 @@ enum CompressionCode typedef uint16_t CompressionCode; #endif // __cplusplus -/** - * 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 [`BinaryOperation`] will be applied on the display comparing old and sent bit. - * - * `new_bit = old_bit op sent_bit` - * - * For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels. - * - * The contained [`DisplayBitVec`] is always uncompressed. - */ -typedef struct BitVecCommand BitVecCommand; - /** * A fixed-size 2D grid of booleans. * @@ -246,161 +196,6 @@ typedef struct BitVecCommand BitVecCommand; */ typedef struct Bitmap Bitmap; -/** - * Overwrites a rectangular region of pixels. - * - * Origin coordinates must be divisible by 8. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * # - * let mut bitmap = Bitmap::max_sized(); - * // draw something to the pixels here - * # bitmap.set(2, 5, true); - * - * // create command to send pixels - * let command = BitmapCommand { - * bitmap, - * origin: Origin::ZERO, - * compression: CompressionCode::Uncompressed - * }; - * - * connection.send_command(command).expect("send failed"); - * ``` - */ -typedef struct BitmapCommand BitmapCommand; - -/** - * Legacy command code, gets ignored by the real display. - * - * Might be useful as a noop package. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * // this sends a packet that does nothing - * connection.send_command(BitmapLegacyCommand).unwrap(); - * ``` - */ -typedef struct BitmapLegacyCommand BitmapLegacyCommand; - -/** - * Set the brightness of individual tiles in a rectangular area of the display. - */ -typedef struct BrightnessGridCommand BrightnessGridCommand; - -/** - * 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::*; - * # let connection = FakeConnection; - * let grid = CharGrid::from("Hello,\nWorld!"); - * connection.send_command(CharGridCommand { origin: Origin::ZERO, grid }).expect("send failed"); - * ``` - */ -typedef struct CharGridCommand CharGridCommand; - -/** - * Set all pixels to the off state. Does not affect brightness. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * connection.send_command(ClearCommand).unwrap(); - * ``` - */ -typedef struct ClearCommand ClearCommand; - -/** - * 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::*; - * # let connection = FakeConnection; - * let grid = CharGrid::from("Hello,\nWorld!"); - * # #[cfg(feature = "cp437")] { - * let grid = Cp437Grid::from(&grid); - * connection.send_command(Cp437GridCommand{ origin: Origin::ZERO, grid }).expect("send failed"); - * # } - * ``` - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap(); - * connection.send_command(Cp437GridCommand{ origin: Origin::new(2, 2), grid }).unwrap(); - * ``` - * [CP-437]: https://en.wikipedia.org/wiki/Code_page_437 - */ -typedef struct Cp437GridCommand Cp437GridCommand; - -/** - * This is a type only used by cbindgen to have a type for pointers. - */ -typedef struct DisplayBitVec DisplayBitVec; - -/** - *
Untested
- * - * Slowly decrease brightness until off or something like that? - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * connection.send_command(FadeOutCommand).unwrap(); - * ``` - */ -typedef struct FadeOutCommand FadeOutCommand; - -/** - * Set the brightness of all tiles to the same value. - * - * # Examples - * - * ```rust - * # use servicepoint::*; - * # let connection = FakeConnection; - * let command = GlobalBrightnessCommand { brightness: Brightness::MAX }; - * connection.send_command(command).unwrap(); - * ``` - */ -typedef struct GlobalBrightnessCommand GlobalBrightnessCommand; - -/** - * 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::*; - * # let connection = FakeConnection; - * connection.send_command(HardResetCommand).unwrap(); - * ``` - */ -typedef struct HardResetCommand HardResetCommand; - /** * The raw packet. * @@ -411,9 +206,27 @@ typedef struct HardResetCommand HardResetCommand; typedef struct Packet Packet; /** - * This is a type only used by cbindgen to have a type for pointers. + * A vector of bits * - * See [servicepoint::UdpSocketExt]. + * # Examples + * ```C + * SPBitVec vec = sp_bitvec_new(8); + * sp_bitvec_set(vec, 5, true); + * sp_bitvec_free(vec); + * ``` + */ +typedef struct SPBitVec SPBitVec; + +/** + * This enum contains all commands provided by the library. + * This is useful in case you want one data type for all kinds of commands without using `dyn`. + * + * Please look at the contained structs for documentation per command. + */ +typedef struct TypedCommand TypedCommand; + +/** + * This is a type only used by cbindgen to have a type for pointers. */ typedef struct UdpSocket UdpSocket; @@ -447,11 +260,6 @@ typedef struct ValueGrid_char ValueGrid_char; */ typedef struct ValueGrid_u8 ValueGrid_u8; -/** - * Type alias for documenting the meaning of the u16 in enum values - */ -typedef size_t Offset; - /** * Represents a span of memory (`&mut [u8]` ) as a struct. * @@ -462,19 +270,12 @@ typedef size_t Offset; * - 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. - * - if `start` is NULL or `length` is 0, do not dereference `start`. - * - * # Examples - * - * ```c - * ByteSlice empty = {.start: NULL, .length = 0}; - * ``` */ -typedef struct ByteSlice { +typedef struct { /** * The start address of the memory. */ - uint8_t *start; + uint8_t */*notnull*/ start; /** * The amount of memory in bytes. */ @@ -499,7 +300,7 @@ typedef struct ByteSlice { * }).unwrap() * ``` */ -typedef struct ValueGrid_Brightness BrightnessGrid; +typedef ValueGrid_Brightness BrightnessGrid; /** * A display brightness value, checked for correct value range @@ -545,49 +346,14 @@ typedef uint8_t Brightness; * connection.send_command(command).unwrap() * ``` */ -typedef struct ValueGrid_char CharGrid; +typedef ValueGrid_char CharGrid; /** * A grid containing codepage 437 characters. * * The encoding is currently not enforced. */ -typedef struct ValueGrid_u8 Cp437Grid; - -/** - * Pointer to one of the available command structs. - */ -typedef union CommandUnion { - uint8_t *null; - struct BitmapCommand */*notnull*/ bitmap; - struct BitVecCommand */*notnull*/ bit_vec; - struct BrightnessGridCommand */*notnull*/ brightness_grid; - struct CharGridCommand */*notnull*/ char_grid; - struct Cp437GridCommand */*notnull*/ cp437_grid; - struct GlobalBrightnessCommand */*notnull*/ global_brightness; - struct ClearCommand */*notnull*/ clear; - struct BitmapLegacyCommand */*notnull*/ bitmap_legacy; - struct HardResetCommand */*notnull*/ hard_reset; - struct FadeOutCommand */*notnull*/ fade_out; -} CommandUnion; - -/** - * This struct represents a pointer to one of the possible command structs. - * - * Only ever access `data` with the correct data type as specified by `tag`! - * - * Rust equivalent: [TypedCommand]. - */ -typedef struct GenericCommand { - /** - * Specifies which kind of command struct is contained in `data` - */ - CommandTag tag; - /** - * The pointer to the command struct - */ - union CommandUnion data; -} GenericCommand; +typedef ValueGrid_u8 Cp437Grid; /** * A raw header. @@ -599,7 +365,7 @@ typedef struct GenericCommand { * * The contained values are in platform endian-ness and may need to be converted before sending. */ -typedef struct Header { +typedef struct { /** * The first two bytes specify which command this packet represents. */ @@ -624,271 +390,29 @@ typedef struct Header { -/** - * Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. - */ -#define SP_FRAME_PACING_MS 30 - - - #ifdef __cplusplus extern "C" { #endif // __cplusplus /** - * Calls method [`BitVecCommand::clone`]. - * - *Clones a [`BitVecCommand`] instance. - * - * This function is part of the `bit_vec_command` module. + * Clones a [Bitmap]. */ -struct BitVecCommand */*notnull*/ sp_bit_vec_command_clone(struct BitVecCommand */*notnull*/ instance); +Bitmap */*notnull*/ sp_bitmap_clone(Bitmap */*notnull*/ bitmap); /** - *Deallocates a [`BitVecCommand`] instance. - * - * This function is part of the `bit_vec_command` module. - */ -void sp_bit_vec_command_free(struct BitVecCommand */*notnull*/ instance); - -/** - * Gets a reference to the field `bitvec` of the [`servicepoint::BitVecCommand`]. - * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - * - * This function is part of the `bit_vec_command` module. - */ -DisplayBitVec */*notnull*/ sp_bit_vec_command_get_bitvec_mut(struct BitVecCommand */*notnull*/ instance); - -/** - * Gets the value of field `compression` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -CompressionCode sp_bit_vec_command_get_compression(struct BitVecCommand */*notnull*/ instance); - -/** - * Gets the value of field `offset` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -Offset sp_bit_vec_command_get_offset(struct BitVecCommand */*notnull*/ instance); - -/** - * Gets the value of field `operation` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -BinaryOperation sp_bit_vec_command_get_operation(struct BitVecCommand */*notnull*/ instance); - -/** - * 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 [`BinaryOperation`] will be applied on the display comparing old and sent bit. - * - * `new_bit = old_bit op sent_bit` - * - * For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels. - * - * The contained [`DisplayBitVec`] is always uncompressed. - * - * This function is part of the `bit_vec_command` module. - */ -struct BitVecCommand */*notnull*/ sp_bit_vec_command_new(DisplayBitVec */*notnull*/ bitvec, - size_t offset, - BinaryOperation operation, - CompressionCode compression); - -/** - * Sets the value of field `bitvec` of the [`servicepoint::BitVecCommand`]. - * The provided value is moved into the instance, potentially invalidating previously taken references. - * - * This function is part of the `bit_vec_command` module. - */ -void sp_bit_vec_command_set_bitvec(struct BitVecCommand */*notnull*/ instance, - DisplayBitVec */*notnull*/ value); - -/** - * Sets the value of field `compression` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -void sp_bit_vec_command_set_compression(struct BitVecCommand */*notnull*/ instance, - CompressionCode value); - -/** - * Sets the value of field `offset` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -void sp_bit_vec_command_set_offset(struct BitVecCommand */*notnull*/ instance, - Offset value); - -/** - * Sets the value of field `operation` of the [`servicepoint::BitVecCommand`]. - * - * This function is part of the `bit_vec_command` module. - */ -void sp_bit_vec_command_set_operation(struct BitVecCommand */*notnull*/ instance, - BinaryOperation value); - -/** - *Tries to turn a [`BitVecCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `bit_vec_command` module. - */ -struct Packet *sp_bit_vec_command_try_into_packet(struct BitVecCommand */*notnull*/ instance); - -/** - * Calls method [`Bitmap::clone`]. - * - *Clones a [`Bitmap`] instance. - * - * This function is part of the `bitmap` module. - */ -struct Bitmap */*notnull*/ sp_bitmap_clone(struct Bitmap */*notnull*/ instance); - -/** - * Calls method [`BitmapCommand::clone`]. - * - *Clones a [`BitmapCommand`] instance. - * - * This function is part of the `bitmap_command` module. - */ -struct BitmapCommand */*notnull*/ sp_bitmap_command_clone(struct BitmapCommand */*notnull*/ instance); - -/** - *Deallocates a [`BitmapCommand`] instance. - * - * This function is part of the `bitmap_command` module. - */ -void sp_bitmap_command_free(struct BitmapCommand */*notnull*/ instance); - -/** - * Move the provided [Bitmap] into a new [BitmapCommand], - * leaving other fields as their default values. - * - * Rust equivalent: `BitmapCommand::from(bitmap)` - * - * This function is part of the `bitmap_command` module. - */ -struct BitmapCommand */*notnull*/ sp_bitmap_command_from_bitmap(struct Bitmap */*notnull*/ bitmap); - -/** - * Gets a reference to the field `bitmap` of the [`servicepoint::BitmapCommand`]. - * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - * - * This function is part of the `bitmap_command` module. - */ -struct Bitmap */*notnull*/ sp_bitmap_command_get_bitmap_mut(struct BitmapCommand */*notnull*/ instance); - -/** - * Gets the value of field `compression` of the [`servicepoint::BitmapCommand`]. - * - * This function is part of the `bitmap_command` module. - */ -CompressionCode sp_bitmap_command_get_compression(struct BitmapCommand */*notnull*/ instance); - -/** - * Reads the origin field of the [`BitmapCommand`]. - * - * This function is part of the `bitmap_command` module. - */ -void sp_bitmap_command_get_origin(struct BitmapCommand */*notnull*/ command, - size_t */*notnull*/ origin_x, - size_t */*notnull*/ origin_y); - -/** - * Sets a window of pixels to the specified values. - * - * The passed [Bitmap] gets consumed. - * - * Returns: a new [BitmapCommand] instance. - * - * This function is part of the `bitmap_command` module. - */ -struct BitmapCommand */*notnull*/ sp_bitmap_command_new(struct Bitmap */*notnull*/ bitmap, - size_t origin_x, - size_t origin_y, - CompressionCode compression); - -/** - * Sets the value of field `bitmap` of the [`servicepoint::BitmapCommand`]. - * The provided value is moved into the instance, potentially invalidating previously taken references. - * - * This function is part of the `bitmap_command` module. - */ -void sp_bitmap_command_set_bitmap(struct BitmapCommand */*notnull*/ instance, - struct Bitmap */*notnull*/ value); - -/** - * Sets the value of field `compression` of the [`servicepoint::BitmapCommand`]. - * - * This function is part of the `bitmap_command` module. - */ -void sp_bitmap_command_set_compression(struct BitmapCommand */*notnull*/ instance, - CompressionCode value); - -/** - * Overwrites the origin field of the [`BitmapCommand`]. - * - * This function is part of the `bitmap_command` module. - */ -void sp_bitmap_command_set_origin(struct BitmapCommand */*notnull*/ command, - size_t origin_x, - size_t origin_y); - -/** - *Tries to turn a [`BitmapCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `bitmap_command` module. - */ -struct Packet *sp_bitmap_command_try_into_packet(struct BitmapCommand */*notnull*/ instance); - -/** - * Calls method [`Bitmap::data_ref_mut`]. - * - * Gets an unsafe reference to the data of the [Bitmap] instance. - * - * The returned memory is valid for the lifetime of the bitmap. - * - * This function is part of the `bitmap` module. - */ -struct ByteSlice sp_bitmap_data_ref_mut(struct Bitmap */*notnull*/ instance); - -/** - * Calls method [`Bitmap::fill`]. - * - * Sets the state of all cells in the grid. + * Sets the state of all pixels in the [Bitmap]. * * # Arguments * - * - `value`: the value to set all cells to - * - * This function is part of the `bitmap` module. + * - `bitmap`: instance to write to + * - `value`: the value to set all pixels to */ -void sp_bitmap_fill(struct Bitmap */*notnull*/ instance, bool value); +void sp_bitmap_fill(Bitmap */*notnull*/ bitmap, bool value); /** - *Deallocates a [`Bitmap`] instance. - * - * This function is part of the `bitmap` module. + * Deallocates a [Bitmap]. */ -void sp_bitmap_free(struct Bitmap */*notnull*/ instance); +void sp_bitmap_free(Bitmap */*notnull*/ bitmap); /** * Tries to convert the BitVec to a Bitmap. @@ -896,44 +420,48 @@ void sp_bitmap_free(struct Bitmap */*notnull*/ instance); * The provided BitVec gets consumed. * * Returns NULL in case of error. - * - * This function is part of the `bitmap` module. */ -struct Bitmap *sp_bitmap_from_bitvec(size_t width, - DisplayBitVec */*notnull*/ bitvec); +Bitmap *sp_bitmap_from_bitvec(size_t width, SPBitVec */*notnull*/ bitvec); /** - * Calls method [`Bitmap::get`]. - * - * Gets the current value at the specified position. + * Gets the current value at the specified position in the [Bitmap]. * * # Arguments * + * - `bitmap`: instance to read from * - `x` and `y`: position of the cell to read * * # Panics * * - when accessing `x` or `y` out of bounds - * - * This function is part of the `bitmap` module. */ -bool sp_bitmap_get(struct Bitmap */*notnull*/ instance, size_t x, size_t y); +bool sp_bitmap_get(Bitmap */*notnull*/ bitmap, size_t x, size_t y); /** - * Calls method [`Bitmap::height`]. + * Gets the height in pixels of the [Bitmap] instance. * - * Gets the height. + * # Arguments * - * This function is part of the `bitmap` module. + * - `bitmap`: instance to read from */ -size_t sp_bitmap_height(struct Bitmap */*notnull*/ instance); +size_t sp_bitmap_height(Bitmap */*notnull*/ bitmap); /** - * Consumes the Bitmap and returns the contained BitVec. - * - * This function is part of the `bitmap` module. + * Consumes the Bitmap and returns the contained BitVec */ -DisplayBitVec */*notnull*/ sp_bitmap_into_bitvec(struct Bitmap */*notnull*/ bitmap); +SPBitVec */*notnull*/ sp_bitmap_into_bitvec(Bitmap */*notnull*/ bitmap); + +/** + * Creates a [BitmapCommand] and immediately turns that into a [Packet]. + * + * The provided [Bitmap] gets consumed. + * + * Returns NULL in case of an error. + */ +Packet *sp_bitmap_into_packet(Bitmap */*notnull*/ bitmap, + size_t x, + size_t y, + CompressionCode compression); /** * Loads a [Bitmap] with the specified dimensions from the provided data. @@ -944,12 +472,10 @@ DisplayBitVec */*notnull*/ sp_bitmap_into_bitvec(struct Bitmap */*notnull*/ bitm * - `height`: size in pixels in y-direction * * returns: [Bitmap] that contains a copy of the provided data, or NULL in case of an error. - * - * This function is part of the `bitmap` module. */ -struct Bitmap *sp_bitmap_load(size_t width, - size_t height, - struct ByteSlice data); +Bitmap *sp_bitmap_load(size_t width, + size_t height, + ByteSlice data); /** * Creates a new [Bitmap] with the specified dimensions. @@ -975,215 +501,230 @@ struct Bitmap *sp_bitmap_load(size_t width, * sp_bitmap_set(grid, 0, 0, false); * sp_bitmap_free(grid); * ``` - * - * This function is part of the `bitmap` module. */ -struct Bitmap *sp_bitmap_new(size_t width, size_t height); +Bitmap *sp_bitmap_new(size_t width, size_t height); /** * Creates a new [Bitmap] with a size matching the screen. * * returns: [Bitmap] initialized to all pixels off. - * - * This function is part of the `bitmap` module. */ -struct Bitmap */*notnull*/ sp_bitmap_new_max_sized(void); +Bitmap */*notnull*/ sp_bitmap_new_max_sized(void); /** - * Calls method [`Bitmap::set`]. - * - * Sets the value of the specified position. + * Sets the value of the specified position in the [Bitmap]. * * # Arguments * + * - `bitmap`: instance to write to * - `x` and `y`: position of the cell * - `value`: the value to write to the cell * * # Panics * * - when accessing `x` or `y` out of bounds - * - * This function is part of the `bitmap` module. */ -void sp_bitmap_set(struct Bitmap */*notnull*/ instance, - size_t x, - size_t y, - bool value); +void sp_bitmap_set(Bitmap */*notnull*/ bitmap, size_t x, size_t y, bool value); /** - * Creates a [BitmapCommand] and immediately turns that into a [Packet]. + * Gets an unsafe reference to the data of the [Bitmap] instance. * - * The provided [Bitmap] gets consumed. - * - * Returns NULL in case of an error. - * - * This function is part of the `bitmap` module. + * The returned memory is valid for the lifetime of the bitmap. */ -struct Packet *sp_bitmap_try_into_packet(struct Bitmap */*notnull*/ bitmap, - size_t x, - size_t y, - CompressionCode compression); +ByteSlice sp_bitmap_unsafe_data_ref(Bitmap */*notnull*/ bitmap); /** - * Calls method [`Bitmap::width`]. - * - * Gets the width. - * - * This function is part of the `bitmap` module. - */ -size_t sp_bitmap_width(struct Bitmap */*notnull*/ instance); - -/** - * Calls method [`BrightnessGrid::clone`]. - * - *Clones a [`BrightnessGrid`] instance. - * - * This function is part of the `brightness_grid` module. - */ -BrightnessGrid */*notnull*/ sp_brightness_grid_clone(BrightnessGrid */*notnull*/ instance); - -/** - * Calls method [`BrightnessGridCommand::clone`]. - * - *Clones a [`BrightnessGridCommand`] instance. - * - * This function is part of the `brightness_grid_command` module. - */ -struct BrightnessGridCommand */*notnull*/ sp_brightness_grid_command_clone(struct BrightnessGridCommand */*notnull*/ instance); - -/** - *Deallocates a [`BrightnessGridCommand`] instance. - * - * This function is part of the `brightness_grid_command` module. - */ -void sp_brightness_grid_command_free(struct BrightnessGridCommand */*notnull*/ instance); - -/** - * Moves the provided [BrightnessGrid] into a new [BrightnessGridCommand], - * leaving other fields as their default values. - * - * This function is part of the `brightness_grid_command` module. - */ -struct BrightnessGridCommand */*notnull*/ sp_brightness_grid_command_from_grid(BrightnessGrid */*notnull*/ grid); - -/** - * Gets a reference to the field `grid` of the [`servicepoint::BrightnessGridCommand`]. - * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - * - * This function is part of the `brightness_grid_command` module. - */ -BrightnessGrid */*notnull*/ sp_brightness_grid_command_get_grid_mut(struct BrightnessGridCommand */*notnull*/ instance); - -/** - * Reads the origin field of the [`BrightnessGridCommand`]. - * - * This function is part of the `brightness_grid_command` module. - */ -void sp_brightness_grid_command_get_origin(struct BrightnessGridCommand */*notnull*/ command, - size_t */*notnull*/ origin_x, - size_t */*notnull*/ origin_y); - -/** - * Set the brightness of individual tiles in a rectangular area of the display. - * - * The passed [BrightnessGrid] gets consumed. - * - * Returns: a new [BrightnessGridCommand] instance. - * - * This function is part of the `brightness_grid_command` module. - */ -struct BrightnessGridCommand */*notnull*/ sp_brightness_grid_command_new(BrightnessGrid */*notnull*/ grid, - size_t origin_x, - size_t origin_y); - -/** - * Sets the value of field `grid` of the [`servicepoint::BrightnessGridCommand`]. - * The provided value is moved into the instance, potentially invalidating previously taken references. - * - * This function is part of the `brightness_grid_command` module. - */ -void sp_brightness_grid_command_set_grid(struct BrightnessGridCommand */*notnull*/ instance, - BrightnessGrid */*notnull*/ value); - -/** - * Overwrites the origin field of the [`BrightnessGridCommand`]. - * - * This function is part of the `brightness_grid_command` module. - */ -void sp_brightness_grid_command_set_origin(struct BrightnessGridCommand */*notnull*/ command, - size_t origin_x, - size_t origin_y); - -/** - *Tries to turn a [`BrightnessGridCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `brightness_grid_command` module. - */ -struct Packet *sp_brightness_grid_command_try_into_packet(struct BrightnessGridCommand */*notnull*/ instance); - -/** - * Gets an unsafe reference to the data of the instance. - * - * The returned memory is valid for the lifetime of the grid. - * - * This function is part of the `brightness_grid` module. - */ -struct ByteSlice sp_brightness_grid_data_ref_mut(BrightnessGrid */*notnull*/ instance); - -/** - * Calls method [`BrightnessGrid::fill`]. - * - * Sets the state of all cells in the grid. + * Gets the width in pixels of the [Bitmap] instance. * * # Arguments * - * - `value`: the value to set all cells to + * - `bitmap`: instance to read from * - * This function is part of the `brightness_grid` module. + * # Panics + * + * - when `bitmap` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `bitmap` points to a valid [Bitmap] */ -void sp_brightness_grid_fill(BrightnessGrid */*notnull*/ instance, +size_t sp_bitmap_width(Bitmap */*notnull*/ bitmap); + +/** + * Clones a [SPBitVec]. + */ +SPBitVec */*notnull*/ sp_bitvec_clone(SPBitVec */*notnull*/ 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 + */ +void sp_bitvec_fill(SPBitVec */*notnull*/ bit_vec, bool value); + +/** + * Deallocates a [SPBitVec]. + */ +void sp_bitvec_free(SPBitVec */*notnull*/ 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 accessing `index` out of bounds + */ +bool sp_bitvec_get(SPBitVec */*notnull*/ bit_vec, size_t index); + +/** + * Creates a [BitVecCommand] and immediately turns that into a [Packet]. + * + * The provided [SPBitVec] gets consumed. + * + * Returns NULL in case of an error. + */ +Packet *sp_bitvec_into_packet(SPBitVec */*notnull*/ bitvec, + size_t offset, + BinaryOperation operation, + CompressionCode compression); + +/** + * Returns true if length is 0. + * + * # Arguments + * + * - `bit_vec`: instance to write to + */ +bool sp_bitvec_is_empty(SPBitVec */*notnull*/ bit_vec); + +/** + * Gets the length of the [SPBitVec] in bits. + * + * # Arguments + * + * - `bit_vec`: instance to write to + */ +size_t sp_bitvec_len(SPBitVec */*notnull*/ bit_vec); + +/** + * Interpret the data as a series of bits and load then into a new [SPBitVec] instance. + * + * returns: [SPBitVec] instance containing data. + */ +SPBitVec */*notnull*/ sp_bitvec_load(ByteSlice data); + +/** + * Creates a new [SPBitVec] instance. + * + * # Arguments + * + * - `size`: size in bits. + * + * returns: [SPBitVec] with all bits set to false. + * + * # Panics + * + * - when `size` is not divisible by 8. + */ +SPBitVec */*notnull*/ 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 accessing `index` out of bounds + */ +void sp_bitvec_set(SPBitVec */*notnull*/ bit_vec, size_t index, bool value); + +/** + * Gets an unsafe reference to the data of the [SPBitVec] instance. + * + * The returned memory is valid for the lifetime of the bitvec. + * + * # Arguments + * + * - `bit_vec`: instance to write to + */ +ByteSlice sp_bitvec_unsafe_data_ref(SPBitVec */*notnull*/ bit_vec); + +/** + * Clones a [BrightnessGrid]. + */ +BrightnessGrid */*notnull*/ sp_brightness_grid_clone(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Sets the value of all cells in the [BrightnessGrid]. + * + * # Arguments + * + * - `brightness_grid`: instance to write to + * - `value`: the value to set all cells to + */ +void sp_brightness_grid_fill(BrightnessGrid */*notnull*/ brightness_grid, Brightness value); /** - *Deallocates a [`BrightnessGrid`] instance. - * - * This function is part of the `brightness_grid` module. + * Deallocates a [BrightnessGrid]. */ -void sp_brightness_grid_free(BrightnessGrid */*notnull*/ instance); +void sp_brightness_grid_free(BrightnessGrid */*notnull*/ brightness_grid); /** - * Calls method [`BrightnessGrid::get`]. - * * 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 accessing `x` or `y` out of bounds - * - * This function is part of the `brightness_grid` module. + * - When accessing `x` or `y` out of bounds. */ -Brightness sp_brightness_grid_get(BrightnessGrid */*notnull*/ instance, +Brightness sp_brightness_grid_get(BrightnessGrid */*notnull*/ brightness_grid, size_t x, size_t y); /** - * Calls method [`BrightnessGrid::height`]. + * Gets the height of the [BrightnessGrid] instance. * - * Gets the height. + * # Arguments * - * This function is part of the `brightness_grid` module. + * - `brightness_grid`: instance to read from + * + * returns: height */ -size_t sp_brightness_grid_height(BrightnessGrid */*notnull*/ instance); +size_t sp_brightness_grid_height(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Creates a [BrightnessGridCommand] and immediately turns that into a [Packet]. + * + * The provided [BrightnessGrid] gets consumed. + * + * Returns NULL in case of an error. + */ +Packet *sp_brightness_grid_into_packet(BrightnessGrid */*notnull*/ grid, + size_t x, + size_t y); /** * Loads a [BrightnessGrid] with the specified dimensions from the provided data. @@ -1191,12 +732,10 @@ size_t sp_brightness_grid_height(BrightnessGrid */*notnull*/ instance); * Any out of range values will be set to [Brightness::MAX] or [Brightness::MIN]. * * returns: new [BrightnessGrid] instance, or NULL in case of an error. - * - * This function is part of the `brightness_grid` module. */ BrightnessGrid *sp_brightness_grid_load(size_t width, size_t height, - struct ByteSlice data); + ByteSlice data); /** * Creates a new [BrightnessGrid] with the specified dimensions. @@ -1205,210 +744,124 @@ BrightnessGrid *sp_brightness_grid_load(size_t width, * * # Examples * ```C - * UdpSocket *connection = sp_udp_open("127.0.0.1:2342"); + * UdpConnection connection = sp_udp_open("127.0.0.1:2342"); * if (connection == NULL) * return 1; * - * BrightnessGrid *grid = sp_brightness_grid_new(2, 2); + * BrightnessGrid grid = sp_brightness_grid_new(2, 2); * sp_brightness_grid_set(grid, 0, 0, 0); * sp_brightness_grid_set(grid, 1, 1, 10); * - * TypedCommand *command = sp_command_char_brightness(grid); + * TypedCommand command = sp_command_char_brightness(grid); * sp_udp_free(connection); * ``` - * - * This function is part of the `brightness_grid` module. */ BrightnessGrid */*notnull*/ sp_brightness_grid_new(size_t width, size_t height); /** - * Calls method [`BrightnessGrid::set`]. - * - * Sets the value of the specified position. + * Sets the value of the specified position in the [BrightnessGrid]. * * # 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 accessing `x` or `y` out of bounds - * - * This function is part of the `brightness_grid` module. + * - When accessing `x` or `y` out of bounds. */ -void sp_brightness_grid_set(BrightnessGrid */*notnull*/ instance, +void sp_brightness_grid_set(BrightnessGrid */*notnull*/ brightness_grid, size_t x, size_t y, Brightness value); /** - * Creates a [BrightnessGridCommand] and immediately turns that into a [Packet]. + * Gets an unsafe reference to the data of the [BrightnessGrid] instance. * - * The provided [BrightnessGrid] gets consumed. - * - * Returns NULL in case of an error. - * - * This function is part of the `brightness_grid` module. - */ -struct Packet *sp_brightness_grid_try_into_packet(BrightnessGrid */*notnull*/ grid, - size_t x, - size_t y); - -/** - * Calls method [`BrightnessGrid::width`]. - * - * Gets the width. - * - * This function is part of the `brightness_grid` module. - */ -size_t sp_brightness_grid_width(BrightnessGrid */*notnull*/ instance); - -/** - * Calls method [`CharGrid::clone`]. - * - *Clones a [`CharGrid`] instance. - * - * This function is part of the `char_grid` module. - */ -CharGrid */*notnull*/ sp_char_grid_clone(CharGrid */*notnull*/ instance); - -/** - * Calls method [`CharGridCommand::clone`]. - * - *Clones a [`CharGridCommand`] instance. - * - * This function is part of the `char_grid_command` module. - */ -struct CharGridCommand */*notnull*/ sp_char_grid_command_clone(struct CharGridCommand */*notnull*/ instance); - -/** - *Deallocates a [`CharGridCommand`] instance. - * - * This function is part of the `char_grid_command` module. - */ -void sp_char_grid_command_free(struct CharGridCommand */*notnull*/ instance); - -/** - * Moves the provided [CharGrid] into a new [CharGridCommand], - * leaving other fields as their default values. - * - * This function is part of the `char_grid_command` module. - */ -struct CharGridCommand */*notnull*/ sp_char_grid_command_from_grid(CharGrid */*notnull*/ grid); - -/** - * Gets a reference to the field `grid` of the [`servicepoint::CharGridCommand`]. - * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - * - * This function is part of the `char_grid_command` module. - */ -CharGrid */*notnull*/ sp_char_grid_command_get_grid_mut(struct CharGridCommand */*notnull*/ instance); - -/** - * Reads the origin field of the [`CharGridCommand`]. - * - * This function is part of the `char_grid_command` module. - */ -void sp_char_grid_command_get_origin(struct CharGridCommand */*notnull*/ command, - size_t */*notnull*/ origin_x, - size_t */*notnull*/ origin_y); - -/** - * Show UTF-8 encoded text on the screen. - * - * The passed [CharGrid] gets consumed. - * - * Returns: a new [CharGridCommand] instance. - * - * This function is part of the `char_grid_command` module. - */ -struct CharGridCommand */*notnull*/ sp_char_grid_command_new(CharGrid */*notnull*/ grid, - size_t origin_x, - size_t origin_y); - -/** - * Sets the value of field `grid` of the [`servicepoint::CharGridCommand`]. - * The provided value is moved into the instance, potentially invalidating previously taken references. - * - * This function is part of the `char_grid_command` module. - */ -void sp_char_grid_command_set_grid(struct CharGridCommand */*notnull*/ instance, - CharGrid */*notnull*/ value); - -/** - * Overwrites the origin field of the [`CharGridCommand`]. - * - * This function is part of the `char_grid_command` module. - */ -void sp_char_grid_command_set_origin(struct CharGridCommand */*notnull*/ command, - size_t origin_x, - size_t origin_y); - -/** - *Tries to turn a [`CharGridCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `char_grid_command` module. - */ -struct Packet *sp_char_grid_command_try_into_packet(struct CharGridCommand */*notnull*/ instance); - -/** - * Sets the value of all cells in the grid. + * The returned memory is valid for the lifetime of the brightness grid. * * # Arguments * - * - `value`: the value to set all cells to - * - when providing values that cannot be converted to Rust's `char`. + * - `brightness_grid`: instance to read from * - * This function is part of the `char_grid` module. + * returns: slice of bytes underlying the `brightness_grid`. */ -void sp_char_grid_fill(CharGrid */*notnull*/ instance, uint32_t value); +ByteSlice sp_brightness_grid_unsafe_data_ref(BrightnessGrid */*notnull*/ brightness_grid); /** - *Deallocates a [`CharGrid`] instance. + * Gets the width of the [BrightnessGrid] instance. * - * This function is part of the `char_grid` module. + * # Arguments + * + * - `brightness_grid`: instance to read from + * + * returns: width */ -void sp_char_grid_free(CharGrid */*notnull*/ instance); +size_t sp_brightness_grid_width(BrightnessGrid */*notnull*/ brightness_grid); + +/** + * Clones a [CharGrid]. + */ +CharGrid */*notnull*/ sp_char_grid_clone(CharGrid */*notnull*/ char_grid); + +/** + * Sets the value of all cells in the [CharGrid]. + * + * # Arguments + * + * - `char_grid`: instance to write to + * - `value`: the value to set all cells to + */ +void sp_char_grid_fill(CharGrid */*notnull*/ char_grid, uint32_t value); + +/** + * Deallocates a [CharGrid]. + */ +void sp_char_grid_free(CharGrid */*notnull*/ char_grid); /** * Returns 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 accessing `x` or `y` out of bounds - * - * This function is part of the `char_grid` module. */ -uint32_t sp_char_grid_get(CharGrid */*notnull*/ instance, size_t x, size_t y); +uint32_t sp_char_grid_get(CharGrid */*notnull*/ char_grid, size_t x, size_t y); /** - * Calls method [`CharGrid::height`]. + * Gets the height of the [CharGrid] instance. * - * Gets the height. + * # Arguments * - * This function is part of the `char_grid` module. + * - `char_grid`: instance to read from */ -size_t sp_char_grid_height(CharGrid */*notnull*/ instance); +size_t sp_char_grid_height(CharGrid */*notnull*/ char_grid); + +/** + * Creates a [CharGridCommand] and immediately turns that into a [Packet]. + * + * The provided [CharGrid] gets consumed. + * + * Returns NULL in case of an error. + */ +Packet *sp_char_grid_into_packet(CharGrid */*notnull*/ grid, + size_t x, + size_t y); /** * Loads a [CharGrid] with the specified dimensions from the provided data. * * returns: new CharGrid or NULL in case of an error - * - * This function is part of the `char_grid` module. */ -CharGrid *sp_char_grid_load(size_t width, size_t height, struct ByteSlice data); +CharGrid *sp_char_grid_load(size_t width, size_t height, ByteSlice data); /** * Creates a new [CharGrid] with the specified dimensions. @@ -1423,16 +876,15 @@ CharGrid *sp_char_grid_load(size_t width, size_t height, struct ByteSlice data); * sp_char_grid_set(grid, 0, 0, '!'); * sp_char_grid_free(grid); * ``` - * - * This function is part of the `char_grid` module. */ CharGrid */*notnull*/ sp_char_grid_new(size_t width, size_t height); /** - * Sets the value of the specified position in the grid. + * Sets the value of the specified position in the [CharGrid]. * * # Arguments * + * - `char_grid`: instance to write to * - `x` and `y`: position of the cell * - `value`: the value to write to the cell * @@ -1441,264 +893,195 @@ CharGrid */*notnull*/ sp_char_grid_new(size_t width, size_t height); * # Panics * * - when accessing `x` or `y` out of bounds - * - when providing values that cannot be converted to Rust's `char`. - * - * This function is part of the `char_grid` module. */ -void sp_char_grid_set(CharGrid */*notnull*/ instance, +void sp_char_grid_set(CharGrid */*notnull*/ char_grid, size_t x, size_t y, uint32_t value); /** - * Creates a [CharGridCommand] and immediately turns that into a [Packet]. + * Gets the width of the [CharGrid] instance. * - * The provided [CharGrid] gets consumed. + * # Arguments * - * Returns NULL in case of an error. - * - * This function is part of the `char_grid` module. + * - `char_grid`: instance to read from */ -struct Packet *sp_char_grid_try_into_packet(CharGrid */*notnull*/ grid, - size_t x, - size_t y); +size_t sp_char_grid_width(CharGrid */*notnull*/ char_grid); /** - * Calls method [`CharGrid::width`]. + * Sets a window of pixels to the specified values. * - * Gets the width. + * The passed [Bitmap] gets consumed. * - * This function is part of the `char_grid` module. + * Returns: a new [servicepoint::BitmapCommand] instance. */ -size_t sp_char_grid_width(CharGrid */*notnull*/ instance); +TypedCommand *sp_command_bitmap(size_t x, + size_t y, + Bitmap */*notnull*/ bitmap, + CompressionCode compression); /** - * Calls method [`ClearCommand::clone`]. + * Set pixel data starting at the pixel offset on screen. * - *Clones a [`ClearCommand`] instance. + * 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. * - * This function is part of the `clear_command` module. + * The [`BinaryOperation`] will be applied on the display comparing old and sent bit. + * + * `new_bit = old_bit op sent_bit` + * + * For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels. + * + * The contained [`BitVecU8Msb0`] is always uncompressed. */ -struct ClearCommand */*notnull*/ sp_clear_command_clone(struct ClearCommand */*notnull*/ instance); +TypedCommand *sp_command_bitvec(size_t offset, + SPBitVec */*notnull*/ bit_vec, + CompressionCode compression, + BinaryOperation operation); /** - *Deallocates a [`ClearCommand`] instance. + * Set the brightness of individual tiles in a rectangular area of the display. * - * This function is part of the `clear_command` module. + * The passed [BrightnessGrid] gets consumed. + * + * Returns: a new [servicepoint::Command::CharBrightness] instance. */ -void sp_clear_command_free(struct ClearCommand */*notnull*/ instance); +TypedCommand */*notnull*/ sp_command_brightness_grid(size_t x, + size_t y, + BrightnessGrid */*notnull*/ grid); + +/** + * Show UTF-8 encoded text on the screen. + * + * The passed [CharGrid] gets consumed. + * + * Returns: a new [servicepoint::CharGridCommand] instance. + */ +TypedCommand */*notnull*/ sp_command_char_grid(size_t x, + size_t y, + CharGrid */*notnull*/ grid); /** * Set all pixels to the off state. * * Does not affect brightness. * - * Returns: a new [`ClearCommand`] instance. + * Returns: a new [servicepoint::Command::Clear] instance. * - * This function is part of the `clear_command` module. + * # Examples + * + * ```C + * sp_udp_send_command(connection, sp_command_clear()); + * ``` */ -struct ClearCommand */*notnull*/ sp_clear_command_new(void); +TypedCommand */*notnull*/ sp_command_clear(void); /** - *Tries to turn a [`ClearCommand`] into a [Packet]. + * Clones a [TypedCommand] instance. * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `clear_command` module. + * returns: new [TypedCommand] instance. */ -struct Packet *sp_clear_command_try_into_packet(struct ClearCommand */*notnull*/ instance); +TypedCommand */*notnull*/ sp_command_clone(TypedCommand */*notnull*/ command); /** - * Calls method [`Cp437Grid::clone`]. + * Show codepage 437 encoded text on the screen. * - *Clones a [`Cp437Grid`] instance. + * The passed [Cp437Grid] gets consumed. * - * This function is part of the `cp437_grid` module. + * Returns: a new [servicepoint::Cp437GridCommand] instance. */ -Cp437Grid */*notnull*/ sp_cp437_grid_clone(Cp437Grid */*notnull*/ instance); +TypedCommand */*notnull*/ sp_command_cp437_grid(size_t x, + size_t y, + Cp437Grid */*notnull*/ grid); /** - * Calls method [`Cp437GridCommand::clone`]. + * A yet-to-be-tested command. * - *Clones a [`Cp437GridCommand`] instance. - * - * This function is part of the `cp437_grid_command` module. + * Returns: a new [servicepoint::Command::FadeOut] instance. */ -struct Cp437GridCommand */*notnull*/ sp_cp437_grid_command_clone(struct Cp437GridCommand */*notnull*/ instance); +TypedCommand */*notnull*/ sp_command_fade_out(void); /** - *Deallocates a [`Cp437GridCommand`] instance. + * Deallocates a [TypedCommand]. * - * This function is part of the `cp437_grid_command` module. + * # Examples + * + * ```C + * TypedCommand c = sp_command_clear(); + * sp_command_free(c); + * ``` */ -void sp_cp437_grid_command_free(struct Cp437GridCommand */*notnull*/ instance); +void sp_command_free(TypedCommand */*notnull*/ command); /** - * Moves the provided [Cp437Grid] into a new [Cp437GridCommand], - * leaving other fields as their default values. + * Set the brightness of all tiles to the same value. * - * This function is part of the `cp437_grid_command` module. + * Returns: a new [servicepoint::Command::Brightness] instance. */ -struct Cp437GridCommand */*notnull*/ sp_cp437_grid_command_from_grid(Cp437Grid */*notnull*/ grid); +TypedCommand */*notnull*/ sp_command_global_brightness(Brightness brightness); /** - * Gets a reference to the field `grid` of the [`servicepoint::Cp437GridCommand`]. + * Kills the udp daemon on the display, which usually results in a restart. * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. + * Please do not send this in your normal program flow. * - * This function is part of the `cp437_grid_command` module. + * Returns: a new [servicepoint::Command::HardReset] instance. */ -Cp437Grid */*notnull*/ sp_cp437_grid_command_get_grid_mut(struct Cp437GridCommand */*notnull*/ instance); +TypedCommand */*notnull*/ sp_command_hard_reset(void); /** - * Reads the origin field of the [`Cp437GridCommand`]. + * Tries to turn a [Packet] into a [TypedCommand]. * - * This function is part of the `cp437_grid_command` module. + * The packet is deallocated in the process. + * + * Returns: pointer to new [TypedCommand] instance or NULL if parsing failed. */ -void sp_cp437_grid_command_get_origin(struct Cp437GridCommand */*notnull*/ command, - size_t */*notnull*/ origin_x, - size_t */*notnull*/ origin_y); +TypedCommand *sp_command_try_from_packet(Packet */*notnull*/ packet); /** - * Show text on the screen. - * - * The text is sent in the form of a 2D grid of [CP-437] encoded characters. - * - * The origin is relative to the top-left of the display. - * - * This function is part of the `cp437_grid_command` module. + * Clones a [Cp437Grid]. */ -struct Cp437GridCommand */*notnull*/ sp_cp437_grid_command_new(Cp437Grid */*notnull*/ grid, - size_t origin_x, - size_t origin_y); +Cp437Grid */*notnull*/ sp_cp437_grid_clone(Cp437Grid */*notnull*/ cp437_grid); /** - * Sets the value of field `grid` of the [`servicepoint::Cp437GridCommand`]. - * The provided value is moved into the instance, potentially invalidating previously taken references. - * - * This function is part of the `cp437_grid_command` module. - */ -void sp_cp437_grid_command_set_grid(struct Cp437GridCommand */*notnull*/ instance, - Cp437Grid */*notnull*/ value); - -/** - * Overwrites the origin field of the [`Cp437GridCommand`]. - * - * This function is part of the `cp437_grid_command` module. - */ -void sp_cp437_grid_command_set_origin(struct Cp437GridCommand */*notnull*/ command, - size_t origin_x, - size_t origin_y); - -/** - *Tries to turn a [`Cp437GridCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `cp437_grid_command` module. - */ -struct Packet *sp_cp437_grid_command_try_into_packet(struct Cp437GridCommand */*notnull*/ instance); - -/** - * Calls method [`Cp437Grid::data_ref_mut`]. - * - * Gets an unsafe reference to the data of the grid. - * - * The returned memory is valid for the lifetime of the instance. - * - * This function is part of the `cp437_grid` module. - */ -struct ByteSlice sp_cp437_grid_data_ref_mut(Cp437Grid */*notnull*/ instance); - -/** - * Calls method [`Cp437Grid::fill`]. - * - * Sets the state of all cells in the grid. + * Sets the value of all cells in the [Cp437Grid]. * * # Arguments * + * - `cp437_grid`: instance to write to * - `value`: the value to set all cells to - * - * This function is part of the `cp437_grid` module. */ -void sp_cp437_grid_fill(Cp437Grid */*notnull*/ instance, uint8_t value); +void sp_cp437_grid_fill(Cp437Grid */*notnull*/ cp437_grid, uint8_t value); /** - *Deallocates a [`Cp437Grid`] instance. - * - * This function is part of the `cp437_grid` module. + * Deallocates a [Cp437Grid]. */ -void sp_cp437_grid_free(Cp437Grid */*notnull*/ instance); +void sp_cp437_grid_free(Cp437Grid */*notnull*/ cp437_grid); /** - * Calls method [`Cp437Grid::get`]. - * * 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 accessing `x` or `y` out of bounds - * - * This function is part of the `cp437_grid` module. */ -uint8_t sp_cp437_grid_get(Cp437Grid */*notnull*/ instance, size_t x, size_t y); +uint8_t sp_cp437_grid_get(Cp437Grid */*notnull*/ cp437_grid, + size_t x, + size_t y); /** - * Calls method [`Cp437Grid::height`]. - * - * Gets the height. - * - * This function is part of the `cp437_grid` module. - */ -size_t sp_cp437_grid_height(Cp437Grid */*notnull*/ instance); - -/** - * Loads a [Cp437Grid] with the specified dimensions from the provided data. - * - * This function is part of the `cp437_grid` module. - */ -Cp437Grid *sp_cp437_grid_load(size_t width, - size_t height, - struct ByteSlice data); - -/** - * Creates a new [Cp437Grid] with the specified dimensions. - * - * returns: [Cp437Grid] initialized to 0. - * - * This function is part of the `cp437_grid` module. - */ -Cp437Grid */*notnull*/ sp_cp437_grid_new(size_t width, size_t height); - -/** - * Calls method [`Cp437Grid::set`]. - * - * Sets the value of the specified position. + * Gets the height of the [Cp437Grid] instance. * * # 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 - * - * This function is part of the `cp437_grid` module. + * - `cp437_grid`: instance to read from */ -void sp_cp437_grid_set(Cp437Grid */*notnull*/ instance, - size_t x, - size_t y, - uint8_t value); +size_t sp_cp437_grid_height(Cp437Grid */*notnull*/ cp437_grid); /** * Creates a [Cp437GridCommand] and immediately turns that into a [Packet]. @@ -1706,381 +1089,98 @@ void sp_cp437_grid_set(Cp437Grid */*notnull*/ instance, * The provided [Cp437Grid] gets consumed. * * Returns NULL in case of an error. - * - * This function is part of the `cp437_grid` module. */ -struct Packet *sp_cp437_grid_try_into_packet(Cp437Grid */*notnull*/ grid, - size_t x, - size_t y); +Packet *sp_cp437_grid_into_packet(Cp437Grid */*notnull*/ grid, + size_t x, + size_t y); /** - * Calls method [`Cp437Grid::width`]. - * - * Gets the width. - * - * This function is part of the `cp437_grid` module. + * Loads a [Cp437Grid] with the specified dimensions from the provided data. */ -size_t sp_cp437_grid_width(Cp437Grid */*notnull*/ instance); +Cp437Grid *sp_cp437_grid_load(size_t width, size_t height, ByteSlice data); /** - * Calls method [`DisplayBitVec::as_raw_mut_slice`]. + * Creates a new [Cp437Grid] with the specified dimensions. * - * Gets an unsafe reference to the data of the [DisplayBitVec] instance. - * - * The returned memory is valid for the lifetime of the bitvec. - * - * This function is part of the `display_bit_vec` module. + * returns: [Cp437Grid] initialized to 0. */ -struct ByteSlice sp_display_bit_vec_as_raw_mut_slice(DisplayBitVec */*notnull*/ instance); +Cp437Grid */*notnull*/ sp_cp437_grid_new(size_t width, size_t height); /** - * Calls method [`DisplayBitVec::clone`]. - * - *Clones a [`DisplayBitVec`] instance. - * - * This function is part of the `display_bit_vec` module. - */ -DisplayBitVec */*notnull*/ sp_display_bit_vec_clone(DisplayBitVec */*notnull*/ instance); - -/** - * Calls method [`DisplayBitVec::fill`]. - * - * Sets the value of all bits. + * Sets the value of the specified position in the [Cp437Grid]. * * # Arguments * - * - `value`: the value to set all bits to + * - `cp437_grid`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell * - * This function is part of the `display_bit_vec` module. - */ -void sp_display_bit_vec_fill(DisplayBitVec */*notnull*/ instance, bool value); - -/** - *Deallocates a [`DisplayBitVec`] instance. - * - * This function is part of the `display_bit_vec` module. - */ -void sp_display_bit_vec_free(DisplayBitVec */*notnull*/ instance); - -/** - * Gets the value of a bit. - * - * # Arguments - * - * - `bit_vec`: instance to read from - * - `index`: the bit index to read - * - * returns: value of the bit + * returns: old value of the cell * * # Panics * - * - when accessing `index` out of bounds - * - * This function is part of the `display_bit_vec` module. + * - when accessing `x` or `y` out of bounds */ -bool sp_display_bit_vec_get(DisplayBitVec */*notnull*/ instance, size_t index); +void sp_cp437_grid_set(Cp437Grid */*notnull*/ cp437_grid, + size_t x, + size_t y, + uint8_t value); /** - * Calls method [`DisplayBitVec::is_empty`]. + * Gets an unsafe reference to the data of the [Cp437Grid] instance. * - * Returns true if length is 0. - * - * This function is part of the `display_bit_vec` module. + * The returned memory is valid for the lifetime of the grid. */ -bool sp_display_bit_vec_is_empty(DisplayBitVec */*notnull*/ instance); +ByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid */*notnull*/ cp437_grid); /** - * Calls method [`DisplayBitVec::len`]. - * - * Gets the length in bits. - * - * This function is part of the `display_bit_vec` module. - */ -size_t sp_display_bit_vec_len(DisplayBitVec */*notnull*/ instance); - -/** - * Interpret the data as a series of bits and load then into a new [DisplayBitVec] instance. - * - * returns: [DisplayBitVec] instance containing data. - * - * This function is part of the `display_bit_vec` module. - */ -DisplayBitVec */*notnull*/ sp_display_bit_vec_load(struct ByteSlice data); - -/** - * Creates a new [DisplayBitVec] instance. + * Gets the width of the [Cp437Grid] instance. * * # Arguments * - * - `size`: size in bits. - * - * returns: [DisplayBitVec] with all bits set to false. - * - * # Panics - * - * - when `size` is not divisible by 8. - * - * This function is part of the `display_bit_vec` module. + * - `cp437_grid`: instance to read from */ -DisplayBitVec */*notnull*/ sp_display_bit_vec_new(size_t size); +size_t sp_cp437_grid_width(Cp437Grid */*notnull*/ cp437_grid); /** - * Calls method [`DisplayBitVec::set`]. - * - * Sets the value of a bit. - * - * # Arguments - * - * - `index`: the bit index to edit - * - `value`: the value to set the bit to - * - * # Panics - * - * - when accessing `index` out of bounds - * - * This function is part of the `display_bit_vec` module. + * Clones a [Packet]. */ -void sp_display_bit_vec_set(DisplayBitVec */*notnull*/ instance, - size_t index, - bool value); +Packet */*notnull*/ sp_packet_clone(Packet */*notnull*/ packet); /** - * Creates a [BitVecCommand] and immediately turns that into a [Packet]. - * - * The provided [DisplayBitVec] gets consumed. + * Deallocates a [Packet]. + */ +void sp_packet_free(Packet */*notnull*/ packet); + +/** + * Turns a [TypedCommand] into a [Packet]. + * The [TypedCommand] gets consumed. * * Returns NULL in case of an error. - * - * This function is part of the `display_bit_vec` module. */ -struct Packet *sp_display_bit_vec_try_into_packet(DisplayBitVec */*notnull*/ bitvec, - size_t offset, - BinaryOperation operation, - CompressionCode compression); - -/** - * Call this function at the beginning of main to enable rust logging controlled by the - * `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). - * - * This function is part of the `envlogger` module. - */ -void sp_envlogger_init(void); - -/** - * Calls method [`FadeOutCommand::clone`]. - * - *Clones a [`FadeOutCommand`] instance. - * - * This function is part of the `fade_out_command` module. - */ -struct FadeOutCommand */*notnull*/ sp_fade_out_command_clone(struct FadeOutCommand */*notnull*/ instance); - -/** - *Deallocates a [`FadeOutCommand`] instance. - * - * This function is part of the `fade_out_command` module. - */ -void sp_fade_out_command_free(struct FadeOutCommand */*notnull*/ instance); - -/** - * A yet-to-be-tested command. - * - * Returns: a new [`FadeOutCommand`] instance. - * - * This function is part of the `fade_out_command` module. - */ -struct FadeOutCommand */*notnull*/ sp_fade_out_command_new(void); - -/** - *Tries to turn a [`FadeOutCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `fade_out_command` module. - */ -struct Packet *sp_fade_out_command_try_into_packet(struct FadeOutCommand */*notnull*/ instance); - -/** - * Calls method [`GenericCommand::clone`]. - * - *Clones a [`GenericCommand`] instance. - * - * This function is part of the `generic_command` module. - */ -struct GenericCommand */*notnull*/ sp_generic_command_clone(struct GenericCommand */*notnull*/ instance); - -/** - *Deallocates a [`GenericCommand`] instance. - * - * This function is part of the `generic_command` module. - */ -void sp_generic_command_free(struct GenericCommand */*notnull*/ instance); - -/** - * Tries to turn a [Packet] into a [GenericCommand]. - * - * The packet is dropped in the process. - * - * Returns: pointer to new [GenericCommand] instance or NULL if parsing failed. - * - * This function is part of the `generic_command` module. - */ -struct GenericCommand */*notnull*/ sp_generic_command_try_from_packet(struct Packet */*notnull*/ packet); - -/** - * Tries to turn a [GenericCommand] into a [Packet]. - * The [GenericCommand] gets consumed. - * - * Returns tag [CommandTag::Invalid] in case of an error. - * - * This function is part of the `generic_command` module. - */ -struct Packet *sp_generic_command_try_into_packet(struct GenericCommand */*notnull*/ command); - -/** - * Calls method [`GlobalBrightnessCommand::clone`]. - * - *Clones a [`GlobalBrightnessCommand`] instance. - * - * This function is part of the `global_brightness_command` module. - */ -struct GlobalBrightnessCommand */*notnull*/ sp_global_brightness_command_clone(struct GlobalBrightnessCommand */*notnull*/ instance); - -/** - *Deallocates a [`GlobalBrightnessCommand`] instance. - * - * This function is part of the `global_brightness_command` module. - */ -void sp_global_brightness_command_free(struct GlobalBrightnessCommand */*notnull*/ instance); - -/** - * Gets the value of field `brightness` of the [`servicepoint::GlobalBrightnessCommand`]. - * - * This function is part of the `global_brightness_command` module. - */ -Brightness sp_global_brightness_command_get_brightness(struct GlobalBrightnessCommand */*notnull*/ instance); - -/** - * Set the brightness of all tiles to the same value. - * - * Returns: a new [GlobalBrightnessCommand] instance. - * - * This function is part of the `global_brightness_command` module. - */ -struct GlobalBrightnessCommand */*notnull*/ sp_global_brightness_command_new(Brightness brightness); - -/** - * Sets the value of field `brightness` of the [`servicepoint::GlobalBrightnessCommand`]. - * - * This function is part of the `global_brightness_command` module. - */ -void sp_global_brightness_command_set_brightness(struct GlobalBrightnessCommand */*notnull*/ instance, - Brightness value); - -/** - *Tries to turn a [`GlobalBrightnessCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `global_brightness_command` module. - */ -struct Packet *sp_global_brightness_command_try_into_packet(struct GlobalBrightnessCommand */*notnull*/ instance); - -/** - * Calls method [`HardResetCommand::clone`]. - * - *Clones a [`HardResetCommand`] instance. - * - * This function is part of the `hard_reset_command` module. - */ -struct HardResetCommand */*notnull*/ sp_hard_reset_command_clone(struct HardResetCommand */*notnull*/ instance); - -/** - *Deallocates a [`HardResetCommand`] instance. - * - * This function is part of the `hard_reset_command` module. - */ -void sp_hard_reset_command_free(struct HardResetCommand */*notnull*/ instance); - -/** - * 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 [`HardResetCommand`] instance. - * - * This function is part of the `hard_reset_command` module. - */ -struct HardResetCommand */*notnull*/ sp_hard_reset_command_new(void); - -/** - *Tries to turn a [`HardResetCommand`] into a [Packet]. - * - * Returns: NULL or a [Packet] containing the command. - * - * [Packet]: [`servicepoint::Packet`] - * - * This function is part of the `hard_reset_command` module. - */ -struct Packet *sp_hard_reset_command_try_into_packet(struct HardResetCommand */*notnull*/ instance); - -/** - * Calls method [`Packet::clone`]. - * - *Clones a [`Packet`] instance. - * - * This function is part of the `packet` module. - */ -struct Packet */*notnull*/ sp_packet_clone(struct Packet */*notnull*/ instance); - -/** - *Deallocates a [`Packet`] instance. - * - * This function is part of the `packet` module. - */ -void sp_packet_free(struct Packet */*notnull*/ instance); +Packet *sp_packet_from_command(TypedCommand */*notnull*/ command); /** * Creates a raw [Packet] from parts. * * returns: new instance. Will never return null. - * - * This function is part of the `packet` module. */ -struct Packet */*notnull*/ sp_packet_from_parts(struct Header header, - struct ByteSlice payload); +Packet */*notnull*/ sp_packet_from_parts(Header header, + const ByteSlice *payload); /** - * Gets the value of field `header` of the [`servicepoint::Packet`]. + * Returns a pointer to the header field of the provided packet. * - * This function is part of the `packet` module. + * The returned header can be changed and will be valid for the lifetime of the packet. */ -struct Header sp_packet_get_header(struct Packet */*notnull*/ instance); - -/** - * Gets a reference to the field `header` of the [`servicepoint::Packet`]. - * - * - The returned reference inherits the lifetime of object in which it is contained. - * - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - * - * This function is part of the `packet` module. - */ -struct Header */*notnull*/ sp_packet_get_header_mut(struct Packet */*notnull*/ instance); +Header */*notnull*/ sp_packet_get_header(Packet */*notnull*/ packet); /** * Returns a pointer to the current payload of the provided packet. * - * Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload. - * * The returned memory can be changed and will be valid until a new payload is set. - * - * This function is part of the `packet` module. */ -struct ByteSlice sp_packet_get_payload(struct Packet */*notnull*/ packet); +ByteSlice sp_packet_get_payload(Packet */*notnull*/ packet); /** * Serialize the packet into the provided buffer. @@ -2088,96 +1188,72 @@ struct ByteSlice sp_packet_get_payload(struct Packet */*notnull*/ packet); * # Panics * * - if the buffer is not big enough to hold header+payload. - * - * This function is part of the `packet` module. */ -size_t sp_packet_serialize_to(struct Packet */*notnull*/ packet, - struct ByteSlice buffer); - -/** - * Sets the value of field `header` of the [`servicepoint::Packet`]. - * - * This function is part of the `packet` module. - */ -void sp_packet_set_header(struct Packet */*notnull*/ instance, - struct Header value); +void sp_packet_serialize_to(Packet */*notnull*/ packet, ByteSlice buffer); /** * Sets the payload of the provided packet to the provided data. * * This makes previous payload pointers invalid. - * - * This function is part of the `packet` module. */ -void sp_packet_set_payload(struct Packet */*notnull*/ packet, - struct ByteSlice data); +void sp_packet_set_payload(Packet */*notnull*/ packet, ByteSlice data); /** * Tries to load a [Packet] from the passed array with the specified length. * * returns: NULL in case of an error, pointer to the allocated packet otherwise - * - * This function is part of the `packet` module. */ -struct Packet *sp_packet_try_load(struct ByteSlice data); +Packet *sp_packet_try_load(ByteSlice data); /** * Converts u16 into [CommandCode]. * * If the provided value is not valid, false is returned and result is not changed. - * - * This function is part of the `sp` module. */ -bool sp_sp_u16_to_command_code(uint16_t code, - CommandCode */*notnull*/ result); +bool sp_u16_to_command_code(uint16_t code, + CommandCode *result); /** - *Deallocates a [`UdpSocket`] instance. - * - * This function is part of the `udp_socket` module. + * Closes and deallocates a [UdpConnection]. */ -void sp_udp_socket_free(struct UdpSocket */*notnull*/ instance); +void sp_udp_free(UdpSocket */*notnull*/ connection); /** - * Creates a new instance of [UdpSocket]. + * Creates a new instance of [UdpConnection]. * * returns: NULL if connection fails, or connected instance * * # Examples * * ```C - * UdpSocket connection = sp_udp_open("172.23.42.29:2342"); + * UdpConnection connection = sp_udp_open("172.23.42.29:2342"); * if (connection != NULL) * sp_udp_send_command(connection, sp_command_clear()); * ``` - * - * This function is part of the `udp_socket` module. */ -struct UdpSocket *sp_udp_socket_open(char */*notnull*/ host); +UdpSocket *sp_udp_open(char */*notnull*/ host); /** - * Creates a new instance of [UdpSocket]. + * Creates a new instance of [UdpConnection]. * * returns: NULL if connection fails, or connected instance * * # Examples * * ```C - * UdpSocket connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); + * UdpConnection connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); * if (connection != NULL) * sp_udp_send_command(connection, sp_command_clear()); * ``` - * - * This function is part of the `udp_socket` module. */ -struct UdpSocket *sp_udp_socket_open_ipv4(uint8_t ip1, - uint8_t ip2, - uint8_t ip3, - uint8_t ip4, - uint16_t port); +UdpSocket *sp_udp_open_ipv4(uint8_t ip1, + uint8_t ip2, + uint8_t ip3, + uint8_t ip4, + uint16_t port); /** - * Sends a [GenericCommand] to the display using the [UdpSocket]. + * Sends a [TypedCommand] to the display using the [UdpConnection]. * * The passed `command` gets consumed. * @@ -2188,14 +1264,12 @@ struct UdpSocket *sp_udp_socket_open_ipv4(uint8_t ip1, * ```C * sp_udp_send_command(connection, sp_command_brightness(5)); * ``` - * - * This function is part of the `udp_socket` module. */ -bool sp_udp_socket_send_command(struct UdpSocket */*notnull*/ connection, - struct GenericCommand */*notnull*/ command); +bool sp_udp_send_command(UdpSocket */*notnull*/ connection, + TypedCommand */*notnull*/ command); /** - * Sends a [Header] to the display using the [UdpSocket]. + * Sends a [Header] to the display using the [UdpConnection]. * * returns: true in case of success * @@ -2204,26 +1278,19 @@ bool sp_udp_socket_send_command(struct UdpSocket */*notnull*/ connection, * ```C * sp_udp_send_header(connection, sp_command_brightness(5)); * ``` - * - * This function is part of the `udp_socket` module. */ -bool sp_udp_socket_send_header(struct UdpSocket */*notnull*/ udp_connection, - struct Header header); +bool sp_udp_send_header(UdpSocket */*notnull*/ udp_connection, Header header); /** - * Sends a [Packet] to the display using the [UdpSocket]. + * Sends a [Packet] to the display using the [UdpConnection]. * * The passed `packet` gets consumed. * * returns: true in case of success - * - * This function is part of the `udp_socket` module. */ -bool sp_udp_socket_send_packet(struct UdpSocket */*notnull*/ connection, - struct Packet */*notnull*/ packet); +bool sp_udp_send_packet(UdpSocket */*notnull*/ connection, + Packet */*notnull*/ packet); #ifdef __cplusplus } // extern "C" #endif // __cplusplus - -#endif /* SERVICEPOINT_BINDINGS_C */ diff --git a/nix-build-all.sh b/nix-build-all.sh deleted file mode 100755 index 9472cc5..0000000 --- a/nix-build-all.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -BUILD="nom build -L" - -$BUILD .#servicepoint-binding-c -o result - -$BUILD .#servicepoint-binding-c-stable-release -o result-stable-release -$BUILD .#servicepoint-binding-c-stable-size -o result-stable-size -$BUILD .#servicepoint-binding-c-nightly-release -o result-nightly-release - -$BUILD .#all-examples -o result-examples -$BUILD .#all-examples-size -o result-examples-size diff --git a/packages.nix b/packages.nix deleted file mode 100644 index e01882e..0000000 --- a/packages.nix +++ /dev/null @@ -1,181 +0,0 @@ -{ - pkgs, - lib, - fenix, - selfPkgs, - ... -}: -let - version = "0.15.0"; - mkServicepoint = - { - rustPlatform, - pkgs, - buildType ? "release", - buildNoDefaultFeatures ? false, - cargoBuildFlags ? [ ], - nativeBuildInputs ? [], - stdlib ? false, - }: - rustPlatform.buildRustPackage (finalAttrs: { - inherit version buildType cargoBuildFlags stdlib; - - pname = "servicepoint-binding-c"; - src = lib.filter { - root = ./.; - include = [ - ./Cargo.lock - ./Cargo.toml - ./src - ./include - ./LICENSE - ./cbindgen.toml - ]; - }; - cargoLock.lockFile = ./Cargo.lock; - useFetchCargoVendor = true; - meta = { - description = "C bindings for the servicepoint crate."; - homepage = "https://git.berlin.ccc.de/servicepoint/servicepoint-binding-c"; - license = lib.licenses.gpl3Plus; - pkgConfigModules = [ "servicepoint" ]; - }; - nativeBuildInputs = [ pkgs.pkg-config ] ++ nativeBuildInputs; - buildInputs = [ pkgs.xz ]; - postInstall = '' - cp -r $src/include $out - - mkdir -p $out/lib/pkgconfig - sed "s:\$out:$out:g" ${./servicepoint.pc.in} | sed "s:\$version:$version:g" > $out/lib/pkgconfig/servicepoint.pc - ''; - }); - mkExample = - { - name, - servicepointBinding, - pkgs, - EXTRA_CFLAGS ? "", - static ? false, - }: - let - staticPkgConfigParam = if static then "--static" else ""; - in - pkgs.gccStdenv.mkDerivation { - inherit version EXTRA_CFLAGS; - pname = "servicepoint-c-example-${name}"; - nativeBuildInputs = [ pkgs.pkg-config ]; - buildInputs = [ - servicepointBinding - pkgs.xz - ]; - src = ./example/src; - buildPhase = '' - set -e - set -x - mkdir -p $out/bin - $CC ${name}.c ${if static then "-static" else ""} $CFLAGS $EXTRA_CFLAGS \ - $(pkg-config --libs --cflags ${staticPkgConfigParam} servicepoint) \ - $(pkg-config --libs --cflags ${staticPkgConfigParam} liblzma) \ - -o $out/bin/${name} - ''; - }; - examples = [ - "announce" - "brightness_tester" - "header_logger" - "moving_line" - "random_stuff" - "wiping_clear" - ]; - mkAllExamples = - suffix: - pkgs.symlinkJoin { - name = "servicepoint-all-examples"; - paths = builtins.map (e: selfPkgs."${e}${suffix}") examples; - }; - size-cflags = [ - "-Oz" - "-fwrapv" - "-fomit-frame-pointer" - "-fno-stack-protector" - "-fno-unroll-loops" - "-fno-unwind-tables" - "-fno-asynchronous-unwind-tables" - "-fmerge-all-constants" - "-fvisibility=hidden" - "-Bsymbolic" - "-fno-ident" - "-fno-exceptions" - "-ffunction-sections" - "-fdata-sections" - "-Wl,-z,norelro" - "-Wl,--hash-style=gnu" - "-Wl,--gc-sections" - "-Wl,--exclude-libs,ALL" - ]; - rustPlatform-stable = pkgs.rustPlatform; - rustPlatform-nightly = pkgs.makeRustPlatform fenix.complete; - rustPlatform-musl-stable = pkgs.pkgsMusl.rustPlatform; - rustPlatform-musl-nightly = pkgs.pkgsMusl.makeRustPlatform fenix.complete; - stable-release-args = { - inherit pkgs; - rustPlatform = rustPlatform-stable; - }; - nightly-release-args = { - inherit pkgs; - rustPlatform = rustPlatform-nightly; - }; - musl-stable-release-args = { - pkgs = pkgs.pkgsMusl; - rustPlatform = rustPlatform-musl-stable; - }; - musl-nightly-release-args = { - pkgs = pkgs.pkgsMusl; - rustPlatform = rustPlatform-musl-nightly; - }; - stable-size-args = { - buildType = "size_optimized"; - buildNoDefaultFeatures = true; - }; - nightly-size-args = { - cargoBuildFlags = [ - "-Zbuild-std=core,std,alloc,proc_macro,panic_abort" - "-Zbuild-std-features=panic_immediate_abort" - ]; - stdlib = true; - # TODO: remove hard-coded target - nativeBuildInputs = [fenix.targets."x86_64-unknown-linux-gnu".latest.rust-std]; - # TODO: those override the nix flags - # NIX_CFLAGS_COMPILE = builtins.toString ["-Oz" "-fwrapv" "-fomit-frame-pointer" "-fno-stack-protector" "-fno-unroll-loops" "-fno-unwind-tables" "-fno-asynchronous-unwind-tables" "-fmerge-all-constants" "-fvisibility=hidden" "-Bsymbolic" "-fno-ident" "-fno-exceptions" "-ffunction-sections" "-fdata-sections"]; - # NIX_CFLAGS_LINK = builtins.toString ["Wl,-z,norelro" "-Wl,--hash-style=gnu" "-Wl,--gc-sections" "-Wl,--exclude-libs,ALL"]; - }; -in -rec { - servicepoint-binding-c-stable-release = mkServicepoint stable-release-args; - servicepoint-binding-c-nightly-release = mkServicepoint nightly-release-args; - servicepoint-binding-c-stable-size = mkServicepoint (stable-release-args // stable-size-args); - - servicepoint-binding-c = servicepoint-binding-c-stable-release; - - all-examples = mkAllExamples ""; - all-examples-size = mkAllExamples "-size"; -} -# construct one package per example -// (lib.genAttrs examples ( - name: - mkExample { - inherit name pkgs; - servicepointBinding = selfPkgs.servicepoint-binding-c; - } -)) -# construct another pakage per example, but optimized for size with stable rust -// (lib.mapAttrs' (name: value: lib.nameValuePair ("${name}-size") value) ( - lib.genAttrs examples ( - name: - mkExample { - inherit name pkgs; - servicepointBinding = selfPkgs.servicepoint-binding-c-stable-size; - EXTRA_CFLAGS = builtins.toString size-cflags; - } - ) -)) diff --git a/servicepoint.pc.in b/servicepoint.pc.in deleted file mode 100644 index 5f474e7..0000000 --- a/servicepoint.pc.in +++ /dev/null @@ -1,6 +0,0 @@ -Name: servicepoint -Description: C bindings for the servicepoint library -Version: $version -URL: https://git.berlin.ccc.de/servicepoint/servicepoint-binding-c -Libs: -L$out/lib -lservicepoint_binding_c -Cflags: -I$out/include diff --git a/src/bitmap.rs b/src/bitmap.rs new file mode 100644 index 0000000..5db4af0 --- /dev/null +++ b/src/bitmap.rs @@ -0,0 +1,226 @@ +use crate::byte_slice::ByteSlice; +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, SPBitVec}; +use servicepoint::{ + Bitmap, BitmapCommand, CompressionCode, DataRef, Grid, Origin, Packet, +}; +use std::ptr::NonNull; + +/// 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, or NULL in case of an error. +/// +/// # Errors +/// +/// In the following cases, this function will return NULL: +/// +/// - when the width is not dividable by 8 +/// +/// # 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); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_new( + width: usize, + height: usize, +) -> *mut Bitmap { + if let Some(bitmap) = Bitmap::new(width, height) { + heap_move(bitmap) + } else { + std::ptr::null_mut() + } +} + +/// Creates a new [Bitmap] with a size matching the screen. +/// +/// returns: [Bitmap] initialized to all pixels off. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_new_max_sized() -> NonNull { + heap_move_nonnull(Bitmap::max_sized()) +} + +/// 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, or NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_load( + width: usize, + height: usize, + data: ByteSlice, +) -> *mut Bitmap { + let data = unsafe { data.as_slice() }; + if let Ok(bitmap) = Bitmap::load(width, height, data) { + heap_move(bitmap) + } else { + std::ptr::null_mut() + } +} + +/// Tries to convert the BitVec to a Bitmap. +/// +/// The provided BitVec gets consumed. +/// +/// Returns NULL in case of error. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_from_bitvec( + width: usize, + bitvec: NonNull, +) -> *mut Bitmap { + let bitvec = unsafe { heap_remove(bitvec) }; + if let Ok(bitmap) = Bitmap::from_bitvec(width, bitvec.0) { + heap_move(bitmap) + } else { + std::ptr::null_mut() + } +} + +/// Clones a [Bitmap]. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_clone( + bitmap: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { bitmap.as_ref().clone() }) +} + +/// Deallocates a [Bitmap]. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull) { + unsafe { heap_drop(bitmap) } +} + +/// Gets the current value at the specified position in the [Bitmap]. +/// +/// # Arguments +/// +/// - `bitmap`: instance to read from +/// - `x` and `y`: position of the cell to read +/// +/// # Panics +/// +/// - when accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_get( + bitmap: NonNull, + x: usize, + y: usize, +) -> bool { + unsafe { bitmap.as_ref().get(x, y) } +} + +/// Sets the value of the specified position in the [Bitmap]. +/// +/// # Arguments +/// +/// - `bitmap`: instance to write to +/// - `x` and `y`: position of the cell +/// - `value`: the value to write to the cell +/// +/// # Panics +/// +/// - when accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_set( + bitmap: NonNull, + x: usize, + y: usize, + value: bool, +) { + unsafe { (*bitmap.as_ptr()).set(x, y, value) }; +} + +/// Sets the state of all pixels in the [Bitmap]. +/// +/// # Arguments +/// +/// - `bitmap`: instance to write to +/// - `value`: the value to set all pixels to +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_fill(bitmap: NonNull, value: bool) { + unsafe { (*bitmap.as_ptr()).fill(value) }; +} + +/// Gets the width in pixels of the [Bitmap] 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 [Bitmap] +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_width(bitmap: NonNull) -> usize { + unsafe { bitmap.as_ref().width() } +} + +/// Gets the height in pixels of the [Bitmap] instance. +/// +/// # Arguments +/// +/// - `bitmap`: instance to read from +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_height(bitmap: NonNull) -> usize { + unsafe { bitmap.as_ref().height() } +} + +/// Gets an unsafe reference to the data of the [Bitmap] instance. +/// +/// The returned memory is valid for the lifetime of the bitmap. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_unsafe_data_ref( + mut bitmap: NonNull, +) -> ByteSlice { + unsafe { ByteSlice::from_slice(bitmap.as_mut().data_ref_mut()) } +} + +/// Consumes the Bitmap and returns the contained BitVec +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_into_bitvec( + bitmap: NonNull, +) -> NonNull { + let bitmap = unsafe { heap_remove(bitmap) }; + heap_move_nonnull(SPBitVec(bitmap.into())) +} + +/// Creates a [BitmapCommand] and immediately turns that into a [Packet]. +/// +/// The provided [Bitmap] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_bitmap_into_packet( + bitmap: NonNull, + x: usize, + y: usize, + compression: CompressionCode, +) -> *mut Packet { + let bitmap = unsafe { heap_remove(bitmap) }; + match Packet::try_from(BitmapCommand { + bitmap, + origin: Origin::new(x, y), + compression, + }) { + Ok(packet) => heap_move(packet), + Err(_) => std::ptr::null_mut(), + } +} diff --git a/src/bitvec.rs b/src/bitvec.rs new file mode 100644 index 0000000..0e7ee5e --- /dev/null +++ b/src/bitvec.rs @@ -0,0 +1,174 @@ +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; +use servicepoint::{ + BinaryOperation, BitVecCommand, CompressionCode, DisplayBitVec, Packet, +}; +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(pub(crate) DisplayBitVec); + +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. +/// +/// # Panics +/// +/// - when `size` is not divisible by 8. +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull { + heap_move_nonnull(SPBitVec(DisplayBitVec::repeat(false, size))) +} + +/// Interpret the data as a series of bits and load then into a new [SPBitVec] instance. +/// +/// returns: [SPBitVec] instance containing data. +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_load(data: ByteSlice) -> NonNull { + let data = unsafe { data.as_slice() }; + heap_move_nonnull(SPBitVec(DisplayBitVec::from_slice(data))) +} + +/// Clones a [SPBitVec]. +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_clone( + bit_vec: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { bit_vec.as_ref().clone() }) +} + +/// Deallocates a [SPBitVec]. +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_free(bit_vec: NonNull) { + unsafe { heap_drop(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 accessing `index` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_get( + bit_vec: NonNull, + index: usize, +) -> bool { + unsafe { *bit_vec.as_ref().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 accessing `index` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_set( + bit_vec: NonNull, + index: usize, + value: bool, +) { + unsafe { (*bit_vec.as_ptr()).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 +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_fill( + bit_vec: NonNull, + value: bool, +) { + unsafe { (*bit_vec.as_ptr()).0.fill(value) } +} + +/// Gets the length of the [SPBitVec] in bits. +/// +/// # Arguments +/// +/// - `bit_vec`: instance to write to +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_len(bit_vec: NonNull) -> usize { + unsafe { bit_vec.as_ref().0.len() } +} + +/// Returns true if length is 0. +/// +/// # Arguments +/// +/// - `bit_vec`: instance to write to +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_is_empty( + bit_vec: NonNull, +) -> bool { + unsafe { bit_vec.as_ref().0.is_empty() } +} + +/// Gets an unsafe reference to the data of the [SPBitVec] instance. +/// +/// The returned memory is valid for the lifetime of the bitvec. +/// +/// # Arguments +/// +/// - `bit_vec`: instance to write to +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_unsafe_data_ref( + bit_vec: NonNull, +) -> ByteSlice { + unsafe { ByteSlice::from_slice((*bit_vec.as_ptr()).0.as_raw_mut_slice()) } +} + +/// Creates a [BitVecCommand] and immediately turns that into a [Packet]. +/// +/// The provided [SPBitVec] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_bitvec_into_packet( + bitvec: NonNull, + offset: usize, + operation: BinaryOperation, + compression: CompressionCode, +) -> *mut Packet { + let bitvec = unsafe { heap_remove(bitvec) }.0; + match Packet::try_from(BitVecCommand { + bitvec, + offset, + operation, + compression, + }) { + Ok(packet) => heap_move(packet), + Err(_) => std::ptr::null_mut(), + } +} diff --git a/src/brightness_grid.rs b/src/brightness_grid.rs new file mode 100644 index 0000000..1099be0 --- /dev/null +++ b/src/brightness_grid.rs @@ -0,0 +1,197 @@ +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; +use servicepoint::{ + Brightness, BrightnessGrid, BrightnessGridCommand, ByteGrid, DataRef, Grid, + Origin, Packet, +}; +use std::mem::transmute; +use std::ptr::NonNull; + +/// Creates a new [BrightnessGrid] with the specified dimensions. +/// +/// returns: [BrightnessGrid] initialized to 0. +/// +/// # Examples +/// ```C +/// UdpConnection connection = sp_udp_open("127.0.0.1:2342"); +/// if (connection == NULL) +/// return 1; +/// +/// BrightnessGrid grid = sp_brightness_grid_new(2, 2); +/// sp_brightness_grid_set(grid, 0, 0, 0); +/// sp_brightness_grid_set(grid, 1, 1, 10); +/// +/// TypedCommand command = sp_command_char_brightness(grid); +/// sp_udp_free(connection); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_new( + width: usize, + height: usize, +) -> NonNull { + heap_move_nonnull(BrightnessGrid::new(width, height)) +} + +/// Loads a [BrightnessGrid] with the specified dimensions from the provided data. +/// +/// Any out of range values will be set to [Brightness::MAX] or [Brightness::MIN]. +/// +/// returns: new [BrightnessGrid] instance, or NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_load( + width: usize, + height: usize, + data: ByteSlice, +) -> *mut BrightnessGrid { + let data = unsafe { data.as_slice() }; + + match ByteGrid::load(width, height, data) + .map(move |grid| grid.map(Brightness::saturating_from)) + { + None => std::ptr::null_mut(), + Some(grid) => heap_move(grid), + } +} + +/// Clones a [BrightnessGrid]. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_clone( + brightness_grid: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { brightness_grid.as_ref().clone() }) +} + +/// Deallocates a [BrightnessGrid]. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_free( + brightness_grid: NonNull, +) { + unsafe { heap_drop(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 accessing `x` or `y` out of bounds. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_get( + brightness_grid: NonNull, + x: usize, + y: usize, +) -> Brightness { + unsafe { brightness_grid.as_ref().get(x, y) } +} + +/// Sets the value of the specified position in the [BrightnessGrid]. +/// +/// # 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 accessing `x` or `y` out of bounds. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_set( + brightness_grid: NonNull, + x: usize, + y: usize, + value: Brightness, +) { + unsafe { (*brightness_grid.as_ptr()).set(x, y, value) }; +} + +/// Sets the value of all cells in the [BrightnessGrid]. +/// +/// # Arguments +/// +/// - `brightness_grid`: instance to write to +/// - `value`: the value to set all cells to +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_fill( + brightness_grid: NonNull, + value: Brightness, +) { + unsafe { (*brightness_grid.as_ptr()).fill(value) }; +} + +/// Gets the width of the [BrightnessGrid] instance. +/// +/// # Arguments +/// +/// - `brightness_grid`: instance to read from +/// +/// returns: width +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_width( + brightness_grid: NonNull, +) -> usize { + unsafe { brightness_grid.as_ref().width() } +} + +/// Gets the height of the [BrightnessGrid] instance. +/// +/// # Arguments +/// +/// - `brightness_grid`: instance to read from +/// +/// returns: height +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_height( + brightness_grid: NonNull, +) -> usize { + unsafe { brightness_grid.as_ref().height() } +} + +/// Gets an unsafe reference to the data of the [BrightnessGrid] instance. +/// +/// The returned memory is valid for the lifetime of the brightness grid. +/// +/// # Arguments +/// +/// - `brightness_grid`: instance to read from +/// +/// returns: slice of bytes underlying the `brightness_grid`. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( + brightness_grid: NonNull, +) -> ByteSlice { + //noinspection RsAssertEqual + const _: () = assert!(size_of::() == 1); + + let data = unsafe { (*brightness_grid.as_ptr()).data_ref_mut() }; + unsafe { + ByteSlice::from_slice(transmute::<&mut [Brightness], &mut [u8]>(data)) + } +} + +/// Creates a [BrightnessGridCommand] and immediately turns that into a [Packet]. +/// +/// The provided [BrightnessGrid] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_brightness_grid_into_packet( + grid: NonNull, + x: usize, + y: usize, +) -> *mut Packet { + let grid = unsafe { heap_remove(grid) }; + match Packet::try_from(BrightnessGridCommand { + grid, + origin: Origin::new(x, y), + }) { + Ok(packet) => heap_move(packet), + Err(_) => std::ptr::null_mut(), + } +} diff --git a/src/byte_slice.rs b/src/byte_slice.rs new file mode 100644 index 0000000..de7d0df --- /dev/null +++ b/src/byte_slice.rs @@ -0,0 +1,39 @@ +//! FFI slice helper + +use std::ptr::NonNull; + +/// Represents a span of memory (`&mut [u8]` ) as a struct. +/// +/// # 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. +#[repr(C)] +pub struct ByteSlice { + /// The start address of the memory. + pub start: NonNull, + /// The amount of memory in bytes. + pub length: usize, +} + +impl ByteSlice { + pub(crate) unsafe fn as_slice(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) } + } + + pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { + std::slice::from_raw_parts_mut(self.start.as_ptr(), self.length) + } + } + + pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self { + Self { + start: NonNull::new(slice.as_mut_ptr()).unwrap(), + length: slice.len(), + } + } +} diff --git a/src/char_grid.rs b/src/char_grid.rs new file mode 100644 index 0000000..bfb2585 --- /dev/null +++ b/src/char_grid.rs @@ -0,0 +1,155 @@ +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; +use servicepoint::{CharGrid, CharGridCommand, Grid, Origin, Packet}; +use std::ptr::NonNull; + +/// Creates a new [CharGrid] with the specified dimensions. +/// +/// returns: [CharGrid] initialized to 0. +/// +/// # 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); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_new( + width: usize, + height: usize, +) -> NonNull { + heap_move_nonnull(CharGrid::new(width, height)) +} + +/// Loads a [CharGrid] with the specified dimensions from the provided data. +/// +/// returns: new CharGrid or NULL in case of an error +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_load( + width: usize, + height: usize, + data: ByteSlice, +) -> *mut CharGrid { + let data = unsafe { data.as_slice() }; + if let Ok(grid) = CharGrid::load_utf8(width, height, data.to_vec()) { + heap_move(grid) + } else { + std::ptr::null_mut() + } +} + +/// Clones a [CharGrid]. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_clone( + char_grid: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { char_grid.as_ref().clone() }) +} + +/// Deallocates a [CharGrid]. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_free(char_grid: NonNull) { + unsafe { heap_drop(char_grid) } +} + +/// Returns 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 accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_get( + char_grid: NonNull, + x: usize, + y: usize, +) -> u32 { + unsafe { char_grid.as_ref().get(x, y) as u32 } +} + +/// Sets the value of the specified position in the [CharGrid]. +/// +/// # 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 accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_set( + char_grid: NonNull, + x: usize, + y: usize, + value: u32, +) { + unsafe { (*char_grid.as_ptr()).set(x, y, char::from_u32(value).unwrap()) }; +} + +/// Sets the value of all cells in the [CharGrid]. +/// +/// # Arguments +/// +/// - `char_grid`: instance to write to +/// - `value`: the value to set all cells to +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_fill( + char_grid: NonNull, + value: u32, +) { + unsafe { (*char_grid.as_ptr()).fill(char::from_u32(value).unwrap()) }; +} + +/// Gets the width of the [CharGrid] instance. +/// +/// # Arguments +/// +/// - `char_grid`: instance to read from +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_width( + char_grid: NonNull, +) -> usize { + unsafe { char_grid.as_ref().width() } +} + +/// Gets the height of the [CharGrid] instance. +/// +/// # Arguments +/// +/// - `char_grid`: instance to read from +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_height( + char_grid: NonNull, +) -> usize { + unsafe { char_grid.as_ref().height() } +} + +/// Creates a [CharGridCommand] and immediately turns that into a [Packet]. +/// +/// The provided [CharGrid] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_into_packet( + grid: NonNull, + x: usize, + y: usize, +) -> *mut Packet { + let grid = unsafe { heap_remove(grid) }; + match Packet::try_from(CharGridCommand { + grid, + origin: Origin::new(x, y), + }) { + Ok(packet) => heap_move(packet), + Err(_) => std::ptr::null_mut(), + } +} diff --git a/src/commands/bitmap_command.rs b/src/commands/bitmap_command.rs deleted file mode 100644 index d325bce..0000000 --- a/src/commands/bitmap_command.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{Bitmap, BitmapCommand, CompressionCode, Origin}; -use std::ptr::NonNull; - -wrap! { - BitmapCommand { - derives: crate::commands::derive_command[Bitmap], crate::commands::derive_origin_accessors; - properties: - prop bitmap: Bitmap { get mut; set move; }; - prop compression: CompressionCode { get; set; }; - functions: - /// Sets a window of pixels to the specified values. - /// - /// The passed [Bitmap] gets consumed. - /// - /// Returns: a new [BitmapCommand] instance. - fn new( - bitmap: move NonNull, - origin_x: val usize, - origin_y: val usize, - compression: val CompressionCode, - ) -> move NonNull { - BitmapCommand { - bitmap, - origin: Origin::new(origin_x, origin_y), - compression, - } - }; - - /// Move the provided [Bitmap] into a new [BitmapCommand], - /// leaving other fields as their default values. - /// - /// Rust equivalent: `BitmapCommand::from(bitmap)` - fn from_bitmap(bitmap: move NonNull) -> move NonNull { - bitmap.into() - }; - } -} diff --git a/src/commands/bitvec_command.rs b/src/commands/bitvec_command.rs deleted file mode 100644 index 82b92fb..0000000 --- a/src/commands/bitvec_command.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{ - BinaryOperation, BitVecCommand, CompressionCode, DisplayBitVec, Offset, -}; -use std::ptr::NonNull; - -wrap!( - BitVecCommand { - derives: crate::commands::derive_command[BitVec]; - properties: - prop bitvec: DisplayBitVec { get mut; set move; }; - prop offset: Offset { get; set; }; - prop operation: BinaryOperation { get; set; }; - prop compression: CompressionCode { get; set; }; - functions: - /// 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 [`BinaryOperation`] will be applied on the display comparing old and sent bit. - /// - /// `new_bit = old_bit op sent_bit` - /// - /// For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels. - /// - /// The contained [`DisplayBitVec`] is always uncompressed. - fn new( - bitvec: move NonNull, - offset: val usize, - operation: val BinaryOperation, - compression: val CompressionCode, - ) -> move NonNull { - BitVecCommand { - bitvec, - offset, - operation, - compression, - } - }; - } -); diff --git a/src/commands/brightness_grid_command.rs b/src/commands/brightness_grid_command.rs deleted file mode 100644 index ef568bd..0000000 --- a/src/commands/brightness_grid_command.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{BrightnessGrid, BrightnessGridCommand, Origin}; -use std::ptr::NonNull; - -wrap!( - BrightnessGridCommand { - derives: crate::commands::derive_command[BrightnessGrid], crate::commands::derive_origin_accessors; - properties: - prop grid: BrightnessGrid { get mut; set move; }; - functions: - /// Set the brightness of individual tiles in a rectangular area of the display. - /// - /// The passed [BrightnessGrid] gets consumed. - /// - /// Returns: a new [BrightnessGridCommand] instance. - fn new( - grid: move NonNull, - origin_x: val usize, - origin_y: val usize - ) -> move NonNull { - BrightnessGridCommand { - grid, - origin: Origin::new(origin_x, origin_y), - } - }; - - /// Moves the provided [BrightnessGrid] into a new [BrightnessGridCommand], - /// leaving other fields as their default values. - fn from_grid(grid: move NonNull) -> move NonNull { - grid.into() - }; - } -); diff --git a/src/commands/cc_only_commands.rs b/src/commands/cc_only_commands.rs deleted file mode 100644 index 22a4a3b..0000000 --- a/src/commands/cc_only_commands.rs +++ /dev/null @@ -1,39 +0,0 @@ -use servicepoint::{ClearCommand, FadeOutCommand, HardResetCommand}; - -macro_rules! wrap_cc_only { - ($(#[$meta:meta])* $command:ident) => { - ::paste::paste!{ - $crate::macros::wrap!{ - [< $command Command >] { - derives: $crate::commands::derive_command[$command]; - functions: - $(#[$meta])* - /// - #[doc = " Returns: a new [`" [< $command Command >] "`] instance."] - fn new() -> move ::core::ptr::NonNull<[< $command Command >]> { - [< $command Command >] - }; - } - } - } - }; -} - -wrap_cc_only!( - /// Set all pixels to the off state. - /// - /// Does not affect brightness. - Clear -); - -wrap_cc_only!( - /// Kills the udp daemon on the display, which usually results in a restart. - /// - /// Please do not send this in your normal program flow. - HardReset -); - -wrap_cc_only!( - /// A yet-to-be-tested command. - FadeOut -); diff --git a/src/commands/char_grid_command.rs b/src/commands/char_grid_command.rs deleted file mode 100644 index 96d8b90..0000000 --- a/src/commands/char_grid_command.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{CharGrid, CharGridCommand, Origin}; -use std::ptr::NonNull; - -wrap!( - CharGridCommand { - derives: crate::commands::derive_command[CharGrid], crate::commands::derive_origin_accessors; - properties: - prop grid: CharGrid { get mut; set move; }; - functions: - /// Show UTF-8 encoded text on the screen. - /// - /// The passed [CharGrid] gets consumed. - /// - /// Returns: a new [CharGridCommand] instance. - fn new( - grid: move NonNull, - origin_x: val usize, - origin_y: val usize, - ) -> move NonNull { - CharGridCommand { - grid, - origin: Origin::new(origin_x, origin_y), - } - }; - - /// Moves the provided [CharGrid] into a new [CharGridCommand], - /// leaving other fields as their default values. - fn from_grid(grid: move NonNull) -> move NonNull { - grid.into() - }; - } -); diff --git a/src/commands/cp437_grid_command.rs b/src/commands/cp437_grid_command.rs deleted file mode 100644 index 19b35f6..0000000 --- a/src/commands/cp437_grid_command.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{Cp437Grid, Cp437GridCommand, Origin}; -use std::ptr::NonNull; - -wrap!( - Cp437GridCommand { - derives: crate::commands::derive_command[Cp437Grid], crate::commands::derive_origin_accessors; - properties: - prop grid: Cp437Grid { get mut; set move; }; - functions: - /// Show text on the screen. - /// - /// The text is sent in the form of a 2D grid of [CP-437] encoded characters. - /// - /// The origin is relative to the top-left of the display. - fn new( - grid: move NonNull, - origin_x: val usize, - origin_y: val usize, - ) -> move NonNull { - Cp437GridCommand { - grid, - origin: Origin::new(origin_x, origin_y), - } - }; - - /// Moves the provided [Cp437Grid] into a new [Cp437GridCommand], - /// leaving other fields as their default values. - fn from_grid(grid: move NonNull) -> move NonNull { - grid.into() - }; - } -); diff --git a/src/commands/generic_command.rs b/src/commands/generic_command.rs deleted file mode 100644 index 2991352..0000000 --- a/src/commands/generic_command.rs +++ /dev/null @@ -1,293 +0,0 @@ -use crate::{ - macros::{derive_clone, derive_free, wrap}, - mem::{ - heap_clone, heap_drop, heap_move, heap_move_nonnull, heap_move_ok, - heap_remove, - }, -}; -use servicepoint::{ - BitVecCommand, BitmapCommand, BrightnessGridCommand, CharGridCommand, - ClearCommand, Cp437GridCommand, FadeOutCommand, GlobalBrightnessCommand, - HardResetCommand, Packet, TypedCommand, -}; -use std::ptr::{null_mut, NonNull}; - -/// Pointer to one of the available command structs. -#[repr(C)] -#[allow(missing_docs)] -pub union CommandUnion { - pub null: *mut u8, - pub bitmap: NonNull, - pub bit_vec: NonNull, - pub brightness_grid: NonNull, - pub char_grid: NonNull, - pub cp437_grid: NonNull, - pub global_brightness: NonNull, - pub clear: NonNull, - #[allow(deprecated)] - pub bitmap_legacy: NonNull, - pub hard_reset: NonNull, - pub fade_out: NonNull, -} - -/// Specifies the kind of command struct. -/// -/// This is _not_ equivalent to the [servicepoint::CommandCode]s. -#[repr(u8)] -#[allow(missing_docs)] -pub enum CommandTag { - Invalid = 0, - Bitmap, - BitVec, - BrightnessGrid, - CharGrid, - Cp437Grid, - GlobalBrightness, - Clear, - HardReset, - FadeOut, - BitmapLegacy, -} - -/// This struct represents a pointer to one of the possible command structs. -/// -/// Only ever access `data` with the correct data type as specified by `tag`! -/// -/// Rust equivalent: [TypedCommand]. -#[repr(C)] -pub struct GenericCommand { - /// Specifies which kind of command struct is contained in `data` - pub tag: CommandTag, - /// The pointer to the command struct - pub data: CommandUnion, -} - -impl GenericCommand { - pub(crate) const INVALID: GenericCommand = GenericCommand { - tag: CommandTag::Invalid, - data: CommandUnion { null: null_mut() }, - }; -} - -impl Clone for GenericCommand { - fn clone(&self) -> Self { - unsafe { - match self.tag { - CommandTag::Clear => GenericCommand { - tag: CommandTag::Clear, - data: CommandUnion { - clear: heap_clone(self.data.clear), - }, - }, - CommandTag::CharGrid => GenericCommand { - tag: CommandTag::CharGrid, - data: CommandUnion { - char_grid: heap_clone(self.data.char_grid), - }, - }, - CommandTag::Cp437Grid => GenericCommand { - tag: CommandTag::Cp437Grid, - data: CommandUnion { - cp437_grid: heap_clone(self.data.cp437_grid), - }, - }, - CommandTag::Bitmap => GenericCommand { - tag: CommandTag::Bitmap, - data: CommandUnion { - bitmap: heap_clone(self.data.bitmap), - }, - }, - CommandTag::GlobalBrightness => GenericCommand { - tag: CommandTag::GlobalBrightness, - data: CommandUnion { - global_brightness: heap_clone( - self.data.global_brightness, - ), - }, - }, - CommandTag::BrightnessGrid => GenericCommand { - tag: CommandTag::BrightnessGrid, - data: CommandUnion { - brightness_grid: heap_clone(self.data.brightness_grid), - }, - }, - CommandTag::BitVec => GenericCommand { - tag: CommandTag::BitVec, - data: CommandUnion { - bit_vec: heap_clone(self.data.bit_vec), - }, - }, - CommandTag::HardReset => GenericCommand { - tag: CommandTag::HardReset, - data: CommandUnion { - hard_reset: heap_clone(self.data.hard_reset), - }, - }, - CommandTag::FadeOut => GenericCommand { - tag: CommandTag::FadeOut, - data: CommandUnion { - fade_out: heap_clone(self.data.fade_out), - }, - }, - #[allow(deprecated)] - CommandTag::BitmapLegacy => GenericCommand { - tag: CommandTag::BitmapLegacy, - data: CommandUnion { - bitmap_legacy: heap_clone(self.data.bitmap_legacy), - }, - }, - CommandTag::Invalid => GenericCommand::INVALID, - } - } - } -} - -impl Drop for GenericCommand { - fn drop(&mut self) { - unsafe { - match self.tag { - CommandTag::Invalid => (), - CommandTag::Bitmap => heap_drop(self.data.bitmap), - CommandTag::BitVec => heap_drop(self.data.bit_vec), - CommandTag::BrightnessGrid => { - heap_drop(self.data.brightness_grid) - } - CommandTag::CharGrid => heap_drop(self.data.char_grid), - CommandTag::Cp437Grid => heap_drop(self.data.cp437_grid), - CommandTag::GlobalBrightness => { - heap_drop(self.data.global_brightness) - } - CommandTag::Clear => heap_drop(self.data.clear), - CommandTag::HardReset => heap_drop(self.data.hard_reset), - CommandTag::FadeOut => heap_drop(self.data.fade_out), - CommandTag::BitmapLegacy => heap_drop(self.data.bitmap_legacy), - } - } - - *self = Self::INVALID; - } -} - -derive_clone!(GenericCommand); -derive_free!(GenericCommand); - -wrap! { - GenericCommand { - functions: - /// Tries to turn a [Packet] into a [GenericCommand]. - /// - /// The packet is dropped in the process. - /// - /// Returns: pointer to new [GenericCommand] instance or NULL if parsing failed. - fn try_from_packet(packet: move NonNull) -> move NonNull { - servicepoint::TypedCommand::try_from(packet) - .map(|value| match value { - TypedCommand::Clear(clear) => GenericCommand { - tag: CommandTag::Clear, - data: CommandUnion { - clear: heap_move_nonnull(clear), - }, - }, - TypedCommand::CharGrid(char_grid) => GenericCommand { - tag: CommandTag::CharGrid, - data: CommandUnion { - char_grid: heap_move_nonnull(char_grid), - }, - }, - TypedCommand::Cp437Grid(cp437_grid) => GenericCommand { - tag: CommandTag::Cp437Grid, - data: CommandUnion { - cp437_grid: heap_move_nonnull(cp437_grid), - }, - }, - TypedCommand::Bitmap(bitmap) => GenericCommand { - tag: CommandTag::Bitmap, - data: CommandUnion { - bitmap: heap_move_nonnull(bitmap), - }, - }, - TypedCommand::Brightness(global_brightness) => GenericCommand { - tag: CommandTag::GlobalBrightness, - data: CommandUnion { - global_brightness: heap_move_nonnull(global_brightness), - }, - }, - TypedCommand::BrightnessGrid(brightness_grid) => GenericCommand { - tag: CommandTag::BrightnessGrid, - data: CommandUnion { - brightness_grid: heap_move_nonnull(brightness_grid), - }, - }, - TypedCommand::BitVec(bitvec) => GenericCommand { - tag: CommandTag::BitVec, - data: CommandUnion { - bit_vec: heap_move_nonnull(bitvec), - }, - }, - TypedCommand::HardReset(hard_reset) => GenericCommand { - tag: CommandTag::HardReset, - data: CommandUnion { - hard_reset: heap_move_nonnull(hard_reset), - }, - }, - TypedCommand::FadeOut(fade_out) => GenericCommand { - tag: CommandTag::FadeOut, - data: CommandUnion { - fade_out: heap_move_nonnull(fade_out), - }, - }, - #[allow(deprecated)] - TypedCommand::BitmapLegacy(bitmap_legacy) => GenericCommand { - tag: CommandTag::BitmapLegacy, - data: CommandUnion { - bitmap_legacy: heap_move_nonnull(bitmap_legacy), - }, - }, - }) - .unwrap_or_else(move |_| GenericCommand { - tag: CommandTag::Invalid, - data: CommandUnion { null: null_mut() }, - }) - }; - methods: - /// Tries to turn a [GenericCommand] into a [Packet]. - /// The [GenericCommand] gets consumed. - /// - /// Returns tag [CommandTag::Invalid] in case of an error. - fn try_into_packet(move command) -> val *mut Packet { - match command.tag { - CommandTag::Invalid => null_mut(), - CommandTag::Bitmap => { - heap_move_ok(unsafe { heap_remove(command.data.bitmap).try_into() }) - } - CommandTag::BitVec => { - heap_move_ok(unsafe { heap_remove(command.data.bit_vec).try_into() }) - } - CommandTag::BrightnessGrid => heap_move_ok(unsafe { - heap_remove(command.data.brightness_grid).try_into() - }), - CommandTag::CharGrid => heap_move_ok(unsafe { - heap_remove(command.data.char_grid).try_into() - }), - CommandTag::Cp437Grid => heap_move_ok(unsafe { - heap_remove(command.data.cp437_grid).try_into() - }), - CommandTag::GlobalBrightness => heap_move(unsafe { - heap_remove(command.data.global_brightness).into() - }), - CommandTag::Clear => { - heap_move(unsafe { heap_remove(command.data.clear).into() }) - } - CommandTag::HardReset => { - heap_move(unsafe { heap_remove(command.data.hard_reset).into() }) - } - CommandTag::FadeOut => { - heap_move(unsafe { heap_remove(command.data.fade_out).into() }) - } - CommandTag::BitmapLegacy => { - heap_move(unsafe { heap_remove(command.data.bitmap_legacy).into() }) - } - } - }; - } -} diff --git a/src/commands/global_brightness_command.rs b/src/commands/global_brightness_command.rs deleted file mode 100644 index 1ab9a1b..0000000 --- a/src/commands/global_brightness_command.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::macros::wrap; -use servicepoint::{Brightness, GlobalBrightnessCommand}; -use std::ptr::NonNull; - -wrap!( - GlobalBrightnessCommand { - derives: crate::commands::derive_command[GlobalBrightness]; - properties: - prop brightness: Brightness { get; set; }; - functions: - /// Set the brightness of all tiles to the same value. - /// - /// Returns: a new [GlobalBrightnessCommand] instance. - fn new(brightness: val Brightness) -> move NonNull { - GlobalBrightnessCommand::from(brightness) - }; - } -); diff --git a/src/commands/mod.rs b/src/commands/mod.rs deleted file mode 100644 index c8e9dfc..0000000 --- a/src/commands/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -mod bitmap_command; -mod bitvec_command; -mod brightness_grid_command; -mod cc_only_commands; -mod char_grid_command; -mod cp437_grid_command; -mod generic_command; -mod global_brightness_command; - -pub use bitmap_command::*; -pub use bitvec_command::*; -pub use brightness_grid_command::*; -pub use char_grid_command::*; -pub use cp437_grid_command::*; -pub use generic_command::*; - -macro_rules! derive_origin_accessors { - ($object_type:ident) => { - ::paste::paste! { - $crate::macros::wrap_methods!($object_type; - #[doc = " Reads the origin field of the [`" $object_type "`]."] - fn get_origin( - ref command, - origin_x: mut ::core::ptr::NonNull, - origin_y: mut ::core::ptr::NonNull - ) { - let origin = command.origin; - *origin_x = origin.x; - *origin_y = origin.y; - }; - - #[doc = " Overwrites the origin field of the [`" $object_type "`]."] - fn set_origin(mut command, origin_x: val usize, origin_y: val usize) { - command.origin = ::servicepoint::Origin::new(origin_x, origin_y); - }; - ); - } - }; -} - -macro_rules! derive_command_from { - ($command:ident) => { - ::paste::paste! { - impl From<::servicepoint::[< $command Command >]> for $crate::commands::GenericCommand { - fn from(command: ::servicepoint::[< $command Command >]) -> Self { - Self { - tag: $crate::commands::CommandTag::$command, - data: $crate::commands::CommandUnion { - [< $command:snake >]: $crate::mem::heap_move_nonnull(command) - }, - } - } - } - } - }; -} - -macro_rules! derive_command_into_packet { - ($command_type:ident) => { - ::paste::paste! { - $crate::macros::wrap_method!($command_type; - #[doc = "Tries to turn a [`" $command_type "`] into a [Packet]."] - /// - /// Returns: NULL or a [Packet] containing the command. - /// - /// [Packet]: [`servicepoint::Packet`] - fn try_into_packet(move instance) -> move_ok *mut ::servicepoint::Packet { - instance.try_into() - }; - ); - } - } -} - -macro_rules! derive_command { - ($object_type:ident, $command:ident) => { - $crate::macros::derive_clone!($object_type); - $crate::macros::derive_free!($object_type); - $crate::commands::derive_command_from!($command); - $crate::commands::derive_command_into_packet!($object_type); - }; -} - -pub(crate) use { - derive_command, derive_command_from, derive_command_into_packet, - derive_origin_accessors, -}; diff --git a/src/containers/bitmap.rs b/src/containers/bitmap.rs deleted file mode 100644 index 5974016..0000000 --- a/src/containers/bitmap.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::{containers::ByteSlice, macros::wrap}; -use servicepoint::{ - Bitmap, BitmapCommand, CompressionCode, DataRef, DisplayBitVec, Grid, - Origin, Packet, -}; -use std::ptr::NonNull; - -wrap! { - Bitmap { - derives: crate::containers::derive_container, crate::containers::derive_grid[bool]; - functions: - /// 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, or NULL in case of an error. - /// - /// # Errors - /// - /// In the following cases, this function will return NULL: - /// - /// - when the width is not dividable by 8 - /// - /// # 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); - /// ``` - fn new(width: val usize, height: val usize) -> move_some *mut Bitmap { - Bitmap::new(width, height) - }; - - /// Creates a new [Bitmap] with a size matching the screen. - /// - /// returns: [Bitmap] initialized to all pixels off. - fn new_max_sized() -> move NonNull { - Bitmap::max_sized() - }; - - /// 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, or NULL in case of an error. - fn load( - width: val usize, - height: val usize, - data: slice ByteSlice, - ) -> move_ok *mut Bitmap { - Bitmap::load(width, height, data) - }; - - /// Tries to convert the BitVec to a Bitmap. - /// - /// The provided BitVec gets consumed. - /// - /// Returns NULL in case of error. - fn from_bitvec( - width: val usize, - bitvec: move NonNull, - ) -> move_ok *mut Bitmap { - Bitmap::from_bitvec(width, bitvec) - }; - - methods: - /// Consumes the Bitmap and returns the contained BitVec. - fn into_bitvec(move bitmap) -> move NonNull { - bitmap.into() - }; - - /// Creates a [BitmapCommand] and immediately turns that into a [Packet]. - /// - /// The provided [Bitmap] gets consumed. - /// - /// Returns NULL in case of an error. - fn try_into_packet(move bitmap, x: val usize, y: val usize, compression: val CompressionCode) -> move_ok *mut Packet { - Packet::try_from(BitmapCommand { - bitmap, - origin: Origin::new(x, y), - compression, - }) - }; - - /// Gets an unsafe reference to the data of the [Bitmap] instance. - /// - /// The returned memory is valid for the lifetime of the bitmap. - fn data_ref_mut(mut instance) -> slice ByteSlice; - } -} diff --git a/src/containers/bitvec.rs b/src/containers/bitvec.rs deleted file mode 100644 index 57e4fd8..0000000 --- a/src/containers/bitvec.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::{containers::ByteSlice, macros::wrap}; -use servicepoint::{ - BinaryOperation, BitVecCommand, CompressionCode, DisplayBitVec, Packet, -}; -use std::ptr::NonNull; - -wrap! { - DisplayBitVec { - derives: crate::containers::derive_container; - functions: - /// Creates a new [DisplayBitVec] instance. - /// - /// # Arguments - /// - /// - `size`: size in bits. - /// - /// returns: [DisplayBitVec] with all bits set to false. - /// - /// # Panics - /// - /// - when `size` is not divisible by 8. - fn new(size: val usize) -> move NonNull { - DisplayBitVec::repeat(false, size) - }; - - /// Interpret the data as a series of bits and load then into a new [DisplayBitVec] instance. - /// - /// returns: [DisplayBitVec] instance containing data. - fn load(data: slice ByteSlice) -> move NonNull { - DisplayBitVec::from_slice(data) - }; - - methods: - /// Creates a [BitVecCommand] and immediately turns that into a [Packet]. - /// - /// The provided [DisplayBitVec] gets consumed. - /// - /// Returns NULL in case of an error. - fn try_into_packet( - move bitvec, - offset: val usize, - operation: val BinaryOperation, - compression: val CompressionCode - ) -> move_ok *mut Packet { - Packet::try_from(BitVecCommand { - bitvec, - offset, - operation, - compression, - }) - }; - - /// Gets the value of a bit. - /// - /// # Arguments - /// - /// - `bit_vec`: instance to read from - /// - `index`: the bit index to read - /// - /// returns: value of the bit - /// - /// # Panics - /// - /// - when accessing `index` out of bounds - fn get(ref instance, index: val usize) -> val bool { - instance.get(index).map(|x| *x).unwrap_or(false) - }; - - /// Sets the value of a bit. - /// - /// # Arguments - /// - /// - `index`: the bit index to edit - /// - `value`: the value to set the bit to - /// - /// # Panics - /// - /// - when accessing `index` out of bounds - fn set(mut instance, index: val usize, value: val bool); - - /// Sets the value of all bits. - /// - /// # Arguments - /// - /// - `value`: the value to set all bits to - fn fill(mut instance, value: val bool); - - /// Gets the length in bits. - fn len(ref instance) -> val usize; - - /// Returns true if length is 0. - fn is_empty(ref instance) -> val bool; - - /// Gets an unsafe reference to the data of the [DisplayBitVec] instance. - /// - /// The returned memory is valid for the lifetime of the bitvec. - fn as_raw_mut_slice(mut instance) -> slice ByteSlice; - } -} diff --git a/src/containers/brightness_grid.rs b/src/containers/brightness_grid.rs deleted file mode 100644 index 3b3fabb..0000000 --- a/src/containers/brightness_grid.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::{containers::ByteSlice, macros::wrap}; -use servicepoint::{ - Brightness, BrightnessGrid, BrightnessGridCommand, ByteGrid, DataRef, Grid, - Origin, Packet, -}; -use std::{mem::transmute, ptr::NonNull}; - -wrap! { - BrightnessGrid { - derives: crate::containers::derive_container, crate::containers::derive_grid[Brightness]; - functions: - /// Creates a new [BrightnessGrid] with the specified dimensions. - /// - /// returns: [BrightnessGrid] initialized to 0. - /// - /// # Examples - /// ```C - /// UdpSocket *connection = sp_udp_open("127.0.0.1:2342"); - /// if (connection == NULL) - /// return 1; - /// - /// BrightnessGrid *grid = sp_brightness_grid_new(2, 2); - /// sp_brightness_grid_set(grid, 0, 0, 0); - /// sp_brightness_grid_set(grid, 1, 1, 10); - /// - /// TypedCommand *command = sp_command_char_brightness(grid); - /// sp_udp_free(connection); - /// ``` - fn new(width: val usize, height: val usize) -> move NonNull { - BrightnessGrid::new(width, height) - }; - - /// Loads a [BrightnessGrid] with the specified dimensions from the provided data. - /// - /// Any out of range values will be set to [Brightness::MAX] or [Brightness::MIN]. - /// - /// returns: new [BrightnessGrid] instance, or NULL in case of an error. - fn load( - width: val usize, - height: val usize, - data: slice ByteSlice, - ) -> move_some *mut BrightnessGrid { - ByteGrid::load(width, height, data) - .map(move |grid| grid.map(Brightness::saturating_from)) - }; - - methods: - /// Creates a [BrightnessGridCommand] and immediately turns that into a [Packet]. - /// - /// The provided [BrightnessGrid] gets consumed. - /// - /// Returns NULL in case of an error. - fn try_into_packet(move grid, x: val usize, y: val usize) -> move_ok *mut Packet { - Packet::try_from(BrightnessGridCommand { - grid, - origin: Origin::new(x, y), - }) - }; - - /// Gets an unsafe reference to the data of the instance. - /// - /// The returned memory is valid for the lifetime of the grid. - fn data_ref_mut(mut instance) -> slice ByteSlice { - //noinspection RsAssertEqual - const _: () = assert!(size_of::() == 1); - - let br_slice = instance.data_ref_mut(); - unsafe { - transmute::<&mut [Brightness], &mut [u8]>(br_slice) - } - }; - } -} diff --git a/src/containers/byte_slice.rs b/src/containers/byte_slice.rs deleted file mode 100644 index 81380e9..0000000 --- a/src/containers/byte_slice.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! FFI slice helper - -/// Represents a span of memory (`&mut [u8]` ) as a struct. -/// -/// # 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. -/// - if `start` is NULL or `length` is 0, do not dereference `start`. -/// -/// # Examples -/// -/// ```c -/// ByteSlice empty = {.start: NULL, .length = 0}; -/// ``` -#[repr(C)] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct ByteSlice { - /// The start address of the memory. - pub start: *mut u8, - /// The amount of memory in bytes. - pub length: usize, -} - -impl ByteSlice { - /// Represents an invalid [ByteSlice] instance. - pub const INVALID: ByteSlice = ByteSlice { - start: std::ptr::null_mut(), - length: 0, - }; - - pub(crate) unsafe fn as_slice(&self) -> &[u8] { - assert!(!self.start.is_null()); - unsafe { std::slice::from_raw_parts(self.start, self.length) } - } - - #[allow( - clippy::mut_from_ref, - reason = "This is used to get a pointer from the C side." - )] - pub(crate) unsafe fn as_slice_mut(&self) -> &mut [u8] { - unsafe { std::slice::from_raw_parts_mut(self.start, self.length) } - } - - pub(crate) unsafe fn from_slice(slice: &mut [u8]) -> Self { - Self { - start: slice.as_mut_ptr(), - length: slice.len(), - } - } -} diff --git a/src/containers/char_grid.rs b/src/containers/char_grid.rs deleted file mode 100644 index 8946b50..0000000 --- a/src/containers/char_grid.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::{containers::ByteSlice, macros::wrap}; -use servicepoint::{CharGrid, CharGridCommand, Grid, Origin, Packet}; -use std::ptr::NonNull; - -wrap! { - CharGrid { - derives: crate::containers::derive_container, crate::containers::derive_get_width_height; - functions: - /// Creates a new [CharGrid] with the specified dimensions. - /// - /// returns: [CharGrid] initialized to 0. - /// - /// # 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); - /// ``` - fn new(width: val usize, height: val usize) -> move NonNull { - CharGrid::new(width, height) - }; - - /// Loads a [CharGrid] with the specified dimensions from the provided data. - /// - /// returns: new CharGrid or NULL in case of an error - fn load(width: val usize, height: val usize, data: slice ByteSlice) -> move_ok *mut CharGrid { - CharGrid::load_utf8(width, height, data.to_vec()) - }; - methods: - /// Returns 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(ref instance, x: val usize, y: val usize) -> val u32 { - instance.get(x, y) as u32 - }; - - /// Sets the value of the specified position in the grid. - /// - /// # 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 - /// - when providing values that cannot be converted to Rust's `char`. - fn set(mut instance, x: val usize, y: val usize, value: val u32) { - instance.set(x, y, char::from_u32(value).unwrap()) - }; - - /// Sets the value of all cells in the grid. - /// - /// # Arguments - /// - /// - `value`: the value to set all cells to - /// - when providing values that cannot be converted to Rust's `char`. - fn fill(mut instance, value: val u32) { - instance.fill(char::from_u32(value).unwrap()) - }; - - /// Creates a [CharGridCommand] and immediately turns that into a [Packet]. - /// - /// The provided [CharGrid] gets consumed. - /// - /// Returns NULL in case of an error. - fn try_into_packet(move grid, x: val usize, y: val usize) -> move_ok *mut Packet { - Packet::try_from(CharGridCommand { - grid, - origin: Origin::new(x, y), - }) - }; - } -} diff --git a/src/containers/cp437_grid.rs b/src/containers/cp437_grid.rs deleted file mode 100644 index b67d1ae..0000000 --- a/src/containers/cp437_grid.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::{containers::ByteSlice, macros::wrap}; -use servicepoint::{ - Cp437Grid, Cp437GridCommand, DataRef, Grid, Origin, Packet, -}; -use std::ptr::NonNull; - -wrap! { - Cp437Grid { - derives: crate::containers::derive_container, crate::containers::derive_grid[u8]; - functions: - /// Creates a new [Cp437Grid] with the specified dimensions. - /// - /// returns: [Cp437Grid] initialized to 0. - fn new(width: val usize, height: val usize) -> move NonNull { - Cp437Grid::new(width, height) - }; - - /// Loads a [Cp437Grid] with the specified dimensions from the provided data. - fn load(width: val usize, height: val usize, data: slice ByteSlice) -> move_some *mut Cp437Grid { - Cp437Grid::load(width, height, data) - }; - - methods: - /// Creates a [Cp437GridCommand] and immediately turns that into a [Packet]. - /// - /// The provided [Cp437Grid] gets consumed. - /// - /// Returns NULL in case of an error. - fn try_into_packet(move grid, x: val usize, y: val usize) -> move_ok *mut Packet { - Packet::try_from(Cp437GridCommand { - grid, - origin: Origin::new(x, y), - }) - }; - - /// Gets an unsafe reference to the data of the grid. - /// - /// The returned memory is valid for the lifetime of the instance. - fn data_ref_mut(mut instance) -> slice ByteSlice; - } -} diff --git a/src/containers/mod.rs b/src/containers/mod.rs deleted file mode 100644 index 5e0b48d..0000000 --- a/src/containers/mod.rs +++ /dev/null @@ -1,77 +0,0 @@ -mod bitmap; -mod bitvec; -mod brightness_grid; -mod byte_slice; -mod char_grid; -mod cp437_grid; - -pub use bitmap::*; -pub use bitvec::*; -pub use brightness_grid::*; -pub use byte_slice::*; -pub use char_grid::*; -pub use cp437_grid::*; - -macro_rules! derive_container { - ($object_type:ident) => { - $crate::macros::derive_clone!($object_type); - $crate::macros::derive_free!($object_type); - }; -} - -macro_rules! derive_get_width_height { - ($object_type:ident) => { - $crate::macros::wrap_methods! {$object_type; - /// Gets the width. - fn width(ref instance) -> val usize; - - /// Gets the height. - fn height(ref instance) -> val usize; - } - }; -} - -macro_rules! derive_grid { - ($object_type:ident, $value_type:ident) => { - $crate::containers::derive_get_width_height!($object_type); - $crate::macros::wrap_methods! {$object_type; - /// 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(ref instance, x: val usize, y: val usize) -> val $value_type; - - /// Sets the value of the specified position. - /// - /// # 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 instance, x: val usize, y: val usize, value: val $value_type); - - /// Sets the state of all cells in the grid. - /// - /// # Arguments - /// - /// - `value`: the value to set all cells to - fn fill(mut instance, value: val $value_type); - } - }; -} - -pub(crate) use {derive_container, derive_get_width_height, derive_grid}; - -mod _hidden { - /// This is a type only used by cbindgen to have a type for pointers. - #[allow(unused)] - pub struct DisplayBitVec; -} diff --git a/src/cp437_grid.rs b/src/cp437_grid.rs new file mode 100644 index 0000000..ff70d2b --- /dev/null +++ b/src/cp437_grid.rs @@ -0,0 +1,157 @@ +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; +use servicepoint::{ + Cp437Grid, Cp437GridCommand, DataRef, Grid, Origin, Packet, +}; +use std::ptr::NonNull; + +/// Creates a new [Cp437Grid] with the specified dimensions. +/// +/// returns: [Cp437Grid] initialized to 0. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_new( + width: usize, + height: usize, +) -> NonNull { + heap_move_nonnull(Cp437Grid::new(width, height)) +} + +/// Loads a [Cp437Grid] with the specified dimensions from the provided data. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_load( + width: usize, + height: usize, + data: ByteSlice, +) -> *mut Cp437Grid { + let data = unsafe { data.as_slice() }; + let grid = Cp437Grid::load(width, height, data); + if let Some(grid) = grid { + heap_move(grid) + } else { + std::ptr::null_mut() + } +} + +/// Clones a [Cp437Grid]. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_clone( + cp437_grid: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { cp437_grid.as_ref().clone() }) +} + +/// Deallocates a [Cp437Grid]. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: NonNull) { + unsafe { heap_drop(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 accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_get( + cp437_grid: NonNull, + x: usize, + y: usize, +) -> u8 { + unsafe { cp437_grid.as_ref().get(x, y) } +} + +/// Sets the value of the specified position in the [Cp437Grid]. +/// +/// # 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 accessing `x` or `y` out of bounds +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_set( + cp437_grid: NonNull, + x: usize, + y: usize, + value: u8, +) { + unsafe { (*cp437_grid.as_ptr()).set(x, y, value) }; +} + +/// Sets the value of all cells in the [Cp437Grid]. +/// +/// # Arguments +/// +/// - `cp437_grid`: instance to write to +/// - `value`: the value to set all cells to +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_fill( + cp437_grid: NonNull, + value: u8, +) { + unsafe { (*cp437_grid.as_ptr()).fill(value) }; +} + +/// Gets the width of the [Cp437Grid] instance. +/// +/// # Arguments +/// +/// - `cp437_grid`: instance to read from +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_width( + cp437_grid: NonNull, +) -> usize { + unsafe { cp437_grid.as_ref().width() } +} + +/// Gets the height of the [Cp437Grid] instance. +/// +/// # Arguments +/// +/// - `cp437_grid`: instance to read from +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_height( + cp437_grid: NonNull, +) -> usize { + unsafe { cp437_grid.as_ref().height() } +} + +/// Gets an unsafe reference to the data of the [Cp437Grid] instance. +/// +/// The returned memory is valid for the lifetime of the grid. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( + cp437_grid: NonNull, +) -> ByteSlice { + unsafe { ByteSlice::from_slice((*cp437_grid.as_ptr()).data_ref_mut()) } +} + +/// Creates a [Cp437GridCommand] and immediately turns that into a [Packet]. +/// +/// The provided [Cp437Grid] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_cp437_grid_into_packet( + grid: NonNull, + x: usize, + y: usize, +) -> *mut Packet { + let grid = unsafe { heap_remove(grid) }; + match Packet::try_from(Cp437GridCommand { + grid, + origin: Origin::new(x, y), + }) { + Ok(packet) => heap_move(packet), + Err(_) => std::ptr::null_mut(), + } +} diff --git a/src/lib.rs b/src/lib.rs index f884a83..60c42ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,14 @@ //! //! # Examples //! +//! Make sure to check out [this GitHub repo](https://github.com/arfst23/ServicePoint) as well! +//! //! ```C //! #include //! #include "servicepoint.h" //! //! int main(void) { -//! UdpSocket *connection = sp_udp_open("172.23.42.29:2342"); +//! UdpConnection *connection = sp_udp_open("172.23.42.29:2342"); //! if (connection == NULL) //! return 1; //! @@ -22,32 +24,49 @@ //! return 0; //! } //! ``` -//! -//! There are more examples in the source repository. -/// Functions related to commands. -pub mod commands; -/// Functions related to [servicepoint::Bitmap], [servicepoint::CharGrid] and friends. -pub mod containers; -pub(crate) mod macros; -pub(crate) mod mem; -/// Functions related to [servicepoint::Packet]. -pub mod packet; -/// Functions related to [servicepoint::UdpSocketExt]. -pub mod udp; +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::cp437_grid::*; +pub use crate::packet::*; +pub use crate::typed_command::*; +pub use crate::udp::*; +pub use servicepoint::CommandCode; +use std::ptr::NonNull; + +mod bitmap; +mod bitvec; +mod brightness_grid; +mod byte_slice; +mod char_grid; +mod cp437_grid; +mod packet; +mod typed_command; +mod udp; + +use std::time::Duration; /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets. -pub const SP_FRAME_PACING_MS: u128 = 30; +pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); -#[cfg(feature = "env_logger")] -mod feature_env_logger { - use crate::macros::wrap_functions; - - wrap_functions!(envlogger; - /// Call this function at the beginning of main to enable rust logging controlled by the - /// `RUST_LOG` environment variable. See [env_logger](https://docs.rs/env_logger/latest/env_logger/). - fn init() { - env_logger::init(); - }; - ); +pub(crate) fn heap_move(x: T) -> *mut T { + Box::into_raw(Box::new(x)) } + +pub(crate) fn heap_move_nonnull(x: T) -> NonNull { + NonNull::from(Box::leak(Box::new(x))) +} + +pub(crate) unsafe fn heap_drop(x: NonNull) { + drop(unsafe { heap_remove(x) }); +} + +pub(crate) unsafe fn heap_remove(x: NonNull) -> T { + unsafe { *Box::from_raw(x.as_ptr()) } +} + +/// This is a type only used by cbindgen to have a type for pointers. +pub struct UdpSocket; diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 89d3369..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,326 +0,0 @@ -macro_rules! derive_free { - ($object_type:ident) => { - $crate::macros::wrap_method!($object_type; - #[doc = concat!("Deallocates a [`", stringify!($object_type), "`] instance.")] - #[allow(dropping_copy_types)] - fn free(move instance) { - ::std::mem::drop(instance) - }; - ); - }; -} - -macro_rules! derive_clone { - ($object_type:ident) => { - $crate::macros::wrap_method!($object_type; - #[doc = concat!("Clones a [`", stringify!($object_type), "`] instance.")] - fn clone(ref instance) -> move ::core::ptr::NonNull<$object_type>; - ); - }; -} - -macro_rules! wrap_method { - ( - $object_type:ident; - $(#[$meta:meta])+ - fn $function:ident($ref_or_mut:ident $instance:ident $(, $($param_name:ident: $param_modifier:ident $param_type:ty),*)?) - $(-> $return_modifier:ident $return_type:ty)?; - ) => { - ::paste::paste!{ - $crate::macros::wrap_method!( - $object_type; - #[doc = " Calls method [`" $object_type "::" $function "`]."] - /// - $(#[$meta])+ - fn $function($ref_or_mut $instance $(, $($param_name: $param_modifier $param_type),*)?) - $(-> $return_modifier $return_type)? { - $instance.$function($($($param_name),*)?) - }; - ); - } - }; - ($object_type:ident; - $(#[$meta:meta])+ - fn $function:ident($ref_or_mut:ident $instance:ident $(, $($param_name:ident: $param_modifier:ident $param_type:ty),*)?) - $(-> $return_modifier:ident $return_type:ty)? - $impl:block; - ) => { - paste::paste! { - $crate::macros::wrap_functions!(associate $object_type; - $(#[$meta])* - fn $function( - $instance: $ref_or_mut ::core::ptr::NonNull<$object_type> - $(,$($param_name: $param_modifier $param_type),*)? - ) $(-> $return_modifier $return_type)? - $impl; - ); - } - }; -} - -macro_rules! wrap_methods { - ( - $object_type:ident; - $( - $(#[$meta:meta])+ - fn $function:ident($ref_or_mut:ident $instance:ident $(, $($param_name:ident: $param_modifier:ident $param_type:ty),*)?) - $(-> $return_modifier:ident $return_type:ty)? - $($impl:block)?; - )+ - ) => { - paste::paste! { - $( - $crate::macros::wrap_method!($object_type; - $(#[$meta])* - fn $function($ref_or_mut $instance $(, $($param_name: $param_modifier $param_type),*)?) - $(-> $return_modifier $return_type)? - $($impl)?; - ); - )+ - } - }; -} - -macro_rules! wrap_fields_accessor { - (get; $object_type:ident :: $prop_name:ident : $prop_type:ty) => { - paste::paste! { - $crate::macros::wrap_method! {$object_type; - #[doc = " Gets the value of field `" $prop_name - "` of the [`servicepoint::" $object_type "`]."] - fn [](ref instance) -> val $prop_type { - instance.$prop_name - }; - } - } - }; - (mut get; $object_type:ident :: $prop_name:ident : $prop_type:ty) => { - paste::paste! { - $crate::macros::wrap_method! {$object_type; - #[doc = " Gets a reference to the field `" $prop_name - "` of the [`servicepoint::" $object_type "`]."] - /// - /// - The returned reference inherits the lifetime of object in which it is contained. - /// - The returned pointer may not be used in a function that consumes the instance, e.g. to create a command. - fn [](mut instance) -> val ::core::ptr::NonNull<$prop_type> { - ::core::ptr::NonNull::from(&mut instance.$prop_name) - }; - } - } - }; - (set; $object_type:ident :: $prop_name:ident : $prop_type:ty) => { - paste::paste! { - $crate::macros::wrap_method! {$object_type; - #[doc = " Sets the value of field `" $prop_name - "` of the [`servicepoint::" $object_type "`]."] - fn [](mut instance, value: val $prop_type) { - instance.$prop_name = value; - }; - } - } - }; - (move set; $object_type:ident :: $prop_name:ident : $prop_type:ty) => { - paste::paste! { - $crate::macros::wrap_method! {$object_type; - #[doc = " Sets the value of field `" $prop_name - "` of the [`servicepoint::" $object_type "`]."] - /// The provided value is moved into the instance, potentially invalidating previously taken references. - fn [](mut instance, value: move ::core::ptr::NonNull<$prop_type>) { - instance.$prop_name = value; - }; - } - } - }; -} - -macro_rules! wrap_fields { - ( - $object_type:ident; - $( - prop $prop_name:ident : $prop_type:ty { $($accessor:ident $($modifier:ident)?;)+ }; - )+ - ) => { - $($( - ::paste::paste!{ - $crate::macros::wrap_fields_accessor! { - $($modifier)? $accessor; - $object_type :: $prop_name: $prop_type - } - } - )+)+ - }; -} - -macro_rules! apply_param_modifier { - (move, $param_name:ident) => { - unsafe { $crate::mem::heap_remove($param_name) } - }; - (val, $param_name:ident) => { - $param_name - }; - (mut, $param_name:ident) => { - unsafe { (&mut *$param_name.as_ptr()) } - }; - (ref, $param_name:ident) => { - unsafe { $param_name.as_ref() } - }; - (slice, $param_name:ident) => { - unsafe { $param_name.as_slice() } - }; -} - -macro_rules! apply_return_modifier { - (val, $result:ident) => { - $result - }; - (move, $result:ident) => { - $crate::mem::heap_move_nonnull($result) - }; - (move_ok, $result:ident) => { - $crate::mem::heap_move_ok($result) - }; - (move_some, $result:ident) => { - $crate::mem::heap_move_some($result) - }; - (slice, $result:ident) => { - unsafe { ByteSlice::from_slice($result) } - }; -} - -macro_rules! wrap_function { - ( - $module:ident; - $(#[$meta:meta])+ - fn $function:ident($($param_name:ident: $param_modifier:ident $param_type:ty),*$(,)?) - $(-> $return_modifier:ident $return_type:ty)? - $block:block; - ) => { - ::paste::paste! { - $(#[$meta])+ - #[doc = ""] - #[doc = " This function is part of the `" $module "` module."] - #[no_mangle] - pub unsafe extern "C" fn [< sp_ $module _ $function >]( - $($param_name: $param_type),* - ) $(-> $return_type)? - { - $( - let $param_name = $crate::macros::apply_param_modifier!($param_modifier, $param_name); - )* - let result = $block; - $( - let result = $crate::macros::apply_return_modifier!($return_modifier, result); - )? - result - } - } - }; -} - -macro_rules! wrap_functions { - ( - $module:ident; - $( - $(#[$meta:meta])+ - fn $function:ident($($param_name:ident: $param_modifier:ident $param_type:ty),*$(,)?) - $(-> $return_modifier:ident $return_type:ty)? - $block:block; - )+ - ) => { - ::paste::paste! { - $( - $crate::macros::wrap_function!($module; - $(#[$meta])+ - fn $function($($param_name: $param_modifier $param_type),*) $(-> $return_modifier $return_type)? - $block; - ); - )+ - } - }; - ( - associate $object_type:ident; - $( - $(#[$meta:meta])+ - fn $function:ident($($param_name:ident: $param_modifier:ident $param_type:ty),*$(,)?) - $(-> $return_modifier:ident $return_type:ty)? - $block:block; - )+ - ) => { - ::paste::paste! { - $crate::macros::wrap_functions!{[< $object_type:snake >]; - $( - $(#[$meta])+ - fn $function($($param_name: $param_modifier $param_type),*) $(-> $return_modifier $return_type)? - $block; - )+ - } - } - }; -} - -macro_rules! wrap { - ( - $object_type:ident { - $( - derives: - $( - $derive:path $( [ $( $derive_arg:tt ),+ ] )? - ),+; - )? - $( - properties: - $( - prop $prop_name:ident : $prop_type:ty { $($accessor:ident $($modifier:ident)?;)+ }; - )* - )? - $( - functions: - $( - $(#[$fn_meta:meta])+ - fn $fn_name:ident($($fn_param_name:ident: $fn_param_modifier:ident $fn_param_type:ty),*$(,)?) - $(-> $fn_return_modifier:ident $fn_return_type:ty)? - $fn_block:block; - )* - )? - $( - methods: - $( - $(#[$method_meta:meta])+ - fn $method_name:ident($method_instance_modifier:ident $method_instance:ident $(, $($method_param_name:ident: $method_param_modifier:ident $method_param_type:ty),*)?) - $(-> $method_return_modifier:ident $method_return_type:ty)? - $($method_impl:block)?; - )* - )? - } - ) => { - $($( - $derive!($object_type $(, $($derive_arg),+)?); - )+)? - $($( - $crate::macros::wrap_fields!($object_type; - prop $prop_name : $prop_type { $($accessor $($modifier)?;)+ }; - ); - )*)? - $($( - $crate::macros::wrap_functions!(associate $object_type; - $(#[$fn_meta])+ - fn $fn_name($($fn_param_name: $fn_param_modifier $fn_param_type),*) - $(-> $fn_return_modifier $fn_return_type)? - $fn_block; - ); - )*)? - $($( - $crate::macros::wrap_method!($object_type; - $(#[$method_meta])+ - fn $method_name($method_instance_modifier $method_instance $(, $($method_param_name: $method_param_modifier $method_param_type),*)?) - $(-> $method_return_modifier $method_return_type)? - $($method_impl)?; - ); - )*)? - }; -} - -pub(crate) use { - apply_param_modifier, apply_return_modifier, derive_clone, derive_free, - wrap, wrap_fields, wrap_fields_accessor, wrap_function, wrap_functions, - wrap_method, wrap_methods, -}; diff --git a/src/mem.rs b/src/mem.rs deleted file mode 100644 index 6555999..0000000 --- a/src/mem.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::ptr::NonNull; - -pub(crate) fn heap_move(x: T) -> *mut T { - Box::into_raw(Box::new(x)) -} - -pub(crate) fn heap_move_nonnull(x: T) -> NonNull { - NonNull::from(Box::leak(Box::new(x))) -} - -pub(crate) fn heap_move_ok(x: Result) -> *mut T { - x.map(|x| heap_move(x)).unwrap_or(std::ptr::null_mut()) -} - -pub(crate) fn heap_move_some(x: Option) -> *mut T { - x.map(|x| heap_move(x)).unwrap_or(std::ptr::null_mut()) -} - -pub(crate) unsafe fn heap_drop(x: NonNull) { - drop(unsafe { heap_remove(x) }); -} - -pub(crate) unsafe fn heap_remove(x: NonNull) -> T { - unsafe { *Box::from_raw(x.as_ptr()) } -} - -pub(crate) unsafe fn heap_clone(source: NonNull) -> NonNull { - heap_move_nonnull(unsafe { source.as_ref().clone() }) -} diff --git a/src/packet.rs b/src/packet.rs index 00a34a9..d8b1579 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,85 +1,128 @@ -use crate::{ - containers::ByteSlice, - macros::{derive_clone, derive_free, wrap, wrap_functions}, -}; -use servicepoint::{CommandCode, Header, Packet}; +use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; +use servicepoint::{CommandCode, Header, Packet, TypedCommand}; use std::ptr::NonNull; -derive_clone!(Packet); -derive_free!(Packet); - -wrap! { - Packet { - properties: - prop header: Header { get; get mut; set; }; - functions: - /// Tries to load a [Packet] from the passed array with the specified length. - /// - /// returns: NULL in case of an error, pointer to the allocated packet otherwise - fn try_load(data: slice ByteSlice) -> move_ok *mut Packet { - servicepoint::Packet::try_from(data) - }; - - /// Creates a raw [Packet] from parts. - /// - /// returns: new instance. Will never return null. - fn from_parts(header: val Header, payload: val ByteSlice) -> move NonNull { - let payload = if payload == ByteSlice::INVALID { - None - } else { - Some(Vec::from(unsafe { payload.as_slice() })) - }; - - Packet { header, payload } - }; - methods: - /// Returns a pointer to the current payload of the provided packet. - /// - /// Returns an [ByteSlice::INVALID] instance in case the packet does not have any payload. - /// - /// The returned memory can be changed and will be valid until a new payload is set. - fn get_payload(mut packet) -> val ByteSlice { - match &mut packet.payload { - None => ByteSlice::INVALID, - Some(payload) => unsafe { ByteSlice::from_slice(payload) }, - } - }; - - /// Sets the payload of the provided packet to the provided data. - /// - /// This makes previous payload pointers invalid. - fn set_payload(mut packet, data: val ByteSlice) { - packet.payload = if data == ByteSlice::INVALID { - None - } else { - Some(unsafe { data.as_slice().to_vec() }) - } - }; - - /// Serialize the packet into the provided buffer. - /// - /// # Panics - /// - /// - if the buffer is not big enough to hold header+payload. - fn serialize_to(mut packet, buffer: val ByteSlice) -> val usize { - unsafe { - packet.serialize_to(buffer.as_slice_mut()).unwrap_or(0) - } - }; +/// Turns a [TypedCommand] into a [Packet]. +/// The [TypedCommand] gets consumed. +/// +/// Returns NULL in case of an error. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_from_command( + command: NonNull, +) -> *mut Packet { + let command = unsafe { heap_remove(command) }; + if let Ok(packet) = command.try_into() { + heap_move(packet) + } else { + std::ptr::null_mut() } } -wrap_functions!(sp; - /// Converts u16 into [CommandCode]. - /// - /// If the provided value is not valid, false is returned and result is not changed. - fn u16_to_command_code(code: val u16, result: mut NonNull) -> val bool { - match CommandCode::try_from(code) { - Ok(code) => { - *result = code; - true - } - Err(_) => false, - } +/// Tries to load a [Packet] from the passed array with the specified length. +/// +/// returns: NULL in case of an error, pointer to the allocated packet otherwise +#[no_mangle] +pub unsafe extern "C" fn sp_packet_try_load(data: ByteSlice) -> *mut Packet { + let data = unsafe { data.as_slice() }; + match servicepoint::Packet::try_from(data) { + Err(_) => std::ptr::null_mut(), + Ok(packet) => heap_move(packet), + } +} + +/// Creates a raw [Packet] from parts. +/// +/// returns: new instance. Will never return null. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_from_parts( + header: Header, + payload: *const ByteSlice, +) -> NonNull { + let payload = if payload.is_null() { + vec![] + } else { + let payload = unsafe { (*payload).as_slice() }; + Vec::from(payload) }; -); + + heap_move_nonnull(Packet { header, payload }) +} + +/// Returns a pointer to the header field of the provided packet. +/// +/// The returned header can be changed and will be valid for the lifetime of the packet. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_get_header( + packet: NonNull, +) -> NonNull
{ + NonNull::from(&mut unsafe { (*packet.as_ptr()).header }) +} + +/// Returns a pointer to the current payload of the provided packet. +/// +/// The returned memory can be changed and will be valid until a new payload is set. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_get_payload( + packet: NonNull, +) -> ByteSlice { + unsafe { ByteSlice::from_slice(&mut (*packet.as_ptr()).payload) } +} + +/// Sets the payload of the provided packet to the provided data. +/// +/// This makes previous payload pointers invalid. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_set_payload( + packet: NonNull, + data: ByteSlice, +) { + unsafe { (*packet.as_ptr()).payload = data.as_slice().to_vec() } +} + +/// Serialize the packet into the provided buffer. +/// +/// # Panics +/// +/// - if the buffer is not big enough to hold header+payload. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_serialize_to( + packet: NonNull, + mut buffer: ByteSlice, +) { + unsafe { + packet.as_ref().serialize_to(buffer.as_slice_mut()); + } +} + +/// Clones a [Packet]. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_clone( + packet: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { packet.as_ref().clone() }) +} + +/// Deallocates a [Packet]. +#[no_mangle] +pub unsafe extern "C" fn sp_packet_free(packet: NonNull) { + unsafe { heap_drop(packet) } +} + +/// Converts u16 into [CommandCode]. +/// +/// If the provided value is not valid, false is returned and result is not changed. +#[no_mangle] +pub unsafe extern "C" fn sp_u16_to_command_code( + code: u16, + result: *mut CommandCode, +) -> bool { + match CommandCode::try_from(code) { + Ok(code) => { + unsafe { + *result = code; + } + true + } + Err(_) => false, + } +} diff --git a/src/typed_command.rs b/src/typed_command.rs new file mode 100644 index 0000000..9e27540 --- /dev/null +++ b/src/typed_command.rs @@ -0,0 +1,201 @@ +use crate::{heap_drop, heap_move, heap_move_nonnull, SPBitVec}; +use servicepoint::{ + BinaryOperation, Bitmap, Brightness, BrightnessGrid, CharGrid, + CompressionCode, Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand, +}; +use std::ptr::NonNull; + +/// Tries to turn a [Packet] into a [TypedCommand]. +/// +/// The packet is deallocated in the process. +/// +/// Returns: pointer to new [TypedCommand] instance or NULL if parsing failed. +#[no_mangle] +pub unsafe extern "C" fn sp_command_try_from_packet( + packet: NonNull, +) -> *mut TypedCommand { + let packet = *unsafe { Box::from_raw(packet.as_ptr()) }; + match servicepoint::TypedCommand::try_from(packet) { + Err(_) => std::ptr::null_mut(), + Ok(command) => heap_move(command), + } +} + +/// Clones a [TypedCommand] instance. +/// +/// returns: new [TypedCommand] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_clone( + command: NonNull, +) -> NonNull { + heap_move_nonnull(unsafe { command.as_ref().clone() }) +} + +/// Set all pixels to the off state. +/// +/// Does not affect brightness. +/// +/// Returns: a new [servicepoint::Command::Clear] instance. +/// +/// # Examples +/// +/// ```C +/// sp_udp_send_command(connection, sp_command_clear()); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_command_clear() -> NonNull { + heap_move_nonnull(servicepoint::ClearCommand.into()) +} + +/// 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. +#[no_mangle] +pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull { + heap_move_nonnull(servicepoint::HardResetCommand.into()) +} + +/// A yet-to-be-tested command. +/// +/// Returns: a new [servicepoint::Command::FadeOut] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_fade_out() -> NonNull { + heap_move_nonnull(servicepoint::FadeOutCommand.into()) +} + +/// Set the brightness of all tiles to the same value. +/// +/// Returns: a new [servicepoint::Command::Brightness] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_global_brightness( + brightness: Brightness, +) -> NonNull { + heap_move_nonnull(GlobalBrightnessCommand::from(brightness).into()) +} + +/// Set the brightness of individual tiles in a rectangular area of the display. +/// +/// The passed [BrightnessGrid] gets consumed. +/// +/// Returns: a new [servicepoint::Command::CharBrightness] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_brightness_grid( + x: usize, + y: usize, + grid: NonNull, +) -> NonNull { + let grid = unsafe { *Box::from_raw(grid.as_ptr()) }; + let result = servicepoint::BrightnessGridCommand { + origin: servicepoint::Origin::new(x, y), + grid, + } + .into(); + heap_move_nonnull(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 [`BinaryOperation`] will be applied on the display comparing old and sent bit. +/// +/// `new_bit = old_bit op sent_bit` +/// +/// For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels. +/// +/// The contained [`BitVecU8Msb0`] is always uncompressed. +#[no_mangle] +pub unsafe extern "C" fn sp_command_bitvec( + offset: usize, + bit_vec: NonNull, + compression: CompressionCode, + operation: BinaryOperation, +) -> *mut TypedCommand { + let bit_vec = unsafe { *Box::from_raw(bit_vec.as_ptr()) }; + let command = servicepoint::BitVecCommand { + offset, + operation, + bitvec: bit_vec.0, + compression, + } + .into(); + heap_move(command) +} + +/// Show codepage 437 encoded text on the screen. +/// +/// The passed [Cp437Grid] gets consumed. +/// +/// Returns: a new [servicepoint::Cp437GridCommand] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_cp437_grid( + x: usize, + y: usize, + grid: NonNull, +) -> NonNull { + let grid = *unsafe { Box::from_raw(grid.as_ptr()) }; + let result = servicepoint::Cp437GridCommand { + origin: servicepoint::Origin::new(x, y), + grid, + } + .into(); + heap_move_nonnull(result) +} + +/// Show UTF-8 encoded text on the screen. +/// +/// The passed [CharGrid] gets consumed. +/// +/// Returns: a new [servicepoint::CharGridCommand] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_char_grid( + x: usize, + y: usize, + grid: NonNull, +) -> NonNull { + let grid = unsafe { *Box::from_raw(grid.as_ptr()) }; + let result = servicepoint::CharGridCommand { + origin: servicepoint::Origin::new(x, y), + grid, + } + .into(); + heap_move_nonnull(result) +} + +/// Sets a window of pixels to the specified values. +/// +/// The passed [Bitmap] gets consumed. +/// +/// Returns: a new [servicepoint::BitmapCommand] instance. +#[no_mangle] +pub unsafe extern "C" fn sp_command_bitmap( + x: usize, + y: usize, + bitmap: NonNull, + compression: CompressionCode, +) -> *mut TypedCommand { + let bitmap = unsafe { *Box::from_raw(bitmap.as_ptr()) }; + let command = servicepoint::BitmapCommand { + origin: servicepoint::Origin::new(x, y), + bitmap, + compression, + } + .into(); + heap_move(command) +} + +/// Deallocates a [TypedCommand]. +/// +/// # Examples +/// +/// ```C +/// TypedCommand c = sp_command_clear(); +/// sp_command_free(c); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_command_free(command: NonNull) { + unsafe { heap_drop(command) } +} diff --git a/src/udp.rs b/src/udp.rs index 22a7f7c..f68aa16 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -1,117 +1,119 @@ -use crate::{ - commands::{CommandTag, GenericCommand}, - macros::{derive_free, wrap}, - mem::heap_remove, -}; -use servicepoint::{Header, Packet, UdpSocketExt}; -use std::{ - ffi::{c_char, CStr}, - net::{Ipv4Addr, SocketAddrV4, UdpSocket}, - ptr::NonNull, -}; +use crate::{heap_drop, heap_move, heap_remove}; +use servicepoint::{Header, Packet, TypedCommand, UdpSocketExt}; +use std::ffi::{c_char, CStr}; +use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket}; +use std::ptr::NonNull; -derive_free!(UdpSocket); +/// Creates a new instance of [UdpConnection]. +/// +/// returns: NULL if connection fails, or connected instance +/// +/// # Examples +/// +/// ```C +/// UdpConnection connection = sp_udp_open("172.23.42.29:2342"); +/// if (connection != NULL) +/// sp_udp_send_command(connection, sp_command_clear()); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_udp_open(host: NonNull) -> *mut UdpSocket { + let host = unsafe { CStr::from_ptr(host.as_ptr()) } + .to_str() + .expect("Bad encoding"); + let connection = match UdpSocket::bind_connect(host) { + Err(_) => return std::ptr::null_mut(), + Ok(value) => value, + }; -wrap! { - UdpSocket { - functions: - /// Creates a new instance of [UdpSocket]. - /// - /// returns: NULL if connection fails, or connected instance - /// - /// # Examples - /// - /// ```C - /// UdpSocket connection = sp_udp_open("172.23.42.29:2342"); - /// if (connection != NULL) - /// sp_udp_send_command(connection, sp_command_clear()); - /// ``` - fn open(host: val NonNull) -> move_ok *mut UdpSocket { - let host = unsafe { CStr::from_ptr(host.as_ptr()) } - .to_str() - .expect("Bad encoding"); - UdpSocket::bind_connect(host) - }; - - /// Creates a new instance of [UdpSocket]. - /// - /// returns: NULL if connection fails, or connected instance - /// - /// # Examples - /// - /// ```C - /// UdpSocket connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); - /// if (connection != NULL) - /// sp_udp_send_command(connection, sp_command_clear()); - /// ``` - fn open_ipv4(ip1: val u8, ip2: val u8, ip3: val u8, ip4: val u8, port: val u16) -> move_ok *mut UdpSocket { - let addr = SocketAddrV4::new(Ipv4Addr::from([ip1, ip2, ip3, ip4]), port); - UdpSocket::bind_connect(addr) - }; - methods: - /// Sends a [Packet] to the display using the [UdpSocket]. - /// - /// The passed `packet` gets consumed. - /// - /// returns: true in case of success - fn send_packet(ref connection, packet: move NonNull) -> val bool { - connection.send(&Vec::from(packet)).is_ok() - }; - - /// Sends a [GenericCommand] to the display using the [UdpSocket]. - /// - /// The passed `command` gets consumed. - /// - /// returns: true in case of success - /// - /// # Examples - /// - /// ```C - /// sp_udp_send_command(connection, sp_command_brightness(5)); - /// ``` - fn send_command(ref connection, command: mut NonNull) -> val bool { - unsafe { - let result = match command.tag { - CommandTag::Invalid => return false, - CommandTag::Bitmap => connection.send_command(heap_remove(command.data.bitmap)), - CommandTag::BitVec => connection.send_command(heap_remove(command.data.bit_vec)), - CommandTag::BrightnessGrid => connection.send_command(heap_remove(command.data.brightness_grid)), - CommandTag::CharGrid => connection.send_command(heap_remove(command.data.char_grid)), - CommandTag::Cp437Grid => connection.send_command(heap_remove(command.data.cp437_grid)), - CommandTag::GlobalBrightness => connection.send_command(heap_remove(command.data.global_brightness)), - CommandTag::Clear => connection.send_command(heap_remove(command.data.clear)), - CommandTag::HardReset => connection.send_command(heap_remove(command.data.hard_reset)), - CommandTag::FadeOut => connection.send_command(heap_remove(command.data.fade_out)), - CommandTag::BitmapLegacy => connection.send_command(heap_remove(command.data.bitmap_legacy)), - }.is_some(); - *command = GenericCommand::INVALID; - result - } - }; - - /// Sends a [Header] to the display using the [UdpSocket]. - /// - /// returns: true in case of success - /// - /// # Examples - /// - /// ```C - /// sp_udp_send_header(connection, sp_command_brightness(5)); - /// ``` - fn send_header(ref udp_connection, header: val Header) -> val bool { - let packet = Packet { - header, - payload: None, - }; - udp_connection.send(&Vec::from(packet)).is_ok() - }; - } + heap_move(connection) } -mod _hidden { - /// This is a type only used by cbindgen to have a type for pointers. - /// - /// See [servicepoint::UdpSocketExt]. - #[allow(unused)] - pub struct UdpSocket; +/// Creates a new instance of [UdpConnection]. +/// +/// returns: NULL if connection fails, or connected instance +/// +/// # Examples +/// +/// ```C +/// UdpConnection connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342); +/// if (connection != NULL) +/// sp_udp_send_command(connection, sp_command_clear()); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_udp_open_ipv4( + ip1: u8, + ip2: u8, + ip3: u8, + ip4: u8, + port: u16, +) -> *mut UdpSocket { + let addr = SocketAddrV4::new(Ipv4Addr::from([ip1, ip2, ip3, ip4]), port); + let connection = match UdpSocket::bind_connect(addr) { + Err(_) => return std::ptr::null_mut(), + Ok(value) => value, + }; + heap_move(connection) +} + +/// Sends a [Packet] to the display using the [UdpConnection]. +/// +/// The passed `packet` gets consumed. +/// +/// returns: true in case of success +#[no_mangle] +pub unsafe extern "C" fn sp_udp_send_packet( + connection: NonNull, + packet: NonNull, +) -> bool { + let packet = unsafe { heap_remove(packet) }; + unsafe { connection.as_ref().send(&Vec::from(packet)) }.is_ok() +} + +/// Sends a [TypedCommand] to the display using the [UdpConnection]. +/// +/// The passed `command` gets consumed. +/// +/// returns: true in case of success +/// +/// # Examples +/// +/// ```C +/// sp_udp_send_command(connection, sp_command_brightness(5)); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_udp_send_command( + connection: NonNull, + command: NonNull, +) -> bool { + let command = unsafe { heap_remove(command) }; + unsafe { connection.as_ref().send_command(command) }.is_some() +} + +/// Sends a [Header] to the display using the [UdpConnection]. +/// +/// returns: true in case of success +/// +/// # Examples +/// +/// ```C +/// sp_udp_send_header(connection, sp_command_brightness(5)); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn sp_udp_send_header( + udp_connection: NonNull, + header: Header, +) -> bool { + let packet = Packet { + header, + payload: vec![], + }; + unsafe { udp_connection.as_ref() } + .send(&Vec::from(packet)) + .is_ok() +} + +/// Closes and deallocates a [UdpConnection]. +#[no_mangle] +pub unsafe extern "C" fn sp_udp_free(connection: NonNull) { + unsafe { heap_drop(connection) } }