type per command, binding generation macros, nix package #4
					 53 changed files with 4160 additions and 2374 deletions
				
			
		
							
								
								
									
										14
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -30,15 +30,10 @@ 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 clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=release | ||||
|         run: cd example && make -r clean-all && make -r LIBC=gnu LINK=dynamic PROFILE=release | ||||
|       - name: build example -- glibc debug | ||||
|         run: cd example && make clean && make TARGET=aarch64-unknown-linux-gnu PROFILE=debug | ||||
|         run: cd example && make -r clean-all && make -r LIBC=gnu LINK=dynamic PROFILE=debug | ||||
| 
 | ||||
|   build-size-gnu-unstable: | ||||
|     runs-on: ubuntu-latest | ||||
|  | @ -52,5 +47,6 @@ 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 && make TARGET=aarch64-unknown-linux-gnu PROFILE=size-optimized CARGO="rustup run nightly cargo" LTO=1 | ||||
|       - 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 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,4 +4,5 @@ out | |||
| .direnv | ||||
| .envrc | ||||
| result | ||||
| mutants.* | ||||
| mutants.* | ||||
| _out_* | ||||
							
								
								
									
										382
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										382
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -4,15 +4,24 @@ version = 4 | |||
| 
 | ||||
| [[package]] | ||||
| name = "adler2" | ||||
| version = "2.0.0" | ||||
| version = "2.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" | ||||
| checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "aho-corasick" | ||||
| version = "1.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstream" | ||||
| version = "0.6.18" | ||||
| version = "0.6.19" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" | ||||
| checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "anstyle-parse", | ||||
|  | @ -25,44 +34,44 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle" | ||||
| version = "1.0.10" | ||||
| version = "1.0.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" | ||||
| checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-parse" | ||||
| version = "0.2.6" | ||||
| version = "0.2.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" | ||||
| checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" | ||||
| dependencies = [ | ||||
|  "utf8parse", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-query" | ||||
| version = "1.1.2" | ||||
| version = "1.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" | ||||
| checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" | ||||
| dependencies = [ | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-wincon" | ||||
| version = "3.0.7" | ||||
| version = "3.0.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" | ||||
| checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "once_cell", | ||||
|  "once_cell_polyfill", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "2.9.0" | ||||
| version = "2.9.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" | ||||
| checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitvec" | ||||
|  | @ -95,30 +104,11 @@ dependencies = [ | |||
|  "pkg-config", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cbindgen" | ||||
| version = "0.28.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" | ||||
| dependencies = [ | ||||
|  "clap", | ||||
|  "heck", | ||||
|  "indexmap", | ||||
|  "log", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "syn", | ||||
|  "tempfile", | ||||
|  "toml", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.2.21" | ||||
| version = "1.2.27" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" | ||||
| checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
|  "libc", | ||||
|  | @ -127,42 +117,15 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "cfg-if" | ||||
| version = "1.0.0" | ||||
| version = "1.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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" | ||||
| checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "colorchoice" | ||||
| version = "1.0.3" | ||||
| version = "1.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" | ||||
| checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crc32fast" | ||||
|  | @ -174,32 +137,33 @@ dependencies = [ | |||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "equivalent" | ||||
| version = "1.0.2" | ||||
| name = "env_filter" | ||||
| version = "0.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "errno" | ||||
| version = "0.3.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" | ||||
| checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "windows-sys", | ||||
|  "log", | ||||
|  "regex", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "2.3.0" | ||||
| name = "env_logger" | ||||
| version = "0.11.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" | ||||
| checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" | ||||
| dependencies = [ | ||||
|  "anstream", | ||||
|  "anstyle", | ||||
|  "env_filter", | ||||
|  "jiff", | ||||
|  "log", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "flate2" | ||||
| version = "1.1.1" | ||||
| version = "1.1.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" | ||||
| checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" | ||||
| dependencies = [ | ||||
|  "crc32fast", | ||||
|  "miniz_oxide", | ||||
|  | @ -213,9 +177,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" | |||
| 
 | ||||
| [[package]] | ||||
| name = "getrandom" | ||||
| version = "0.3.2" | ||||
| version = "0.3.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" | ||||
| checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  | @ -224,25 +188,14 @@ dependencies = [ | |||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.15.3" | ||||
| name = "inherent" | ||||
| version = "1.0.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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" | ||||
| checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" | ||||
| dependencies = [ | ||||
|  "equivalent", | ||||
|  "hashbrown", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -252,10 +205,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
| version = "1.0.15" | ||||
| name = "jiff" | ||||
| version = "0.2.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" | ||||
| 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", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "jobserver" | ||||
|  | @ -269,15 +240,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.172" | ||||
| version = "0.2.174" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.9.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" | ||||
| checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "log" | ||||
|  | @ -287,15 +252,15 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | |||
| 
 | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| version = "2.7.4" | ||||
| version = "2.7.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | ||||
| checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.8.8" | ||||
| version = "0.8.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" | ||||
| checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" | ||||
| dependencies = [ | ||||
|  "adler2", | ||||
| ] | ||||
|  | @ -306,12 +271,39 @@ 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" | ||||
|  | @ -332,9 +324,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "r-efi" | ||||
| version = "5.2.0" | ||||
| version = "5.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" | ||||
| checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "radium" | ||||
|  | @ -342,6 +334,35 @@ 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" | ||||
|  | @ -352,25 +373,6 @@ 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" | ||||
|  | @ -391,36 +393,16 @@ dependencies = [ | |||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.140" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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" | ||||
| version = "0.15.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" | ||||
| checksum = "2800caad491cb44f67e5dd5b8c61ece368eecfe588155d03c7d9864acbad6919" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "bzip2", | ||||
|  "flate2", | ||||
|  "inherent", | ||||
|  "log", | ||||
|  "once_cell", | ||||
|  "rust-lzma", | ||||
|  | @ -430,9 +412,10 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.14.1" | ||||
| version = "0.15.0" | ||||
| dependencies = [ | ||||
|  "cbindgen", | ||||
|  "env_logger", | ||||
|  "paste", | ||||
|  "servicepoint", | ||||
| ] | ||||
| 
 | ||||
|  | @ -442,17 +425,11 @@ version = "1.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "strsim" | ||||
| version = "0.11.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.101" | ||||
| version = "2.0.104" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" | ||||
| checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -465,19 +442,6 @@ 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" | ||||
|  | @ -498,47 +462,6 @@ 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" | ||||
|  | @ -639,15 +562,6 @@ 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" | ||||
|  |  | |||
							
								
								
									
										21
									
								
								Cargo.toml
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								Cargo.toml
									
										
									
									
									
								
							|  | @ -1,6 +1,6 @@ | |||
| [package] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.14.1" | ||||
| version = "0.15.0" | ||||
| publish = true | ||||
| edition = "2021" | ||||
| license = "GPL-3.0-or-later" | ||||
|  | @ -8,23 +8,26 @@ 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] | ||||
| package = "servicepoint" | ||||
| version = "0.14.1" | ||||
| version = "0.15.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"] | ||||
| default = ["all_compressions", "servicepoint/default", "env_logger"] | ||||
| env_logger = ["dep:env_logger"] | ||||
| 
 | ||||
| [lints.rust] | ||||
| missing-docs = "warn" | ||||
|  | @ -36,7 +39,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 | ||||
|  |  | |||
|  | @ -1,9 +1,11 @@ | |||
| # servicepoint_binding_c | ||||
| 
 | ||||
| [](https://git.berlin.ccc.de/servicepoint/servicepoint_binding_c/releases) | ||||
| [](https://crates.io/crates/servicepoint) | ||||
| [](https://crates.io/crates/servicepoint) | ||||
| [](https://docs.rs/servicepoint/latest/servicepoint/) | ||||
| [](./LICENSE) | ||||
| [](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". | ||||
|  | @ -12,7 +14,7 @@ This crate contains C bindings for the [servicepoint](https://git.berlin.ccc.de/ | |||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| ```c++ | ||||
| ```c | ||||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										35
									
								
								build.rs
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								build.rs
									
										
									
									
									
								
							|  | @ -1,35 +0,0 @@ | |||
| //! Build script generating the header for the `servicepoint` C library.
 | ||||
| //!
 | ||||
| //! When the environment variable `SERVICEPOINT_HEADER_OUT` is set, the header is copied there from
 | ||||
| //! the out directory. This can be used to use the build script as a command line tool from other
 | ||||
| //! build tools.
 | ||||
| 
 | ||||
| use std::{env, fs::copy}; | ||||
| 
 | ||||
| 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"); | ||||
|     } | ||||
| } | ||||
|  | @ -16,27 +16,28 @@ line_endings = "LF" | |||
| 
 | ||||
| ############################# Codegen Options ################################## | ||||
| 
 | ||||
| style = "type" | ||||
| style = "both" | ||||
| 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"] | ||||
| extra_bindings = ["servicepoint", "servicepoint_binding_c"] | ||||
| 
 | ||||
| [parse.expand] | ||||
| features = ["full"] | ||||
| crates = ["servicepoint_binding_c", "paste"] | ||||
| features = [] | ||||
| 
 | ||||
| [export] | ||||
| include = [] | ||||
| exclude = [] | ||||
| exclude = ["BitVec"] | ||||
| 
 | ||||
| [export.rename] | ||||
| "SpBitVec" = "BitVec" | ||||
| "SpByteSlice" = "ByteSlice" | ||||
| 
 | ||||
| [enum] | ||||
| rename_variants = "QualifiedScreamingSnakeCase" | ||||
|  |  | |||
							
								
								
									
										71
									
								
								devShells.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								devShells.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| { | ||||
|   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; | ||||
| } | ||||
							
								
								
									
										194
									
								
								example/Makefile
									
										
									
									
									
								
							
							
						
						
									
										194
									
								
								example/Makefile
									
										
									
									
									
								
							|  | @ -1,129 +1,121 @@ | |||
| # 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 := "" | ||||
| FEATURES := ""#"env_logger" | ||||
| 
 | ||||
| THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) | ||||
| REPO_ROOT := $(realpath $(THIS_DIR)/..) | ||||
| export SERVICEPOINT_HEADER_OUT := $(REPO_ROOT)/include | ||||
| CARGO_OBJDIR := cargo/$(RUST_TARGET)/$(_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)) | ||||
| _rust_cli_profile := $(if $(filter $(_profile),debug),dev,$(_profile)) | ||||
| 
 | ||||
| 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 | ||||
| STRIPFLAGS += -s --strip-unneeded -R .comment -R .gnu.version -R .note -R .note.gnu.build-id -R .note.ABI-tag | ||||
| 
 | ||||
| CARGOFLAGS += --manifest-path=$(REPO_ROOT)/Cargo.toml \
 | ||||
| 	--profile=$(CARGO_PROFILE) \
 | ||||
| 	--profile=$(_rust_cli_profile) \
 | ||||
| 	--no-default-features \
 | ||||
| 	--features=$(FEATURES) \
 | ||||
| 	--target=$(TARGET) | ||||
| 	--target=$(RUST_TARGET) \
 | ||||
| 	--target-dir=cargo | ||||
| 
 | ||||
| ifneq ($(CFG_PROFILE), debug) | ||||
| 	CCFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections | ||||
| 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) | ||||
| 	RUSTFLAGS += -C link-arg=-s -C link-arg=-Wl,--gc-sections | ||||
| endif | ||||
| 
 | ||||
| ifeq ($(LTO), 1) | ||||
| 	CCFLAGS += -flto | ||||
| endif | ||||
| 
 | ||||
| _c_src := $(wildcard ./src/*.c) | ||||
| # 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 | ||||
| _programs := $(basename $(notdir $(_c_src))) | ||||
| _bins := $(addprefix out/, $(_programs)) | ||||
| _bins := $(_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 | ||||
| 
 | ||||
| all: $(_bins) | ||||
| .SUFFIXES: | ||||
| 
 | ||||
| clean: clean-c clean-rust | ||||
| .PHONY: all build-rust | ||||
| 
 | ||||
| clean-c: | ||||
| 	rm -r out || true | ||||
| all: build-rust $(_bins) | ||||
| 
 | ||||
| clean-rust: | ||||
| 	rm $(SERVICEPOINT_HEADER_OUT)/servicepoint.h || true | ||||
| 	cargo clean | ||||
| $(_unstripped_bins): %_unstripped : src/%.c src/helpers.h build-rust | ||||
| 	$(CC) $< $(CFLAGS) -o $@ | ||||
| 
 | ||||
| .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 | ||||
| $(_bins): %: %_unstripped | ||||
| 	$(STRIP) $(STRIPFLAGS) $^ -o $@ | ||||
| 
 | ||||
| $(_sp_artifacts): $(_rs_src) | ||||
| 	mkdir -p $(SERVICEPOINT_HEADER_OUT) || true | ||||
| build-rust: | ||||
| 	# generate servicepoint header and library to link against | ||||
| 	$(CARGO) rustc $(CARGOFLAGS) -- $(RUSTFLAGS) | ||||
| 
 | ||||
| $(_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: ; | ||||
| #----- Begin Boilerplate
 | ||||
| endif | ||||
|  |  | |||
|  | @ -1,16 +1,9 @@ | |||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| 
 | ||||
| #include "helpers.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     printf("test\n"); | ||||
|     sock_init(); | ||||
| 
 | ||||
|     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}); | ||||
|     sp_udp_socket_send_header(sock, (Header) {.command_code = COMMAND_CODE_CLEAR}); | ||||
| 
 | ||||
|     CharGrid *grid = sp_char_grid_new(5, 2); | ||||
|     if (grid == NULL) | ||||
|  | @ -27,11 +20,10 @@ int main(void) { | |||
|     sp_char_grid_set(grid, 3, 1, 'l'); | ||||
|     sp_char_grid_set(grid, 4, 1, 'd'); | ||||
| 
 | ||||
|     Packet *packet = sp_char_grid_into_packet(grid, 0, 0); | ||||
|     Packet *packet = sp_char_grid_try_into_packet(grid, 0, 0); | ||||
|     if (packet == NULL) | ||||
|         return 1; | ||||
|     sp_udp_send_packet(connection, packet); | ||||
|     sp_udp_socket_send_packet(sock, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,30 +1,35 @@ | |||
| #include "servicepoint.h" | ||||
| #include "helpers.h" | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| void enable_all_pixels(void) { | ||||
|     Bitmap *all_on = sp_bitmap_new_max_sized(); | ||||
|     sp_bitmap_fill(all_on, true); | ||||
| 
 | ||||
|     Packet *packet = sp_bitmap_into_packet(all_on, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     if (packet == NULL) | ||||
|         return -1; | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 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(); | ||||
| 
 | ||||
|     BrightnessGrid *grid = sp_brightness_grid_new(TILE_WIDTH, TILE_HEIGHT); | ||||
|     make_brightness_pattern(grid); | ||||
| 
 | ||||
|     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)); | ||||
|     } | ||||
|     Packet *packet = sp_brightness_grid_command_try_into_packet(sp_brightness_grid_command_from_grid(grid)); | ||||
|     if (packet == NULL) | ||||
|         return -2; | ||||
| 
 | ||||
|     packet = sp_brightness_grid_into_packet(grid, 0, 0); | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     sp_udp_socket_send_packet(sock, packet); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										209
									
								
								example/src/header_logger.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								example/src/header_logger.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| #include <stdio.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #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); | ||||
| } | ||||
							
								
								
									
										44
									
								
								example/src/helpers.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								example/src/helpers.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| #pragma once | ||||
| #ifndef SERVICEPOINT_BINDING_C_MSLEEP_H | ||||
| #define SERVICEPOINT_BINDING_C_MSLEEP_H | ||||
| 
 | ||||
| #include <time.h> | ||||
| #include <errno.h> | ||||
| #include <stdlib.h> | ||||
| #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
 | ||||
							
								
								
									
										34
									
								
								example/src/moving_line.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								example/src/moving_line.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| #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; | ||||
| } | ||||
|  | @ -1,10 +1,9 @@ | |||
| #include <stdio.h> | ||||
| #include "servicepoint.h" | ||||
| #include "helpers.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     UdpSocket *connection = sp_udp_open_ipv4(127, 0, 0, 1, 2342); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
|     sock_init(); | ||||
| 
 | ||||
|     Bitmap *pixels = sp_bitmap_new(PIXEL_WIDTH, PIXEL_HEIGHT); | ||||
|     if (pixels == NULL) | ||||
|  | @ -12,15 +11,13 @@ int main(void) { | |||
| 
 | ||||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     Packet *packet = sp_bitmap_into_packet(pixels, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     Packet *packet = sp_bitmap_try_into_packet(pixels, 0, 0, COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     if (packet == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     Header *header = sp_packet_get_header(packet); | ||||
|     Header *header = sp_packet_get_header_mut(packet); | ||||
|     printf("[%d, %d, %d, %d, %d]\n", header->command_code, header->a, header->b, header->c, header->d); | ||||
| 
 | ||||
|     sp_udp_send_packet(connection, packet); | ||||
| 
 | ||||
|     sp_udp_free(connection); | ||||
|     sp_udp_socket_send_packet(sock, packet); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										22
									
								
								example/src/undefined.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								example/src/undefined.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| #include <stdio.h> | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										34
									
								
								example/src/wiping_clear.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								example/src/wiping_clear.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| #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; | ||||
| } | ||||
							
								
								
									
										56
									
								
								example/target.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								example/target.mk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| # 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." | ||||
							
								
								
									
										63
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										63
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							|  | @ -1,25 +1,80 @@ | |||
| { | ||||
|   "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": 1739357830, | ||||
|         "narHash": "sha256-9xim3nJJUFbVbJCz48UP4fGRStVW5nv4VdbimbKxJ3I=", | ||||
|         "lastModified": 1750838302, | ||||
|         "narHash": "sha256-aVkL3/yu50oQzi2YuKo0ceiCypVZpZXYd2P2p1FMJM4=", | ||||
|         "owner": "nixos", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "0ff09db9d034a04acd4e8908820ba0b410d7a33a", | ||||
|         "rev": "7284e2decc982b81a296ab35aa46e804baaa1cfe", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "nixos", | ||||
|         "ref": "nixos-24.11", | ||||
|         "ref": "nixos-25.05", | ||||
|         "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", | ||||
|  |  | |||
							
								
								
									
										56
									
								
								flake.nix
									
										
									
									
									
								
							
							
						
						
									
										56
									
								
								flake.nix
									
										
									
									
									
								
							|  | @ -2,16 +2,22 @@ | |||
|   description = "Flake for the servicepoint library."; | ||||
| 
 | ||||
|   inputs = { | ||||
|     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; | ||||
|     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"; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   outputs = | ||||
|     inputs@{ | ||||
|       self, | ||||
|       nixpkgs, | ||||
|       fenix, | ||||
|       nix-filter | ||||
|     }: | ||||
|     let | ||||
|       lib = nixpkgs.lib; | ||||
|       supported-systems = [ | ||||
|         "x86_64-linux" | ||||
|         "aarch64-linux" | ||||
|  | @ -20,52 +26,20 @@ | |||
|       ]; | ||||
|       forAllSystems = | ||||
|         f: | ||||
|         lib.genAttrs supported-systems ( | ||||
|         nixpkgs.lib.genAttrs supported-systems ( | ||||
|           system: | ||||
|           f rec { | ||||
|             inherit system self; | ||||
|             pkgs = nixpkgs.legacyPackages.${system}; | ||||
|             inherit system; | ||||
|             lib = pkgs.lib // nix-filter.lib; | ||||
|             fenix = inputs.fenix.packages.${system}; | ||||
|             selfPkgs = self.packages.${system}; | ||||
|           } | ||||
|         ); | ||||
|     in | ||||
|     { | ||||
|       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}"; | ||||
|           }; | ||||
|         } | ||||
|       ); | ||||
| 
 | ||||
|       packages = forAllSystems (import ./packages.nix); | ||||
|       devShells = forAllSystems (import ./devShells.nix); | ||||
|       formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style); | ||||
|     }; | ||||
| } | ||||
|  |  | |||
|  | @ -3,4 +3,4 @@ set -e | |||
| 
 | ||||
| SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" | ||||
| 
 | ||||
| SERVICEPOINT_HEADER_OUT="$SCRIPT_PATH/include" cargo build --release | ||||
| cbindgen --config $SCRIPT_PATH/cbindgen.toml --crate servicepoint_binding_c --output $SCRIPT_PATH/include/servicepoint.h | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										15
									
								
								nix-build-all.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								nix-build-all.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| #!/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 | ||||
							
								
								
									
										181
									
								
								packages.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								packages.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,181 @@ | |||
| { | ||||
|   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; | ||||
|     } | ||||
|   ) | ||||
| )) | ||||
							
								
								
									
										6
									
								
								servicepoint.pc.in
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								servicepoint.pc.in
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| 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 | ||||
							
								
								
									
										226
									
								
								src/bitmap.rs
									
										
									
									
									
								
							
							
						
						
									
										226
									
								
								src/bitmap.rs
									
										
									
									
									
								
							|  | @ -1,226 +0,0 @@ | |||
| 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<Bitmap> { | ||||
|     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<SPBitVec>, | ||||
| ) -> *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<Bitmap>, | ||||
| ) -> NonNull<Bitmap> { | ||||
|     heap_move_nonnull(unsafe { bitmap.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [Bitmap].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_free(bitmap: NonNull<Bitmap>) { | ||||
|     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<Bitmap>, | ||||
|     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<Bitmap>, | ||||
|     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<Bitmap>, 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<Bitmap>) -> 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<Bitmap>) -> 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<Bitmap>, | ||||
| ) -> 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<Bitmap>, | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     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<Bitmap>, | ||||
|     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(), | ||||
|     } | ||||
| } | ||||
							
								
								
									
										174
									
								
								src/bitvec.rs
									
										
									
									
									
								
							
							
						
						
									
										174
									
								
								src/bitvec.rs
									
										
									
									
									
								
							|  | @ -1,174 +0,0 @@ | |||
| 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<SPBitVec> { | ||||
|     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<SPBitVec> { | ||||
|     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<SPBitVec>, | ||||
| ) -> NonNull<SPBitVec> { | ||||
|     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<SPBitVec>) { | ||||
|     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<SPBitVec>, | ||||
|     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<SPBitVec>, | ||||
|     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<SPBitVec>, | ||||
|     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<SPBitVec>) -> 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<SPBitVec>, | ||||
| ) -> 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<SPBitVec>, | ||||
| ) -> 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<SPBitVec>, | ||||
|     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(), | ||||
|     } | ||||
| } | ||||
|  | @ -1,197 +0,0 @@ | |||
| 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<BrightnessGrid> { | ||||
|     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<BrightnessGrid>, | ||||
| ) -> NonNull<BrightnessGrid> { | ||||
|     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<BrightnessGrid>, | ||||
| ) { | ||||
|     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<BrightnessGrid>, | ||||
|     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<BrightnessGrid>, | ||||
|     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<BrightnessGrid>, | ||||
|     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<BrightnessGrid>, | ||||
| ) -> 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<BrightnessGrid>, | ||||
| ) -> 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<BrightnessGrid>, | ||||
| ) -> ByteSlice { | ||||
|     //noinspection RsAssertEqual
 | ||||
|     const _: () = assert!(size_of::<Brightness>() == 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<BrightnessGrid>, | ||||
|     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(), | ||||
|     } | ||||
| } | ||||
|  | @ -1,39 +0,0 @@ | |||
| //! 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<u8>, | ||||
|     /// 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(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										155
									
								
								src/char_grid.rs
									
										
									
									
									
								
							
							
						
						
									
										155
									
								
								src/char_grid.rs
									
										
									
									
									
								
							|  | @ -1,155 +0,0 @@ | |||
| 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<CharGrid> { | ||||
|     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<CharGrid>, | ||||
| ) -> NonNull<CharGrid> { | ||||
|     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<CharGrid>) { | ||||
|     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<CharGrid>, | ||||
|     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<CharGrid>, | ||||
|     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<CharGrid>, | ||||
|     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<CharGrid>, | ||||
| ) -> 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<CharGrid>, | ||||
| ) -> 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<CharGrid>, | ||||
|     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(), | ||||
|     } | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/commands/bitmap_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/commands/bitmap_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| 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<Bitmap>, | ||||
|             origin_x: val usize, | ||||
|             origin_y: val usize, | ||||
|             compression: val CompressionCode, | ||||
|         ) -> move NonNull<BitmapCommand> { | ||||
|             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<Bitmap>) -> move NonNull<BitmapCommand> { | ||||
|             bitmap.into() | ||||
|         }; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/commands/bitvec_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/commands/bitvec_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| 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<DisplayBitVec>, | ||||
|             offset: val usize, | ||||
|             operation: val BinaryOperation, | ||||
|             compression: val CompressionCode, | ||||
|         ) -> move NonNull<BitVecCommand> { | ||||
|             BitVecCommand { | ||||
|                 bitvec, | ||||
|                 offset, | ||||
|                 operation, | ||||
|                 compression, | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										33
									
								
								src/commands/brightness_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/commands/brightness_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| 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<BrightnessGrid>, | ||||
|             origin_x: val usize, | ||||
|             origin_y: val usize | ||||
|         ) -> move NonNull<BrightnessGridCommand> { | ||||
|             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<BrightnessGrid>) -> move NonNull<BrightnessGridCommand> { | ||||
|             grid.into() | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										39
									
								
								src/commands/cc_only_commands.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/commands/cc_only_commands.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| 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 | ||||
| ); | ||||
							
								
								
									
										33
									
								
								src/commands/char_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/commands/char_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| 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<CharGrid>, | ||||
|             origin_x: val usize, | ||||
|             origin_y: val usize, | ||||
|         ) -> move NonNull<CharGridCommand> { | ||||
|             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<CharGrid>) -> move NonNull<CharGridCommand> { | ||||
|             grid.into() | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										33
									
								
								src/commands/cp437_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/commands/cp437_grid_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| 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<Cp437Grid>, | ||||
|             origin_x: val usize, | ||||
|             origin_y: val usize, | ||||
|         ) -> move NonNull<Cp437GridCommand> { | ||||
|             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<Cp437Grid>) -> move NonNull<Cp437GridCommand> { | ||||
|             grid.into() | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										293
									
								
								src/commands/generic_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								src/commands/generic_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,293 @@ | |||
| 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<BitmapCommand>, | ||||
|     pub bit_vec: NonNull<BitVecCommand>, | ||||
|     pub brightness_grid: NonNull<BrightnessGridCommand>, | ||||
|     pub char_grid: NonNull<CharGridCommand>, | ||||
|     pub cp437_grid: NonNull<Cp437GridCommand>, | ||||
|     pub global_brightness: NonNull<GlobalBrightnessCommand>, | ||||
|     pub clear: NonNull<ClearCommand>, | ||||
|     #[allow(deprecated)] | ||||
|     pub bitmap_legacy: NonNull<servicepoint::BitmapLegacyCommand>, | ||||
|     pub hard_reset: NonNull<HardResetCommand>, | ||||
|     pub fade_out: NonNull<FadeOutCommand>, | ||||
| } | ||||
| 
 | ||||
| /// 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<Packet>) -> move NonNull<GenericCommand> { | ||||
|             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() }) | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/commands/global_brightness_command.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/commands/global_brightness_command.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| 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> { | ||||
|             GlobalBrightnessCommand::from(brightness) | ||||
|         }; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										87
									
								
								src/commands/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/commands/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| 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<usize>, | ||||
|                     origin_y: mut ::core::ptr::NonNull<usize> | ||||
|                 ) { | ||||
|                     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, | ||||
| }; | ||||
							
								
								
									
										98
									
								
								src/containers/bitmap.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/containers/bitmap.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | |||
| 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> { | ||||
|             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<DisplayBitVec>, | ||||
|         ) -> 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<DisplayBitVec> { | ||||
|             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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										99
									
								
								src/containers/bitvec.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/containers/bitvec.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | |||
| 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> { | ||||
|             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> { | ||||
|             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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								src/containers/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/containers/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| 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> { | ||||
|             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::<Brightness>() == 1); | ||||
| 
 | ||||
|             let br_slice = instance.data_ref_mut(); | ||||
|             unsafe { | ||||
|                 transmute::<&mut [Brightness], &mut [u8]>(br_slice) | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/containers/byte_slice.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/containers/byte_slice.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | |||
| //! 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(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/containers/char_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/containers/char_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| 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> { | ||||
|             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), | ||||
|             }) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/containers/cp437_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/containers/cp437_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| 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> { | ||||
|             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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/containers/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/containers/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| 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; | ||||
| } | ||||
|  | @ -1,157 +0,0 @@ | |||
| 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<Cp437Grid> { | ||||
|     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<Cp437Grid>, | ||||
| ) -> NonNull<Cp437Grid> { | ||||
|     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<Cp437Grid>) { | ||||
|     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<Cp437Grid>, | ||||
|     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<Cp437Grid>, | ||||
|     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<Cp437Grid>, | ||||
|     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<Cp437Grid>, | ||||
| ) -> 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<Cp437Grid>, | ||||
| ) -> 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<Cp437Grid>, | ||||
| ) -> 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<Cp437Grid>, | ||||
|     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(), | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										69
									
								
								src/lib.rs
									
										
									
									
									
								
							|  | @ -2,14 +2,12 @@ | |||
| //!
 | ||||
| //! # Examples
 | ||||
| //!
 | ||||
| //! Make sure to check out [this GitHub repo](https://github.com/arfst23/ServicePoint) as well!
 | ||||
| //!
 | ||||
| //! ```C
 | ||||
| //! #include <stdio.h>
 | ||||
| //! #include "servicepoint.h"
 | ||||
| //!
 | ||||
| //! int main(void) {
 | ||||
| //!     UdpConnection *connection = sp_udp_open("172.23.42.29:2342");
 | ||||
| //!     UdpSocket *connection = sp_udp_open("172.23.42.29:2342");
 | ||||
| //!     if (connection == NULL)
 | ||||
| //!         return 1;
 | ||||
| //!
 | ||||
|  | @ -24,49 +22,32 @@ | |||
| //!     return 0;
 | ||||
| //! }
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! There are more examples in the source repository.
 | ||||
| 
 | ||||
| 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; | ||||
| /// 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; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); | ||||
| pub const SP_FRAME_PACING_MS: u128 = 30; | ||||
| 
 | ||||
| pub(crate) fn heap_move<T>(x: T) -> *mut T { | ||||
|     Box::into_raw(Box::new(x)) | ||||
| #[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_nonnull<T>(x: T) -> NonNull<T> { | ||||
|     NonNull::from(Box::leak(Box::new(x))) | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_drop<T>(x: NonNull<T>) { | ||||
|     drop(unsafe { heap_remove(x) }); | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_remove<T>(x: NonNull<T>) -> 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; | ||||
|  |  | |||
							
								
								
									
										326
									
								
								src/macros.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/macros.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,326 @@ | |||
| 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 [<get _ $prop_name>](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 [<get _ $prop_name _mut>](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 [<set _ $prop_name>](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 [<set _ $prop_name>](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, | ||||
| }; | ||||
							
								
								
									
										29
									
								
								src/mem.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/mem.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| pub(crate) fn heap_move<T>(x: T) -> *mut T { | ||||
|     Box::into_raw(Box::new(x)) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn heap_move_nonnull<T>(x: T) -> NonNull<T> { | ||||
|     NonNull::from(Box::leak(Box::new(x))) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn heap_move_ok<T, E>(x: Result<T, E>) -> *mut T { | ||||
|     x.map(|x| heap_move(x)).unwrap_or(std::ptr::null_mut()) | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn heap_move_some<T>(x: Option<T>) -> *mut T { | ||||
|     x.map(|x| heap_move(x)).unwrap_or(std::ptr::null_mut()) | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_drop<T>(x: NonNull<T>) { | ||||
|     drop(unsafe { heap_remove(x) }); | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_remove<T>(x: NonNull<T>) -> T { | ||||
|     unsafe { *Box::from_raw(x.as_ptr()) } | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn heap_clone<T: Clone>(source: NonNull<T>) -> NonNull<T> { | ||||
|     heap_move_nonnull(unsafe { source.as_ref().clone() }) | ||||
| } | ||||
							
								
								
									
										197
									
								
								src/packet.rs
									
										
									
									
									
								
							
							
						
						
									
										197
									
								
								src/packet.rs
									
										
									
									
									
								
							|  | @ -1,128 +1,85 @@ | |||
| use crate::{heap_drop, heap_move, heap_move_nonnull, heap_remove, ByteSlice}; | ||||
| use servicepoint::{CommandCode, Header, Packet, TypedCommand}; | ||||
| use crate::{ | ||||
|     containers::ByteSlice, | ||||
|     macros::{derive_clone, derive_free, wrap, wrap_functions}, | ||||
| }; | ||||
| use servicepoint::{CommandCode, Header, Packet}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// 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<TypedCommand>, | ||||
| ) -> *mut Packet { | ||||
|     let command = unsafe { heap_remove(command) }; | ||||
|     if let Ok(packet) = command.try_into() { | ||||
|         heap_move(packet) | ||||
|     } else { | ||||
|         std::ptr::null_mut() | ||||
|     } | ||||
| } | ||||
| derive_clone!(Packet); | ||||
| derive_free!(Packet); | ||||
| 
 | ||||
| /// 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), | ||||
|     } | ||||
| } | ||||
| 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.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_parts( | ||||
|     header: Header, | ||||
|     payload: *const ByteSlice, | ||||
| ) -> NonNull<Packet> { | ||||
|     let payload = if payload.is_null() { | ||||
|         vec![] | ||||
|     } else { | ||||
|         let payload = unsafe { (*payload).as_slice() }; | ||||
|         Vec::from(payload) | ||||
|     }; | ||||
|         /// Creates a raw [Packet] from parts.
 | ||||
|         ///
 | ||||
|         /// returns: new instance. Will never return null.
 | ||||
|         fn from_parts(header: val Header, payload: val ByteSlice) -> move NonNull<Packet> { | ||||
|             let payload = if payload == ByteSlice::INVALID { | ||||
|                 None | ||||
|             } else { | ||||
|                 Some(Vec::from(unsafe { payload.as_slice() })) | ||||
|             }; | ||||
| 
 | ||||
|     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<Packet>, | ||||
| ) -> NonNull<Header> { | ||||
|     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<Packet>, | ||||
| ) -> 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<Packet>, | ||||
|     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<Packet>, | ||||
|     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<Packet>, | ||||
| ) -> NonNull<Packet> { | ||||
|     heap_move_nonnull(unsafe { packet.as_ref().clone() }) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [Packet].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_free(packet: NonNull<Packet>) { | ||||
|     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; | ||||
|             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) }, | ||||
|             } | ||||
|             true | ||||
|         } | ||||
|         Err(_) => false, | ||||
|         }; | ||||
| 
 | ||||
|         /// 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) | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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<CommandCode>) -> val bool { | ||||
|         match CommandCode::try_from(code) { | ||||
|             Ok(code) => { | ||||
|                 *result = code; | ||||
|                 true | ||||
|             } | ||||
|             Err(_) => false, | ||||
|         } | ||||
|     }; | ||||
| ); | ||||
|  |  | |||
|  | @ -1,201 +0,0 @@ | |||
| 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<Packet>, | ||||
| ) -> *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<TypedCommand>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     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<TypedCommand> { | ||||
|     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<TypedCommand> { | ||||
|     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<TypedCommand> { | ||||
|     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<TypedCommand> { | ||||
|     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<BrightnessGrid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     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<SPBitVec>, | ||||
|     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<Cp437Grid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     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<CharGrid>, | ||||
| ) -> NonNull<TypedCommand> { | ||||
|     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<Bitmap>, | ||||
|     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<TypedCommand>) { | ||||
|     unsafe { heap_drop(command) } | ||||
| } | ||||
							
								
								
									
										226
									
								
								src/udp.rs
									
										
									
									
									
								
							
							
						
						
									
										226
									
								
								src/udp.rs
									
										
									
									
									
								
							|  | @ -1,119 +1,117 @@ | |||
| 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; | ||||
| 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, | ||||
| }; | ||||
| 
 | ||||
| /// 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<c_char>) -> *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, | ||||
|     }; | ||||
| derive_free!(UdpSocket); | ||||
| 
 | ||||
|     heap_move(connection) | ||||
| 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<c_char>) -> 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<Packet>) -> 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<GenericCommand>) -> 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() | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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<UdpSocket>, | ||||
|     packet: NonNull<Packet>, | ||||
| ) -> 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<UdpSocket>, | ||||
|     command: NonNull<TypedCommand>, | ||||
| ) -> 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<UdpSocket>, | ||||
|     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<UdpSocket>) { | ||||
|     unsafe { heap_drop(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; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue