Merge pull request #10 from cccb/improve-binding-docs
Improve binding documentation
This commit is contained in:
		
						commit
						c5cb6475b2
					
				
					 44 changed files with 3383 additions and 1659 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,3 +4,4 @@ out | |||
| bin | ||||
| obj | ||||
| .direnv | ||||
| .envrc | ||||
|  |  | |||
							
								
								
									
										28
									
								
								CONTRIBUTING.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								CONTRIBUTING.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| # Contributing | ||||
| 
 | ||||
| Contributions are accepted in any form (issues, documentation, feature requests, code, review, ...). | ||||
| 
 | ||||
| All creatures welcome. | ||||
| 
 | ||||
| ## Pull requests | ||||
| 
 | ||||
| Feel free to create a PR, even if your change is not done yet. | ||||
| 
 | ||||
| Mark your PR as a draft as long as you do not want it to be merged. | ||||
| 
 | ||||
| The main branch is supposed to be a working version, including language bindings, | ||||
| which means sometimes your PR may be merged into a temporary development branch. | ||||
| 
 | ||||
| Unit tests and documentation are required for the core library. | ||||
| 
 | ||||
| ## Language bindings | ||||
| 
 | ||||
| Pull requests for your preferred language will be accepted. | ||||
| If there is no code generator, it should call the C ABI methods provided by `servicepoint_binding_c`. | ||||
| It should be able to send most of the basic commands in a way the simulator accepts, receiving is | ||||
| not required for the merge. | ||||
| 
 | ||||
| It is okay for the feature set of a language binding to lag behind the one of the rust crate. | ||||
| This also means you do not have to expose a feature to all the language bindings when adding something to the core. | ||||
| 
 | ||||
| If your change may break other language bindings, please note that in your PR description so someone can check them. | ||||
							
								
								
									
										441
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										441
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -3,10 +3,10 @@ | |||
| version = 3 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "adler" | ||||
| version = "1.0.2" | ||||
| name = "adler2" | ||||
| version = "2.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||||
| checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "aho-corasick" | ||||
|  | @ -19,9 +19,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "anstream" | ||||
| version = "0.6.14" | ||||
| version = "0.6.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" | ||||
| checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "anstyle-parse", | ||||
|  | @ -34,61 +34,38 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle" | ||||
| version = "1.0.7" | ||||
| version = "1.0.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" | ||||
| checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-parse" | ||||
| version = "0.2.4" | ||||
| version = "0.2.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" | ||||
| checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" | ||||
| dependencies = [ | ||||
|  "utf8parse", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-query" | ||||
| version = "1.1.0" | ||||
| version = "1.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" | ||||
| checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" | ||||
| dependencies = [ | ||||
|  "windows-sys", | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anstyle-wincon" | ||||
| version = "3.0.3" | ||||
| version = "3.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" | ||||
| checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "windows-sys", | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "atty" | ||||
| version = "0.2.14" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" | ||||
| dependencies = [ | ||||
|  "hermit-abi", | ||||
|  "libc", | ||||
|  "winapi", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "autocfg" | ||||
| version = "1.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "2.6.0" | ||||
|  | @ -107,6 +84,12 @@ dependencies = [ | |||
|  "wyz", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "byteorder" | ||||
| version = "1.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bzip2" | ||||
| version = "0.4.4" | ||||
|  | @ -130,11 +113,11 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "cbindgen" | ||||
| version = "0.26.0" | ||||
| version = "0.27.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" | ||||
| checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" | ||||
| dependencies = [ | ||||
|  "clap 3.2.25", | ||||
|  "clap", | ||||
|  "heck 0.4.1", | ||||
|  "indexmap", | ||||
|  "log", | ||||
|  | @ -142,20 +125,20 @@ dependencies = [ | |||
|  "quote", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "syn 1.0.109", | ||||
|  "syn", | ||||
|  "tempfile", | ||||
|  "toml", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.0.101" | ||||
| version = "1.1.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" | ||||
| checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
|  "libc", | ||||
|  "once_cell", | ||||
|  "shlex", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -166,24 +149,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap" | ||||
| version = "3.2.25" | ||||
| version = "4.5.17" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" | ||||
| dependencies = [ | ||||
|  "atty", | ||||
|  "bitflags 1.3.2", | ||||
|  "clap_lex 0.2.4", | ||||
|  "indexmap", | ||||
|  "strsim 0.10.0", | ||||
|  "termcolor", | ||||
|  "textwrap", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap" | ||||
| version = "4.5.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" | ||||
| checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" | ||||
| dependencies = [ | ||||
|  "clap_builder", | ||||
|  "clap_derive", | ||||
|  | @ -191,48 +159,39 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap_builder" | ||||
| version = "4.5.7" | ||||
| version = "4.5.17" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" | ||||
| checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" | ||||
| dependencies = [ | ||||
|  "anstream", | ||||
|  "anstyle", | ||||
|  "clap_lex 0.7.1", | ||||
|  "strsim 0.11.1", | ||||
|  "clap_lex", | ||||
|  "strsim", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap_derive" | ||||
| version = "4.5.5" | ||||
| version = "4.5.13" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" | ||||
| checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" | ||||
| dependencies = [ | ||||
|  "heck 0.5.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap_lex" | ||||
| version = "0.2.4" | ||||
| version = "0.7.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" | ||||
| dependencies = [ | ||||
|  "os_str_bytes", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap_lex" | ||||
| version = "0.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" | ||||
| checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "colorchoice" | ||||
| version = "1.0.1" | ||||
| version = "1.0.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" | ||||
| checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crc32fast" | ||||
|  | @ -245,14 +204,20 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "csbindgen" | ||||
| version = "1.9.1" | ||||
| version = "1.9.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cf70eb656f35e0e6956cbde31c66431c53d8a546823489719099c71525767a9c" | ||||
| checksum = "c26b9831049b947d154bba920e4124053def72447be6fb106a96f483874b482a" | ||||
| dependencies = [ | ||||
|  "regex", | ||||
|  "syn 1.0.109", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "equivalent" | ||||
| version = "1.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "errno" | ||||
| version = "0.3.9" | ||||
|  | @ -260,20 +225,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "windows-sys", | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "2.1.0" | ||||
| version = "2.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" | ||||
| checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "flate2" | ||||
| version = "1.0.30" | ||||
| version = "1.0.33" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" | ||||
| checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" | ||||
| dependencies = [ | ||||
|  "crc32fast", | ||||
|  "miniz_oxide", | ||||
|  | @ -298,9 +263,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.12.3" | ||||
| version = "0.14.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | ||||
| checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "heck" | ||||
|  | @ -314,30 +279,21 @@ version = "0.5.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "hermit-abi" | ||||
| version = "0.1.19" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "indexmap" | ||||
| version = "1.9.3" | ||||
| version = "2.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" | ||||
| checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" | ||||
| dependencies = [ | ||||
|  "autocfg", | ||||
|  "equivalent", | ||||
|  "hashbrown", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "is_terminal_polyfill" | ||||
| version = "1.70.0" | ||||
| version = "1.70.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" | ||||
| checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
|  | @ -347,9 +303,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" | |||
| 
 | ||||
| [[package]] | ||||
| name = "jobserver" | ||||
| version = "0.1.31" | ||||
| version = "0.1.32" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" | ||||
| checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | @ -364,9 +320,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.155" | ||||
| version = "0.2.158" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" | ||||
| checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
|  | @ -376,9 +332,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" | |||
| 
 | ||||
| [[package]] | ||||
| name = "log" | ||||
| version = "0.4.21" | ||||
| version = "0.4.22" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" | ||||
| checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "memchr" | ||||
|  | @ -388,11 +344,11 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | |||
| 
 | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.7.4" | ||||
| version = "0.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" | ||||
| checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" | ||||
| dependencies = [ | ||||
|  "adler", | ||||
|  "adler2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -401,12 +357,6 @@ version = "1.19.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "os_str_bytes" | ||||
| version = "6.6.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pkg-config" | ||||
| version = "0.3.30" | ||||
|  | @ -415,9 +365,12 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" | |||
| 
 | ||||
| [[package]] | ||||
| name = "ppv-lite86" | ||||
| version = "0.2.17" | ||||
| version = "0.2.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | ||||
| checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" | ||||
| dependencies = [ | ||||
|  "zerocopy", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
|  | @ -430,9 +383,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.36" | ||||
| version = "1.0.37" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" | ||||
| checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
|  | @ -475,9 +428,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "regex" | ||||
| version = "1.10.5" | ||||
| version = "1.10.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" | ||||
| checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" | ||||
| dependencies = [ | ||||
|  "aho-corasick", | ||||
|  "memchr", | ||||
|  | @ -514,15 +467,15 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "0.38.34" | ||||
| version = "0.38.36" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" | ||||
| checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" | ||||
| dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "bitflags", | ||||
|  "errno", | ||||
|  "libc", | ||||
|  "linux-raw-sys", | ||||
|  "windows-sys", | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -533,42 +486,52 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" | |||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.203" | ||||
| version = "1.0.210" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" | ||||
| checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.203" | ||||
| version = "1.0.210" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" | ||||
| checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.118" | ||||
| version = "1.0.128" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" | ||||
| checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "memchr", | ||||
|  "ryu", | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_spanned" | ||||
| version = "0.6.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint" | ||||
| version = "0.7.0" | ||||
| version = "0.8.0" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "bzip2", | ||||
|  "clap 4.5.7", | ||||
|  "clap", | ||||
|  "flate2", | ||||
|  "log", | ||||
|  "rand", | ||||
|  | @ -578,7 +541,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.7.0" | ||||
| version = "0.8.0" | ||||
| dependencies = [ | ||||
|  "cbindgen", | ||||
|  "servicepoint", | ||||
|  | @ -586,7 +549,7 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_cs" | ||||
| version = "0.7.0" | ||||
| version = "0.8.0" | ||||
| dependencies = [ | ||||
|  "csbindgen", | ||||
|  "servicepoint", | ||||
|  | @ -594,10 +557,10 @@ dependencies = [ | |||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "strsim" | ||||
| version = "0.10.0" | ||||
| name = "shlex" | ||||
| version = "1.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||||
| checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "strsim" | ||||
|  | @ -607,20 +570,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | |||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.109" | ||||
| version = "2.0.77" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "unicode-ident", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.68" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" | ||||
| checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -635,38 +587,49 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" | |||
| 
 | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.10.1" | ||||
| version = "3.12.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" | ||||
| checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "fastrand", | ||||
|  "once_cell", | ||||
|  "rustix", | ||||
|  "windows-sys", | ||||
|  "windows-sys 0.59.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "termcolor" | ||||
| version = "1.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" | ||||
| dependencies = [ | ||||
|  "winapi-util", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "textwrap" | ||||
| version = "0.16.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "toml" | ||||
| version = "0.5.11" | ||||
| version = "0.8.19" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" | ||||
| checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
|  "serde_spanned", | ||||
|  "toml_datetime", | ||||
|  "toml_edit", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "toml_datetime" | ||||
| version = "0.6.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "toml_edit" | ||||
| version = "0.22.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" | ||||
| dependencies = [ | ||||
|  "indexmap", | ||||
|  "serde", | ||||
|  "serde_spanned", | ||||
|  "toml_datetime", | ||||
|  "winnow", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -693,37 +656,6 @@ version = "0.11.0+wasi-snapshot-preview1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi" | ||||
| version = "0.3.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||||
| dependencies = [ | ||||
|  "winapi-i686-pc-windows-gnu", | ||||
|  "winapi-x86_64-pc-windows-gnu", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi-i686-pc-windows-gnu" | ||||
| version = "0.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi-util" | ||||
| version = "0.1.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" | ||||
| dependencies = [ | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi-x86_64-pc-windows-gnu" | ||||
| version = "0.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-sys" | ||||
| version = "0.52.0" | ||||
|  | @ -734,10 +666,19 @@ dependencies = [ | |||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-targets" | ||||
| version = "0.52.5" | ||||
| name = "windows-sys" | ||||
| version = "0.59.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" | ||||
| checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" | ||||
| dependencies = [ | ||||
|  "windows-targets", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-targets" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" | ||||
| dependencies = [ | ||||
|  "windows_aarch64_gnullvm", | ||||
|  "windows_aarch64_msvc", | ||||
|  | @ -751,51 +692,60 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "windows_aarch64_gnullvm" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" | ||||
| checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_aarch64_msvc" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" | ||||
| checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_i686_gnu" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" | ||||
| checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_i686_gnullvm" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" | ||||
| checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_i686_msvc" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" | ||||
| checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_gnu" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" | ||||
| checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_gnullvm" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" | ||||
| checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows_x86_64_msvc" | ||||
| version = "0.52.5" | ||||
| version = "0.52.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" | ||||
| checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winnow" | ||||
| version = "0.6.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wyz" | ||||
|  | @ -807,28 +757,49 @@ dependencies = [ | |||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd" | ||||
| version = "0.13.1" | ||||
| name = "zerocopy" | ||||
| version = "0.7.35" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" | ||||
| checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "zerocopy-derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zerocopy-derive" | ||||
| version = "0.7.35" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd" | ||||
| version = "0.13.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" | ||||
| dependencies = [ | ||||
|  "zstd-safe", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd-safe" | ||||
| version = "7.1.0" | ||||
| version = "7.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" | ||||
| checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" | ||||
| dependencies = [ | ||||
|  "zstd-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zstd-sys" | ||||
| version = "2.0.11+zstd.1.5.6" | ||||
| version = "2.0.13+zstd.1.5.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" | ||||
| checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "pkg-config", | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ members = [ | |||
| ] | ||||
| 
 | ||||
| [workspace.package] | ||||
| version = "0.7.0" | ||||
| version = "0.8.0" | ||||
| 
 | ||||
| [workspace.lints.rust] | ||||
| missing-docs = "warn" | ||||
|  |  | |||
							
								
								
									
										59
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
										
									
									
									
								
							|  | @ -8,69 +8,28 @@ programming languages. | |||
| Take a look at the contained crates for language specific information: | ||||
| 
 | ||||
| | Language  | Readme                                                              | | ||||
| |----------|---------------------------------------------------------------------| | ||||
| |-----------|---------------------------------------------------------------------| | ||||
| | Rust      | [servicepoint](crates/servicepoint/README.md)                       | | ||||
| | C / C++   | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md)   | | ||||
| | C# / F#  | [servicepoint_binding_cs](crates/servicepoint_binding_cs/README.md) |  | ||||
| | .NET (C#) | [servicepoint_binding_cs](crates/servicepoint_binding_cs/README.md) |  | ||||
| 
 | ||||
| ## Projects using the library | ||||
| 
 | ||||
| - screen simulator (rust): [servicepoint-simulator](https://github.com/kaesaecracker/servicepoint-simulator) | ||||
| - A bunch of projects (C): [arfst23/ServicePoint](https://github.com/arfst23/ServicePoint), including | ||||
|   - a CLI tool to display image files on the display or use the display as a TTY | ||||
|   - a BSD games robots clone | ||||
|   - a split-flap-display simulator | ||||
|   - animations that play on the display | ||||
| - tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs) | ||||
| - cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life) | ||||
| 
 | ||||
| To add yourself to the list, open a pull request. | ||||
| 
 | ||||
| ## About the display | ||||
| ## Contributing | ||||
| 
 | ||||
| - Resolution: 352x160=56,320 pixels | ||||
| - Pixels are grouped into 44x20=880 tiles (8x8=64 pixels each) | ||||
| - Smallest addressable unit: row of pixels inside of a tile (8 pixels = 1 byte) | ||||
| - The brightness can only be set per tile | ||||
| - Screen content can be changed using a simple UDP protocol | ||||
| - Between each row of tiles, there is a gap of around 4 pixels size. This gap changes the aspect ratio of the display. | ||||
| 
 | ||||
| ### Binary format | ||||
| 
 | ||||
| A UDP package sent to the display has a header size of 10 bytes. | ||||
| Each header value has a size of two bytes (unsigned 16 bit integer). | ||||
| Depending on the command, there can be a payload following the header. | ||||
| 
 | ||||
| The commands are implemented in DisplayCommands. | ||||
| 
 | ||||
| To change screen contents, these commands are the most relevant: | ||||
| 
 | ||||
| 1. Clear screen | ||||
|     - command: `0x0002` | ||||
|     - (rest does not matter) | ||||
| 2. Send CP437 data: render specified text into rectangular region | ||||
|     - command: `0x0003` | ||||
|     - top left tile x | ||||
|     - top left tile y | ||||
|     - width in tiles | ||||
|     - height in tiles | ||||
|     - payload: (width in tiles * height in tiles) bytes | ||||
|         - 1 byte = 1 character | ||||
|         - each character is rendered into one tile (mono-spaced) | ||||
|         - characters are encoded using code page 437 | ||||
| 3. Send bitmap window: set pixel states for a rectangular region | ||||
|     - command: `0x0013` | ||||
|     - top left tile x | ||||
|     - top left _pixel_ y | ||||
|     - width in tiles | ||||
|     - height in _pixels_ | ||||
|     - payload: (width in tiles * height in pixels) bytes | ||||
|         - network byte order | ||||
|         - 1 bit = 1 pixel | ||||
| 
 | ||||
| There are other commands implemented as well, e.g. for changing the brightness. | ||||
| See [CONTRIBUTING.md](CONTRIBUTING.md). | ||||
| 
 | ||||
| ## What happened to servicepoint2? | ||||
| 
 | ||||
| After `servicepoint2` has been merged into `servicepoint`, `servicepoint2` will not continue to get any updates. | ||||
| 
 | ||||
| ## Contributing | ||||
| 
 | ||||
| Contributions are accepted in any form (issues, documentation, feature requests, code, review, ...). | ||||
| 
 | ||||
| All creatures welcome. | ||||
|  |  | |||
							
								
								
									
										41
									
								
								about_display.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								about_display.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| # About the display | ||||
| 
 | ||||
| - Resolution: 352x160=56,320 pixels | ||||
| - Pixels are grouped into 44x20=880 tiles (8x8=64 pixels each) | ||||
| - Smallest addressable unit: row of pixels inside of a tile (8 pixels = 1 byte) | ||||
| - The brightness can only be set per tile | ||||
| - Screen content can be changed using a simple UDP protocol | ||||
| - Between each row of tiles, there is a gap of around 4 pixels size. This gap changes the aspect ratio of the display. | ||||
| 
 | ||||
| ### Binary format | ||||
| 
 | ||||
| A UDP package sent to the display has a header size of 10 bytes. | ||||
| Each header value has a size of two bytes (unsigned 16 bit integer). | ||||
| Depending on the command, there can be a payload following the header. | ||||
| 
 | ||||
| To change screen contents, these commands are the most relevant: | ||||
| 
 | ||||
| 1. Clear screen | ||||
|     - command: `0x0002` | ||||
|     - (rest does not matter) | ||||
| 2. Send CP437 data: render specified text into rectangular region | ||||
|     - command: `0x0003` | ||||
|     - top left tile x | ||||
|     - top left tile y | ||||
|     - width in tiles | ||||
|     - height in tiles | ||||
|     - payload: (width in tiles * height in tiles) bytes | ||||
|         - 1 byte = 1 character | ||||
|         - each character is rendered into one tile (mono-spaced) | ||||
|         - characters are encoded using code page 437 | ||||
| 3. Send bitmap window: set pixel states for a rectangular region | ||||
|     - command: `0x0013` | ||||
|     - top left tile x | ||||
|     - top left _pixel_ y | ||||
|     - width in tiles | ||||
|     - height in _pixels_ | ||||
|     - payload: (width in tiles * height in pixels) bytes | ||||
|         - network byte order | ||||
|         - 1 bit = 1 pixel | ||||
| 
 | ||||
| There are other commands implemented as well, e.g. for changing the brightness. | ||||
|  | @ -46,7 +46,7 @@ In the likely case you only need one of them, you can include that one specifica | |||
| 
 | ||||
| ```toml | ||||
| [dependencies] | ||||
| servicepoint = { version = "0.7.0", default-features = false, features = ["compression-bz"] } | ||||
| servicepoint = { version = "0.8.0", default-features = false, features = ["compression-bz"] } | ||||
| ``` | ||||
| 
 | ||||
| ## Everything else | ||||
|  |  | |||
|  | @ -190,10 +190,15 @@ impl TryFrom<Packet> for Command { | |||
| 
 | ||||
|     /// Try to interpret the `Packet` as one containing a `Command`
 | ||||
|     fn try_from(packet: Packet) -> Result<Self, Self::Error> { | ||||
|         let Packet(Header(command_u16, a, _, _, _), _) = packet; | ||||
|         let command_code = match CommandCode::try_from(command_u16) { | ||||
|         let Packet { | ||||
|             header: Header { | ||||
|                 command_code, a, .. | ||||
|             }, | ||||
|             .. | ||||
|         } = packet; | ||||
|         let command_code = match CommandCode::try_from(command_code) { | ||||
|             Err(()) => { | ||||
|                 return Err(TryFromPacketError::InvalidCommand(command_u16)); | ||||
|                 return Err(TryFromPacketError::InvalidCommand(command_code)); | ||||
|             } | ||||
|             Ok(value) => value, | ||||
|         }; | ||||
|  | @ -266,8 +271,17 @@ impl Command { | |||
|         packet: Packet, | ||||
|         compression: CompressionCode, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, tiles_x, pixels_y, tile_w, pixel_h), payload) = | ||||
|             packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a: tiles_x, | ||||
|                     b: pixels_y, | ||||
|                     c: tile_w, | ||||
|                     d: pixel_h, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
| 
 | ||||
|         let payload = match into_decompressed(compression, payload) { | ||||
|             None => return Err(TryFromPacketError::DecompressionFailed), | ||||
|  | @ -290,7 +304,17 @@ impl Command { | |||
|         packet: Packet, | ||||
|         command: Command, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if !payload.is_empty() { | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) | ||||
|         } else if a != 0 || b != 0 || c != 0 || d != 0 { | ||||
|  | @ -304,7 +328,16 @@ impl Command { | |||
|     fn packet_into_linear_bitmap( | ||||
|         packet: Packet, | ||||
|     ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { | ||||
|         let Packet(Header(_, _, length, sub, reserved), payload) = packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     b: length, | ||||
|                     c: sub, | ||||
|                     d: reserved, | ||||
|                     .. | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if reserved != 0 { | ||||
|             return Err(TryFromPacketError::ExtraneousHeaderValues); | ||||
|         } | ||||
|  | @ -330,7 +363,17 @@ impl Command { | |||
|     fn packet_into_char_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, x, y, width, height), payload) = packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a: x, | ||||
|                     b: y, | ||||
|                     c: width, | ||||
|                     d: height, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
| 
 | ||||
|         let grid = | ||||
|             PrimitiveGrid::load(*width as usize, *height as usize, payload); | ||||
|  | @ -348,7 +391,17 @@ impl Command { | |||
|     fn packet_into_brightness( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         if payload.len() != 1 { | ||||
|             return Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|                 1, | ||||
|  | @ -369,7 +422,17 @@ impl Command { | |||
|     fn packet_into_cp437( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet(Header(_, a, b, c, d), payload) = packet; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         Ok(Command::Cp437Data( | ||||
|             Origin::new(*a as usize, *b as usize), | ||||
|             Cp437Grid::load(*c as usize, *d as usize, payload), | ||||
|  | @ -483,7 +546,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_invalid_command() { | ||||
|         let p = Packet(Header(0xFF, 0x00, 0x00, 0x00, 0x00), vec![]); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: 0xFF, | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -493,10 +565,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_values_clear() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::Clear.into(), 0x05, 0x00, 0x00, 0x00), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::Clear.into(), | ||||
|                 a: 0x05, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -506,10 +584,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_values_brightness() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::Brightness.into(), 0x00, 0x13, 0x37, 0x00), | ||||
|             vec![5], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::Brightness.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x13, | ||||
|                 c: 0x37, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![5], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -519,10 +603,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_hard_reset() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::HardReset.into(), 0x00, 0x00, 0x00, 0x01), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::HardReset.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x01, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -532,10 +622,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_extraneous_header_fade_out() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::FadeOut.into(), 0x10, 0x00, 0x00, 0x01), | ||||
|             vec![], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::FadeOut.into(), | ||||
|                 a: 0x10, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x01, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -545,10 +641,16 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_unexpected_payload() { | ||||
|         let p = Packet( | ||||
|             Header(CommandCode::FadeOut.into(), 0x00, 0x00, 0x00, 0x00), | ||||
|             vec![5, 7], | ||||
|         ); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: CommandCode::FadeOut.into(), | ||||
|                 a: 0x00, | ||||
|                 b: 0x00, | ||||
|                 c: 0x00, | ||||
|                 d: 0x00, | ||||
|             }, | ||||
|             payload: vec![5, 7], | ||||
|         }; | ||||
|         let result = Command::try_from(p); | ||||
|         assert!(matches!( | ||||
|             result, | ||||
|  | @ -565,14 +667,18 @@ mod tests { | |||
|                 compression, | ||||
|             ) | ||||
|             .into(); | ||||
|             let Packet(header, mut payload) = p; | ||||
| 
 | ||||
|             let Packet { | ||||
|                 header, | ||||
|                 mut payload, | ||||
|             } = p; | ||||
| 
 | ||||
|             // mangle it
 | ||||
|             for byte in payload.iter_mut() { | ||||
|                 *byte -= *byte / 2; | ||||
|             } | ||||
| 
 | ||||
|             let p = Packet(header, payload); | ||||
|             let p = Packet { header, payload }; | ||||
|             let result = Command::try_from(p); | ||||
|             if compression != CompressionCode::Uncompressed { | ||||
|                 assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) | ||||
|  | @ -591,14 +697,17 @@ mod tests { | |||
|                 compression, | ||||
|             ) | ||||
|             .into(); | ||||
|             let Packet(header, mut payload) = p; | ||||
|             let Packet { | ||||
|                 header, | ||||
|                 mut payload, | ||||
|             } = p; | ||||
| 
 | ||||
|             // mangle it
 | ||||
|             for byte in payload.iter_mut() { | ||||
|                 *byte -= *byte / 2; | ||||
|             } | ||||
| 
 | ||||
|             let p = Packet(header, payload); | ||||
|             let p = Packet { header, payload }; | ||||
|             let result = Command::try_from(p); | ||||
|             if compression != CompressionCode::Uncompressed { | ||||
|                 assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) | ||||
|  | @ -612,32 +721,59 @@ mod tests { | |||
|     #[test] | ||||
|     fn unexpected_payload_size_brightness() { | ||||
|         assert_eq!( | ||||
|             Command::try_from(Packet( | ||||
|                 Header(CommandCode::Brightness.into(), 0, 0, 0, 0), | ||||
|                 vec!(), | ||||
|             )), | ||||
|             Command::try_from(Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0, | ||||
|                     b: 0, | ||||
|                     c: 0, | ||||
|                     d: 0, | ||||
|                 }, | ||||
|                 payload: vec!() | ||||
|             }), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(1, 0)) | ||||
|         ); | ||||
| 
 | ||||
|         assert_eq!( | ||||
|             Command::try_from(Packet( | ||||
|                 Header(CommandCode::Brightness.into(), 0, 0, 0, 0), | ||||
|                 vec!(0, 0), | ||||
|             )), | ||||
|             Command::try_from(Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0, | ||||
|                     b: 0, | ||||
|                     c: 0, | ||||
|                     d: 0, | ||||
|                 }, | ||||
|                 payload: vec!(0, 0) | ||||
|             }), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize(1, 2)) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn error_reserved_used() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, sub, _reserved) = header; | ||||
|         let p = Packet(Header(command, offset, length, sub, 69), payload); | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: sub, | ||||
|             d: _reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: length, | ||||
|                 c: sub, | ||||
|                 d: 69, | ||||
|             }, | ||||
|             payload, | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::ExtraneousHeaderValues) | ||||
|  | @ -646,14 +782,29 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_invalid_compression() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, _sub, reserved) = header; | ||||
|         let p = Packet(Header(command, offset, length, 42, reserved), payload); | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: _sub, | ||||
|             d: reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: length, | ||||
|                 c: 42, | ||||
|                 d: reserved, | ||||
|             }, | ||||
|             payload, | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::InvalidCompressionCode(42)) | ||||
|  | @ -662,17 +813,29 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn error_unexpected_size() { | ||||
|         let Packet(header, payload) = Command::BitmapLinear( | ||||
|         let Packet { header, payload } = Command::BitmapLinear( | ||||
|             0, | ||||
|             BitVec::repeat(false, 8), | ||||
|             CompressionCode::Uncompressed, | ||||
|         ) | ||||
|         .into(); | ||||
|         let Header(command, offset, length, compression, reserved) = header; | ||||
|         let p = Packet( | ||||
|             Header(command, offset, 420, compression, reserved), | ||||
|         let Header { | ||||
|             command_code: command, | ||||
|             a: offset, | ||||
|             b: length, | ||||
|             c: compression, | ||||
|             d: reserved, | ||||
|         } = header; | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: command, | ||||
|                 a: offset, | ||||
|                 b: 420, | ||||
|                 c: compression, | ||||
|                 d: reserved, | ||||
|             }, | ||||
|             payload, | ||||
|         ); | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             Command::try_from(p), | ||||
|             Err(TryFromPacketError::UnexpectedPayloadSize( | ||||
|  |  | |||
|  | @ -9,14 +9,21 @@ use crate::Packet; | |||
| ///
 | ||||
| /// # Examples
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::Command;
 | ||||
| /// let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
| ///     .expect("connection failed");
 | ||||
| ///  connection.send(Command::Clear)
 | ||||
| ///  connection.send(servicepoint::Command::Clear)
 | ||||
| ///     .expect("send failed");
 | ||||
| /// ```
 | ||||
| pub struct Connection { | ||||
|     socket: UdpSocket, | ||||
| pub enum Connection { | ||||
|     /// A real connection using the UDP protocol
 | ||||
|     Udp(UdpSocket), | ||||
|     /// A fake connection for testing that does not actually send anything
 | ||||
|     Fake, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum SendError { | ||||
|     IoError(std::io::Error), | ||||
| } | ||||
| 
 | ||||
| impl Connection { | ||||
|  | @ -37,39 +44,37 @@ impl Connection { | |||
|         info!("connecting to {addr:?}"); | ||||
|         let socket = UdpSocket::bind("0.0.0.0:0")?; | ||||
|         socket.connect(addr)?; | ||||
|         Ok(Self { socket }) | ||||
|         Ok(Self::Udp(socket)) | ||||
|     } | ||||
| 
 | ||||
|     /// Send something packet-like to the display. Usually this is in the form of a Command.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `packet`: the packet-like to send
 | ||||
|     /// - `packet`: the packet-like to send
 | ||||
|     ///
 | ||||
|     /// returns: Ok if packet was sent, otherwise socket error
 | ||||
|     ///
 | ||||
|     /// # Errors
 | ||||
|     ///
 | ||||
|     /// Any errors produced while sending using the underlying socket.
 | ||||
|     /// returns: true if packet was sent, otherwise false
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
 | ||||
|     /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
 | ||||
|     /// #     .expect("connection failed");
 | ||||
|     /// # let connection = servicepoint::Connection::Fake;
 | ||||
|     ///  // turn off all pixels on display
 | ||||
|     ///  connection.send(Command::Clear)
 | ||||
|     ///  connection.send(servicepoint::Command::Clear)
 | ||||
|     ///      .expect("send failed");
 | ||||
|     /// ```
 | ||||
|     pub fn send( | ||||
|         &self, | ||||
|         packet: impl Into<Packet>, | ||||
|     ) -> Result<(), std::io::Error> { | ||||
|     pub fn send(&self, packet: impl Into<Packet>) -> Result<(), SendError> { | ||||
|         let packet = packet.into(); | ||||
|         debug!("sending {packet:?}"); | ||||
|         let data: Vec<u8> = packet.into(); | ||||
|         self.socket.send(&data)?; | ||||
|         Ok(()) | ||||
|         match self { | ||||
|             Connection::Udp(socket) => { | ||||
|                 socket | ||||
|                     .send(&data) | ||||
|                     .map_err(SendError::IoError) | ||||
|                     .map(move |_| ()) // ignore Ok value
 | ||||
|             } | ||||
|             Connection::Fake => Ok(()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ pub trait Grid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell to read
 | ||||
|     /// - `x` and `y`: position of the cell to read
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -15,7 +15,7 @@ pub trait Grid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell to read
 | ||||
|     /// - `x` and `y`: position of the cell to read
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -26,7 +26,7 @@ pub trait Grid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell to read
 | ||||
|     /// - `x` and `y`: position of the cell to read
 | ||||
|     ///
 | ||||
|     /// returns: Value at position or None
 | ||||
|     fn get_optional(&self, x: isize, y: isize) -> Option<T> { | ||||
|  | @ -41,7 +41,7 @@ pub trait Grid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell to read
 | ||||
|     /// - `x` and `y`: position of the cell to read
 | ||||
|     ///
 | ||||
|     /// returns: the old value or None
 | ||||
|     fn set_optional(&mut self, x: isize, y: isize, value: T) -> bool { | ||||
|  |  | |||
|  | @ -3,25 +3,84 @@ use std::mem::size_of; | |||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::into_compressed; | ||||
| use crate::{ | ||||
|     Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, | ||||
|     Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Tiles, | ||||
|     TILE_SIZE, | ||||
| }; | ||||
| 
 | ||||
| /// A raw header. Should probably not be used directly.
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16); | ||||
| /// A raw header.
 | ||||
| ///
 | ||||
| /// The header specifies the kind of command, the size of the payload and where to display the
 | ||||
| /// payload, where applicable.
 | ||||
| ///
 | ||||
| /// Because the meaning of most fields depend on the command, there are no speaking names for them.
 | ||||
| ///
 | ||||
| /// Should probably only be used directly to use features not exposed by the library.
 | ||||
| #[derive(Copy, Clone, Debug, PartialEq)] | ||||
| pub struct Header { | ||||
|     /// The first two bytes specify which command this packet represents.
 | ||||
|     pub command_code: u16, | ||||
|     /// First command-specific value
 | ||||
|     pub a: u16, | ||||
|     /// Second command-specific value
 | ||||
|     pub b: u16, | ||||
|     /// Third command-specific value
 | ||||
|     pub c: u16, | ||||
|     /// Fourth command-specific value
 | ||||
|     pub d: u16, | ||||
| } | ||||
| 
 | ||||
| /// The raw payload. Should probably not be used directly.
 | ||||
| /// The raw payload.
 | ||||
| ///
 | ||||
| /// Should probably only be used directly to use features not exposed by the library.
 | ||||
| pub type Payload = Vec<u8>; | ||||
| 
 | ||||
| /// The raw packet. Should probably not be used directly.
 | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Packet(pub Header, pub Payload); | ||||
| /// The raw packet.
 | ||||
| ///
 | ||||
| /// Contents should probably only be used directly to use features not exposed by the library.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// Converting a packet to a command and back:
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, Packet};
 | ||||
| /// # let command = Command::Clear;
 | ||||
| /// let packet: Packet = command.into();
 | ||||
| /// let command: Command = Command::try_from(packet).expect("could not read packet");
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// Converting a packet to bytes and back:
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Command, Packet};
 | ||||
| /// # let command = Command::Clear;
 | ||||
| /// # let packet: Packet = command.into();
 | ||||
| /// let bytes: Vec<u8> = packet.into();
 | ||||
| /// let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| pub struct Packet { | ||||
|     /// Meta-information for the packed command
 | ||||
|     pub header: Header, | ||||
|     /// The data for the packed command
 | ||||
|     pub payload: Payload, | ||||
| } | ||||
| 
 | ||||
| impl From<Packet> for Vec<u8> { | ||||
|     /// Turn the packet into raw bytes ready to send
 | ||||
|     fn from(value: Packet) -> Self { | ||||
|         let Packet(Header(mode, a, b, c, d), payload) = value; | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: mode, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = value; | ||||
| 
 | ||||
|         let mut packet = vec![0u8; 10 + payload.len()]; | ||||
|         packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode)); | ||||
|  | @ -36,13 +95,6 @@ impl From<Packet> for Vec<u8> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn u16_from_be_slice(slice: &[u8]) -> u16 { | ||||
|     let mut bytes = [0u8; 2]; | ||||
|     bytes[0] = slice[0]; | ||||
|     bytes[1] = slice[1]; | ||||
|     u16::from_be_bytes(bytes) | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<&[u8]> for Packet { | ||||
|     type Error = (); | ||||
| 
 | ||||
|  | @ -54,14 +106,31 @@ impl TryFrom<&[u8]> for Packet { | |||
|             return Err(()); | ||||
|         } | ||||
| 
 | ||||
|         let mode = u16_from_be_slice(&value[0..=1]); | ||||
|         let a = u16_from_be_slice(&value[2..=3]); | ||||
|         let b = u16_from_be_slice(&value[4..=5]); | ||||
|         let c = u16_from_be_slice(&value[6..=7]); | ||||
|         let d = u16_from_be_slice(&value[8..=9]); | ||||
|         let header = { | ||||
|             let command_code = Self::u16_from_be_slice(&value[0..=1]); | ||||
|             let a = Self::u16_from_be_slice(&value[2..=3]); | ||||
|             let b = Self::u16_from_be_slice(&value[4..=5]); | ||||
|             let c = Self::u16_from_be_slice(&value[6..=7]); | ||||
|             let d = Self::u16_from_be_slice(&value[8..=9]); | ||||
|             Header { | ||||
|                 command_code, | ||||
|                 a, | ||||
|                 b, | ||||
|                 c, | ||||
|                 d, | ||||
|             } | ||||
|         }; | ||||
|         let payload = value[10..].to_vec(); | ||||
| 
 | ||||
|         Ok(Packet(Header(mode, a, b, c, d), payload)) | ||||
|         Ok(Packet { header, payload }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Vec<u8>> for Packet { | ||||
|     type Error = (); | ||||
| 
 | ||||
|     fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { | ||||
|         Self::try_from(value.as_slice()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -79,26 +148,23 @@ impl From<Command> for Packet { | |||
|             Command::BitmapLegacy => { | ||||
|                 Self::command_code_only(CommandCode::BitmapLegacy) | ||||
|             } | ||||
|             Command::CharBrightness(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::CharBrightness.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             ), | ||||
|             Command::Brightness(brightness) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Brightness.into(), | ||||
|                     0x00000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                     0x0000, | ||||
|                 ), | ||||
|                 vec![brightness.into()], | ||||
|             ), | ||||
|             Command::CharBrightness(origin, grid) => { | ||||
|                 Self::origin_grid_to_packet( | ||||
|                     origin, | ||||
|                     grid, | ||||
|                     CommandCode::CharBrightness, | ||||
|                 ) | ||||
|             } | ||||
|             Command::Brightness(brightness) => Packet { | ||||
|                 header: Header { | ||||
|                     command_code: CommandCode::Brightness.into(), | ||||
|                     a: 0x00000, | ||||
|                     b: 0x0000, | ||||
|                     c: 0x0000, | ||||
|                     d: 0x0000, | ||||
|                 }, | ||||
|                 payload: vec![brightness.into()], | ||||
|             }, | ||||
|             Command::BitmapLinearWin(origin, pixels, compression) => { | ||||
|                 Self::bitmap_win_into_packet(origin, pixels, compression) | ||||
|             } | ||||
|  | @ -134,15 +200,10 @@ impl From<Command> for Packet { | |||
|                     bits.into(), | ||||
|                 ) | ||||
|             } | ||||
|             Command::Cp437Data(origin, grid) => Packet( | ||||
|                 Header( | ||||
|                     CommandCode::Cp437Data.into(), | ||||
|                     origin.x as u16, | ||||
|                     origin.y as u16, | ||||
|                     grid.width() as u16, | ||||
|                     grid.height() as u16, | ||||
|                 ), | ||||
|                 grid.into(), | ||||
|             Command::Cp437Data(origin, grid) => Self::origin_grid_to_packet( | ||||
|                 origin, | ||||
|                 grid, | ||||
|                 CommandCode::Cp437Data, | ||||
|             ), | ||||
|         } | ||||
|     } | ||||
|  | @ -159,16 +220,16 @@ impl Packet { | |||
|     ) -> Packet { | ||||
|         let length = payload.len() as u16; | ||||
|         let payload = into_compressed(compression, payload); | ||||
|         Packet( | ||||
|             Header( | ||||
|                 command.into(), | ||||
|                 offset as u16, | ||||
|                 length, | ||||
|                 compression.into(), | ||||
|                 0, | ||||
|             ), | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command.into(), | ||||
|                 a: offset as u16, | ||||
|                 b: length, | ||||
|                 c: compression.into(), | ||||
|                 d: 0, | ||||
|             }, | ||||
|             payload, | ||||
|         ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[allow(clippy::cast_possible_truncation)] | ||||
|  | @ -198,15 +259,54 @@ impl Packet { | |||
|             CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, | ||||
|         }; | ||||
| 
 | ||||
|         Packet( | ||||
|             Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h), | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command.into(), | ||||
|                 a: tile_x, | ||||
|                 b: origin.y as u16, | ||||
|                 c: tile_w, | ||||
|                 d: pixel_h, | ||||
|             }, | ||||
|             payload, | ||||
|         ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for creating empty packets only containing the command code
 | ||||
|     fn command_code_only(code: CommandCode) -> Packet { | ||||
|         Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![]) | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: code.into(), | ||||
|                 a: 0x0000, | ||||
|                 b: 0x0000, | ||||
|                 c: 0x0000, | ||||
|                 d: 0x0000, | ||||
|             }, | ||||
|             payload: vec![], | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn u16_from_be_slice(slice: &[u8]) -> u16 { | ||||
|         let mut bytes = [0u8; 2]; | ||||
|         bytes[0] = slice[0]; | ||||
|         bytes[1] = slice[1]; | ||||
|         u16::from_be_bytes(bytes) | ||||
|     } | ||||
| 
 | ||||
|     fn origin_grid_to_packet<T>( | ||||
|         origin: Origin<Tiles>, | ||||
|         grid: impl Grid<T> + Into<Payload>, | ||||
|         command_code: CommandCode, | ||||
|     ) -> Packet { | ||||
|         Packet { | ||||
|             header: Header { | ||||
|                 command_code: command_code.into(), | ||||
|                 a: origin.x as u16, | ||||
|                 b: origin.y as u16, | ||||
|                 c: grid.width() as u16, | ||||
|                 d: grid.height() as u16, | ||||
|             }, | ||||
|             payload: grid.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -216,10 +316,31 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip() { | ||||
|         let p = Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23]); | ||||
|         let p = Packet { | ||||
|             header: Header { | ||||
|                 command_code: 0, | ||||
|                 a: 1, | ||||
|                 b: 2, | ||||
|                 c: 3, | ||||
|                 d: 4, | ||||
|             }, | ||||
|             payload: vec![42u8; 23], | ||||
|         }; | ||||
|         let data: Vec<u8> = p.into(); | ||||
|         let p = Packet::try_from(&*data).unwrap(); | ||||
|         assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23])); | ||||
|         assert_eq!( | ||||
|             p, | ||||
|             Packet { | ||||
|                 header: Header { | ||||
|                     command_code: 0, | ||||
|                     a: 1, | ||||
|                     b: 2, | ||||
|                     c: 3, | ||||
|                     d: 4 | ||||
|                 }, | ||||
|                 payload: vec![42u8; 23] | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|  |  | |||
|  | @ -17,8 +17,8 @@ impl PixelGrid { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `width`: size in pixels in x-direction
 | ||||
|     /// * `height`: size in pixels in y-direction
 | ||||
|     /// - `width`: size in pixels in x-direction
 | ||||
|     /// - `height`: size in pixels in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: `PixelGrid` initialized to all pixels off
 | ||||
|     ///
 | ||||
|  | @ -44,8 +44,8 @@ impl PixelGrid { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `width`: size in pixels in x-direction
 | ||||
|     /// * `height`: size in pixels in y-direction
 | ||||
|     /// - `width`: size in pixels in x-direction
 | ||||
|     /// - `height`: size in pixels in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: `PixelGrid` that contains a copy of the provided data
 | ||||
|     ///
 | ||||
|  | @ -121,8 +121,8 @@ impl Grid<bool> for PixelGrid { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell
 | ||||
|     /// * `value`: the value to write to the cell
 | ||||
|     /// - `x` and `y`: position of the cell
 | ||||
|     /// - `value`: the value to write to the cell
 | ||||
|     ///
 | ||||
|     /// returns: old value of the cell
 | ||||
|     ///
 | ||||
|  | @ -143,8 +143,8 @@ impl Grid<bool> for PixelGrid { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `this`: instance to write to
 | ||||
|     /// * `value`: the value to set all pixels to
 | ||||
|     /// - `this`: instance to write to
 | ||||
|     /// - `value`: the value to set all pixels to
 | ||||
|     fn fill(&mut self, value: bool) { | ||||
|         self.bit_vec.fill(value); | ||||
|     } | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell
 | ||||
|     /// - `x` and `y`: position of the cell
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -96,7 +96,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell
 | ||||
|     /// - `x` and `y`: position of the cell
 | ||||
|     ///
 | ||||
|     /// returns: Reference to cell or None
 | ||||
|     pub fn get_ref_mut_optional( | ||||
|  | @ -117,8 +117,8 @@ impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell
 | ||||
|     /// * `value`: the value to write to the cell
 | ||||
|     /// - `x` and `y`: position of the cell
 | ||||
|     /// - `value`: the value to write to the cell
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  | @ -132,7 +132,7 @@ impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | |||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// * `x` and `y`: position of the cell to read
 | ||||
|     /// - `x` and `y`: position of the cell to read
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|  |  | |||
|  | @ -14,10 +14,10 @@ links = "servicepoint" | |||
| crate-type = ["staticlib", "cdylib", "rlib"] | ||||
| 
 | ||||
| [build-dependencies] | ||||
| cbindgen = "0.26.0" | ||||
| cbindgen = "0.27.0" | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.7.0" | ||||
| version = "0.8.0" | ||||
| path = "../servicepoint" | ||||
| features = ["all_compressions"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,19 +17,18 @@ This crate contains C bindings for the `servicepoint` library, enabling users to | |||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     sp_Connection *connection = sp_connection_open("localhost:2342"); | ||||
|     SPConnection *connection = sp_connection_open("172.23.42.29:2342"); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT); | ||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_pixel_grid_fill(pixels, true); | ||||
| 
 | ||||
|     sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     sp_Packet *packet = sp_packet_from_command(command); | ||||
|     if (!sp_connection_send(connection, packet)) | ||||
|         return 1; | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||
| 
 | ||||
|     sp_connection_dealloc(connection); | ||||
|     sp_command_free(command); | ||||
|     sp_connection_free(connection); | ||||
|     return 0; | ||||
| } | ||||
| ``` | ||||
|  | @ -53,7 +52,7 @@ You have the choice of linking statically (recommended) or dynamically. | |||
| ## Notes on differences to rust library | ||||
| 
 | ||||
| - function names are: `sp_` \<struct_name\> \<rust name\>. | ||||
| - Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free. | ||||
| - Instances get consumed in the same way they do when writing rust code. Do not use an instance after an (implicit!) free. | ||||
| - Option<T> or Result<T, E> turn into nullable return values - check for NULL! | ||||
| - There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable. | ||||
| - Reading and writing to instances concurrently is not safe. Only reading concurrently is safe. | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ language = "C" | |||
| include_version = true | ||||
| cpp_compat = true | ||||
| 
 | ||||
| autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" | ||||
| 
 | ||||
| ############################ Code Style Options ################################ | ||||
| 
 | ||||
| braces = "SameLine" | ||||
|  | @ -15,22 +17,17 @@ line_endings = "LF" | |||
| ############################# Codegen Options ################################## | ||||
| 
 | ||||
| style = "both" | ||||
| sort_by = "Name" | ||||
| usize_is_size_t = true | ||||
| 
 | ||||
| [defines] | ||||
| #"feature = compression_zlib" = "SP_FEATURE_compression_zlib" | ||||
| #"feature = compression_bzip2" = "SP_FEATURE_compression_bzip2" | ||||
| #"feature = compression_lzma" = "SP_FEATURE_compression_lzma" | ||||
| #"feature = compression_zstd" = "SP_FEATURE_compression_zstd" | ||||
| 
 | ||||
| [export] | ||||
| prefix = "sp_" | ||||
| # this is needed because otherwise the order in the C# bindings is different on different machines | ||||
| sort_by = "Name" | ||||
| 
 | ||||
| [parse] | ||||
| parse_deps = true | ||||
| include = ["servicepoint"] | ||||
| extra_bindings = ["servicepoint"] | ||||
| parse_deps = false | ||||
| 
 | ||||
| [parse.expand] | ||||
| all_features = true | ||||
| 
 | ||||
| [export] | ||||
| include = [] | ||||
| exclude = [] | ||||
|  |  | |||
|  | @ -6,8 +6,9 @@ REPO_ROOT := $(THIS_DIR)/../../../.. | |||
| build: out/lang_c | ||||
| 
 | ||||
| clean: | ||||
| 	rm -r out | ||||
| 	rm include/servicepoint.h | ||||
| 	rm -r out || true | ||||
| 	rm include/servicepoint.h || true | ||||
| 	cargo clean | ||||
| 
 | ||||
| run: out/lang_c | ||||
| 	out/lang_c | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -2,18 +2,17 @@ | |||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     sp_Connection *connection = sp_connection_open("localhost:2342"); | ||||
|     SPConnection *connection = sp_connection_open("172.23.42.29:2342"); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|     sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT); | ||||
|     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT); | ||||
|     sp_pixel_grid_fill(pixels, true); | ||||
| 
 | ||||
|     sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     sp_Packet *packet = sp_packet_from_command(command); | ||||
|     if (!sp_connection_send(connection, packet)) | ||||
|         return 1; | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed); | ||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||
| 
 | ||||
|     sp_connection_dealloc(connection); | ||||
|     sp_command_free(command); | ||||
|     sp_connection_free(connection); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,38 +1,45 @@ | |||
| //! C functions for interacting with `BitVec`s
 | ||||
| //! C functions for interacting with `SPBitVec`s
 | ||||
| //!
 | ||||
| //! prefix `sp_bit_vec_`
 | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::bitvec::prelude::{BitVec, Msb0}; | ||||
| 
 | ||||
| /// cbindgen:no-export
 | ||||
| type SpBitVec = BitVec<u8, Msb0>; | ||||
| /// A vector of bits
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| /// ```C
 | ||||
| /// SPBitVec vec = sp_bit_vec_new(8);
 | ||||
| /// sp_bit_vec_set(vec, 5, true);
 | ||||
| /// sp_bit_vec_free(vec);
 | ||||
| /// ```
 | ||||
| pub struct SPBitVec(BitVec<u8, Msb0>); | ||||
| 
 | ||||
| /// Opaque struct needed for correct code generation.
 | ||||
| #[derive(Clone)] | ||||
| pub struct CBitVec { | ||||
|     actual: SpBitVec, | ||||
| } | ||||
| 
 | ||||
| impl From<SpBitVec> for CBitVec { | ||||
|     fn from(actual: SpBitVec) -> Self { | ||||
|         Self { actual } | ||||
| impl From<BitVec<u8, Msb0>> for SPBitVec { | ||||
|     fn from(actual: BitVec<u8, Msb0>) -> Self { | ||||
|         Self(actual) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CBitVec> for SpBitVec { | ||||
|     fn from(value: CBitVec) -> Self { | ||||
|         value.actual | ||||
| impl From<SPBitVec> for BitVec<u8, Msb0> { | ||||
|     fn from(value: SPBitVec) -> Self { | ||||
|         value.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `BitVec` instance.
 | ||||
| impl Clone for SPBitVec { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPBitVec(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPBitVec` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `size`: size in bits.
 | ||||
| /// - `size`: size in bits.
 | ||||
| ///
 | ||||
| /// returns: `BitVec` with all bits set to false.
 | ||||
| /// returns: `SPBitVec` with all bits set to false. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -43,15 +50,13 @@ impl From<CBitVec> for SpBitVec { | |||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_dealloc`.
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut CBitVec { | ||||
|     Box::into_raw(Box::new(CBitVec { | ||||
|         actual: SpBitVec::repeat(false, size), | ||||
|     })) | ||||
| pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut SPBitVec { | ||||
|     Box::into_raw(Box::new(SPBitVec(BitVec::repeat(false, size)))) | ||||
| } | ||||
| 
 | ||||
| /// Interpret the data as a series of bits and load then into a new `BitVec` instance.
 | ||||
| /// Interpret the data as a series of bits and load then into a new `SPBitVec` instance.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  | @ -60,55 +65,53 @@ pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut CBitVec { | |||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_dealloc`.
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_load( | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut CBitVec { | ||||
| ) -> *mut SPBitVec { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(CBitVec { | ||||
|         actual: SpBitVec::from_slice(data), | ||||
|     })) | ||||
|     Box::into_raw(Box::new(SPBitVec(BitVec::from_slice(data)))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `BitVec`.
 | ||||
| /// Clones a `SPBitVec`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_bit_vec_dealloc`.
 | ||||
| ///   by explicitly calling `sp_bit_vec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_clone( | ||||
|     this: *const CBitVec, | ||||
| ) -> *mut CBitVec { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
|     bit_vec: *const SPBitVec, | ||||
| ) -> *mut SPBitVec { | ||||
|     Box::into_raw(Box::new((*bit_vec).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `BitVec`.
 | ||||
| /// Deallocates a `SPBitVec`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `bit_vec` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut CBitVec) { | ||||
|     _ = Box::from_raw(this); | ||||
| pub unsafe extern "C" fn sp_bit_vec_free(bit_vec: *mut SPBitVec) { | ||||
|     _ = Box::from_raw(bit_vec); | ||||
| } | ||||
| 
 | ||||
| /// Gets the value of a bit from the `BitVec`.
 | ||||
| /// Gets the value of a bit from the `SPBitVec`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// * `index`: the bit index to read
 | ||||
| /// - `bit_vec`: instance to read from
 | ||||
| /// - `index`: the bit index to read
 | ||||
| ///
 | ||||
| /// returns: value of the bit
 | ||||
| ///
 | ||||
|  | @ -120,23 +123,23 @@ pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut CBitVec) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_get( | ||||
|     this: *const CBitVec, | ||||
|     bit_vec: *const SPBitVec, | ||||
|     index: usize, | ||||
| ) -> bool { | ||||
|     *(*this).actual.get(index).unwrap() | ||||
|     *(*bit_vec).0.get(index).unwrap() | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of a bit in the `BitVec`.
 | ||||
| /// Sets the value of a bit in the `SPBitVec`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `index`: the bit index to edit
 | ||||
| /// * `value`: the value to set the bit to
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| /// - `index`: the bit index to edit
 | ||||
| /// - `value`: the value to set the bit to
 | ||||
| ///
 | ||||
| /// returns: old value of the bit
 | ||||
| ///
 | ||||
|  | @ -148,73 +151,86 @@ pub unsafe extern "C" fn sp_bit_vec_get( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_set( | ||||
|     this: *mut CBitVec, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     index: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     (*this).actual.set(index, value) | ||||
|     (*bit_vec).0.set(index, value) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all bits in the `BitVec`.
 | ||||
| /// Sets the value of all bits in the `SPBitVec`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `value`: the value to set all bits to
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| /// - `value`: the value to set all bits to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - `bit_vec` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut CBitVec, value: bool) { | ||||
|     (*this).actual.fill(value) | ||||
| pub unsafe extern "C" fn sp_bit_vec_fill(bit_vec: *mut SPBitVec, value: bool) { | ||||
|     (*bit_vec).0.fill(value) | ||||
| } | ||||
| 
 | ||||
| /// Gets the length of the `BitVec` in bits.
 | ||||
| /// Gets the length of the `SPBitVec` in bits.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_len(this: *const CBitVec) -> usize { | ||||
|     (*this).actual.len() | ||||
| pub unsafe extern "C" fn sp_bit_vec_len(bit_vec: *const SPBitVec) -> usize { | ||||
|     (*bit_vec).0.len() | ||||
| } | ||||
| 
 | ||||
| /// Returns true if length is 0.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const CBitVec) -> bool { | ||||
|     (*this).actual.is_empty() | ||||
| pub unsafe extern "C" fn sp_bit_vec_is_empty(bit_vec: *const SPBitVec) -> bool { | ||||
|     (*bit_vec).0.is_empty() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `BitVec` instance.
 | ||||
| /// Gets an unsafe reference to the data of the `SPBitVec` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `bit_vec`: instance to write to
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - the returned memory range is never accessed after the passed `BitVec` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `BitVec` or directly
 | ||||
| /// - `bit_vec` points to a valid `SPBitVec`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPBitVec` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBitVec` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref( | ||||
|     this: *mut CBitVec, | ||||
| ) -> CByteSlice { | ||||
|     let data = (*this).actual.as_raw_mut_slice(); | ||||
|     CByteSlice { | ||||
|     bit_vec: *mut SPBitVec, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*bit_vec).0.as_raw_mut_slice(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
|  |  | |||
|  | @ -1,37 +1,55 @@ | |||
| //! C functions for interacting with `BrightnessGrid`s
 | ||||
| //! C functions for interacting with `SPBrightnessGrid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_brightness_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{Brightness, BrightnessGrid, DataRef, Grid, PrimitiveGrid}; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; | ||||
| use std::intrinsics::transmute; | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| 
 | ||||
| /// C-wrapper for grid containing brightness values.
 | ||||
| #[derive(Clone)] | ||||
| pub struct CBrightnessGrid(pub(crate) BrightnessGrid); | ||||
| 
 | ||||
| /// Creates a new `BrightnessGrid` with the specified dimensions.
 | ||||
| /// A grid containing brightness values.
 | ||||
| ///
 | ||||
| /// returns: `BrightnessGrid` initialized to 0.
 | ||||
| /// # Examples
 | ||||
| /// ```C
 | ||||
| /// SPConnection connection = sp_connection_open("127.0.0.1:2342");
 | ||||
| /// if (connection == NULL)
 | ||||
| ///     return 1;
 | ||||
| ///
 | ||||
| /// SPBrightnessGrid grid = sp_brightness_grid_new(2, 2);
 | ||||
| /// sp_brightness_grid_set(grid, 0, 0, 0);
 | ||||
| /// sp_brightness_grid_set(grid, 1, 1, 10);
 | ||||
| ///
 | ||||
| /// SPCommand command = sp_command_char_brightness(grid);
 | ||||
| /// sp_connection_free(connection);
 | ||||
| /// ```
 | ||||
| pub struct SPBrightnessGrid(pub(crate) servicepoint::BrightnessGrid); | ||||
| 
 | ||||
| impl Clone for SPBrightnessGrid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPBrightnessGrid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPBrightnessGrid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `SPBrightnessGrid` initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut CBrightnessGrid { | ||||
|     Box::into_raw(Box::new(CBrightnessGrid(BrightnessGrid::new( | ||||
|         width, height, | ||||
|     )))) | ||||
| ) -> *mut SPBrightnessGrid { | ||||
|     Box::into_raw(Box::new(SPBrightnessGrid( | ||||
|         servicepoint::BrightnessGrid::new(width, height), | ||||
|     ))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `BrightnessGrid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a `SPBrightnessGrid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -44,60 +62,68 @@ pub unsafe extern "C" fn sp_brightness_grid_new( | |||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut CBrightnessGrid { | ||||
| ) -> *mut SPBrightnessGrid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let grid = PrimitiveGrid::load(width, height, data); | ||||
|     let grid = | ||||
|         BrightnessGrid::try_from(grid).expect("invalid brightness value"); | ||||
|     Box::into_raw(Box::new(CBrightnessGrid(grid))) | ||||
|     let grid = servicepoint::BrightnessGrid::try_from(grid) | ||||
|         .expect("invalid brightness value"); | ||||
|     Box::into_raw(Box::new(SPBrightnessGrid(grid))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `BrightnessGrid`.
 | ||||
| /// Clones a `SPBrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_brightness_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_brightness_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_clone( | ||||
|     this: *const CBrightnessGrid, | ||||
| ) -> *mut CBrightnessGrid { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> *mut SPBrightnessGrid { | ||||
|     Box::into_raw(Box::new((*brightness_grid).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `BrightnessGrid`.
 | ||||
| /// Deallocates a `SPBrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` is not used concurrently or after this call
 | ||||
| /// - `brightness_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_dealloc( | ||||
|     this: *mut CBrightnessGrid, | ||||
| pub unsafe extern "C" fn sp_brightness_grid_free( | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
| ) { | ||||
|     _ = Box::from_raw(this); | ||||
|     _ = Box::from_raw(brightness_grid); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// * `x` and `y`: position of the cell to read
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -107,126 +133,135 @@ pub unsafe extern "C" fn sp_brightness_grid_dealloc( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_get( | ||||
|     this: *const CBrightnessGrid, | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     (*this).0.get(x, y).into() | ||||
|     (*brightness_grid).0.get(x, y).into() | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `BrightnessGrid`.
 | ||||
| /// Sets the value of the specified position in the `SPBrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `x` and `y`: position of the cell
 | ||||
| /// * `value`: the value to write to the cell
 | ||||
| /// - `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.
 | ||||
| /// - When accessing `x` or `y` out of bounds.
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `brightness_grid` points to a valid `SPBitVec`
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_set( | ||||
|     this: *mut CBrightnessGrid, | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*this).0.set(x, y, brightness); | ||||
|     (*brightness_grid).0.set(x, y, brightness); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `BrightnessGrid`.
 | ||||
| /// Sets the value of all cells in the `SPBrightnessGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `value`: the value to set all cells to
 | ||||
| /// - `brightness_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - When providing an invalid brightness value
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - `brightness_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_fill( | ||||
|     this: *mut CBrightnessGrid, | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
|     value: u8, | ||||
| ) { | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     (*this).0.fill(brightness); | ||||
|     (*brightness_grid).0.fill(brightness); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `BrightnessGrid` instance.
 | ||||
| /// Gets the width of the `SPBrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_width( | ||||
|     this: *const CBrightnessGrid, | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> usize { | ||||
|     (*this).0.width() | ||||
|     (*brightness_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `BrightnessGrid` instance.
 | ||||
| /// Gets the height of the `SPBrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_height( | ||||
|     this: *const CBrightnessGrid, | ||||
|     brightness_grid: *const SPBrightnessGrid, | ||||
| ) -> usize { | ||||
|     (*this).0.height() | ||||
|     (*brightness_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `BrightnessGrid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the `SPBrightnessGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `brightness_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BrightnessGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `BrightnessGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `BrightnessGrid` or directly
 | ||||
| /// - `brightness_grid` points to a valid `SPBrightnessGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPBrightnessGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPBrightnessGrid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | ||||
|     this: *mut CBrightnessGrid, | ||||
| ) -> CByteSlice { | ||||
|     assert_eq!(std::mem::size_of::<Brightness>(), 1); | ||||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
| ) -> SPByteSlice { | ||||
|     assert_eq!(core::mem::size_of::<Brightness>(), 1); | ||||
| 
 | ||||
|     let data = (*this).0.data_ref_mut(); | ||||
|     let data = (*brightness_grid).0.data_ref_mut(); | ||||
|     let data: &mut [u8] = transmute(data); | ||||
|     CByteSlice { | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| #[repr(C)] | ||||
| /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
 | ||||
| ///
 | ||||
| /// You should not create an instance of this type in your C code.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
|  | @ -10,7 +12,9 @@ | |||
| /// - 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.
 | ||||
| pub struct CByteSlice { | ||||
| /// - an instance of this created from C is never passed to a consuming function, as the rust code
 | ||||
| ///   will try to free the memory of a potentially separate allocator.
 | ||||
| pub struct SPByteSlice { | ||||
|     /// The start address of the memory
 | ||||
|     pub start: *mut u8, | ||||
|     /// The amount of memory in bytes
 | ||||
|  | @ -1,99 +1,137 @@ | |||
| //! C functions for interacting with `Command`s
 | ||||
| //! C functions for interacting with `SPCommand`s
 | ||||
| //!
 | ||||
| //! prefix `sp_command_`
 | ||||
| 
 | ||||
| use std::ptr::null_mut; | ||||
| 
 | ||||
| use servicepoint::{ | ||||
|     Brightness, Command, CompressionCode, Offset, Origin, Packet, PixelGrid, | ||||
| use servicepoint::{Brightness, Origin}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     SPBitVec, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, SPPacket, | ||||
|     SPPixelGrid, | ||||
| }; | ||||
| 
 | ||||
| use crate::bit_vec::CBitVec; | ||||
| use crate::brightness_grid::CBrightnessGrid; | ||||
| use crate::cp437_grid::CCp437Grid; | ||||
| 
 | ||||
| /// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.
 | ||||
| /// A low-level display command.
 | ||||
| ///
 | ||||
| /// Returns: pointer to new `Command` instance or NULL
 | ||||
| /// This struct and associated functions implement the UDP protocol for the display.
 | ||||
| ///
 | ||||
| /// To send a `SPCommand`, use a `SPConnection`.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// sp_connection_send_command(connection, sp_command_brightness(5));
 | ||||
| /// ```
 | ||||
| pub struct SPCommand(pub(crate) servicepoint::Command); | ||||
| 
 | ||||
| impl Clone for SPCommand { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCommand(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Tries to turn a `SPPacket` into a `SPCommand`.
 | ||||
| ///
 | ||||
| /// The packet is deallocated in the process.
 | ||||
| ///
 | ||||
| /// Returns: pointer to new `SPCommand` instance or NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid instance of `Packet`
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| /// - `SPPacket` points to a valid instance of `SPPacket`
 | ||||
| /// - `SPPacket` is not used concurrently or after this call
 | ||||
| /// - the result is checked for NULL
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_try_from_packet( | ||||
|     packet: *mut Packet, | ||||
| ) -> *mut Command { | ||||
|     packet: *mut SPPacket, | ||||
| ) -> *mut SPCommand { | ||||
|     let packet = *Box::from_raw(packet); | ||||
|     match Command::try_from(packet) { | ||||
|     match servicepoint::Command::try_from(packet.0) { | ||||
|         Err(_) => null_mut(), | ||||
|         Ok(command) => Box::into_raw(Box::new(command)), | ||||
|         Ok(command) => Box::into_raw(Box::new(SPCommand(command))), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Clones a `Command` instance.
 | ||||
| /// Clones a `SPCommand` instance.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid instance of `Command`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - `command` points to a valid instance of `SPCommand`
 | ||||
| /// - `command` is not written to concurrently
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clone( | ||||
|     original: *const Command, | ||||
| ) -> *mut Command { | ||||
|     Box::into_raw(Box::new((*original).clone())) | ||||
|     command: *const SPCommand, | ||||
| ) -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new((*command).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::Clear` instance.
 | ||||
| /// Set all pixels to the off state.
 | ||||
| ///
 | ||||
| /// Does not affect brightness.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Clear` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_clear() -> *mut Command { | ||||
|     Box::into_raw(Box::new(Command::Clear)) | ||||
| pub unsafe extern "C" fn sp_command_clear() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Clear))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::HardReset` instance.
 | ||||
| /// Kills the udp daemon on the display, which usually results in a restart.
 | ||||
| ///
 | ||||
| /// Please do not send this in your normal program flow.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::HardReset` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> *mut Command { | ||||
|     Box::into_raw(Box::new(Command::HardReset)) | ||||
| pub unsafe extern "C" fn sp_command_hard_reset() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::HardReset))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::FadeOut` instance.
 | ||||
| /// A yet-to-be-tested command.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::FadeOut` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { | ||||
|     Box::into_raw(Box::new(Command::FadeOut)) | ||||
| pub unsafe extern "C" fn sp_command_fade_out() -> *mut SPCommand { | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::FadeOut))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the
 | ||||
| /// same value.
 | ||||
| /// Set the brightness of all tiles to the same value.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Brightness` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -103,201 +141,263 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_brightness(brightness: u8) -> *mut Command { | ||||
| pub unsafe extern "C" fn sp_command_brightness( | ||||
|     brightness: u8, | ||||
| ) -> *mut SPCommand { | ||||
|     let brightness = | ||||
|         Brightness::try_from(brightness).expect("invalid brightness"); | ||||
|     Box::into_raw(Box::new(Command::Brightness(brightness))) | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Brightness( | ||||
|         brightness, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::CharBrightness` instance.
 | ||||
| /// The passed `ByteGrid` gets consumed.
 | ||||
| /// Set the brightness of individual tiles in a rectangular area of the display.
 | ||||
| ///
 | ||||
| /// The passed `SPBrightnessGrid` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::CharBrightness` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `byte_grid` points to a valid instance of `ByteGrid`
 | ||||
| /// - `byte_grid` is not used concurrently or after this call
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - `grid` points to a valid instance of `SPBrightnessGrid`
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_char_brightness( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     byte_grid: *mut CBrightnessGrid, | ||||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(byte_grid); | ||||
|     Box::into_raw(Box::new(Command::CharBrightness( | ||||
|     grid: *mut SPBrightnessGrid, | ||||
| ) -> *mut SPCommand { | ||||
|     let byte_grid = *Box::from_raw(grid); | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::CharBrightness( | ||||
|         Origin::new(x, y), | ||||
|         byte_grid.0, | ||||
|     ))) | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinear` instance.
 | ||||
| /// The passed `BitVec` gets consumed.
 | ||||
| /// Set pixel data starting at the pixel offset on screen.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinear` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `BitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear( | ||||
|     offset: Offset, | ||||
|     bit_vec: *mut CBitVec, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Command { | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinear( | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinear( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression, | ||||
|     ))) | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinearAnd` instance.
 | ||||
| /// The passed `BitVec` gets consumed.
 | ||||
| /// Set pixel data according to an and-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearAnd` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `BitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_and( | ||||
|     offset: Offset, | ||||
|     bit_vec: *mut CBitVec, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Command { | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinearAnd( | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression, | ||||
|     ))) | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinearOr` instance.
 | ||||
| /// The passed `BitVec` gets consumed.
 | ||||
| /// Set pixel data according to an or-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearOr` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `BitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_or( | ||||
|     offset: Offset, | ||||
|     bit_vec: *mut CBitVec, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Command { | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinearOr( | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression, | ||||
|     ))) | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinearXor` instance.
 | ||||
| /// The passed `BitVec` gets consumed.
 | ||||
| /// Set pixel data according to a xor-mask starting at the offset.
 | ||||
| ///
 | ||||
| /// The screen will continuously overwrite more pixel data without regarding the offset, meaning
 | ||||
| /// once the starting row is full, overwriting will continue on column 0.
 | ||||
| ///
 | ||||
| /// The contained `SPBitVec` is always uncompressed.
 | ||||
| ///
 | ||||
| /// The passed `SPBitVec` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearXor` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `bit_vec` points to a valid instance of `BitVec`
 | ||||
| /// - `bit_vec` points to a valid instance of `SPBitVec`
 | ||||
| /// - `bit_vec` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | ||||
|     offset: Offset, | ||||
|     bit_vec: *mut CBitVec, | ||||
|     compression: CompressionCode, | ||||
| ) -> *mut Command { | ||||
|     offset: usize, | ||||
|     bit_vec: *mut SPBitVec, | ||||
|     compression: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinearXor( | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression, | ||||
|     ))) | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::Cp437Data` instance.
 | ||||
| /// The passed `ByteGrid` gets consumed.
 | ||||
| /// Show text on the screen.
 | ||||
| ///
 | ||||
| /// <div class="warning">
 | ||||
| ///     The library does not currently convert between UTF-8 and CP-437.
 | ||||
| ///     Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
 | ||||
| /// </div>
 | ||||
| ///
 | ||||
| /// The passed `SPCp437Grid` gets consumed.///
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::Cp437Data` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `byte_grid` points to a valid instance of `ByteGrid`
 | ||||
| /// - `byte_grid` is not used concurrently or after this call
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - `grid` points to a valid instance of `SPCp437Grid`
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_cp437_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     byte_grid: *mut CCp437Grid, | ||||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(byte_grid); | ||||
|     Box::into_raw(Box::new(Command::Cp437Data(Origin::new(x, y), byte_grid.0))) | ||||
|     grid: *mut SPCp437Grid, | ||||
| ) -> *mut SPCommand { | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::Cp437Data( | ||||
|         Origin::new(x, y), | ||||
|         grid.0, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Allocates a new `Command::BitmapLinearWin` instance.
 | ||||
| /// The passed `PixelGrid` gets consumed.
 | ||||
| /// Sets a window of pixels to the specified values.
 | ||||
| ///
 | ||||
| /// The passed `SPPixelGrid` gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new `Command::BitmapLinearWin` instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `pixel_grid` points to a valid instance of `PixelGrid`
 | ||||
| /// - `pixel_grid` points to a valid instance of `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not used concurrently or after this call
 | ||||
| /// - `compression` matches one of the allowed enum values
 | ||||
| /// - the returned `Command` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_dealloc`.
 | ||||
| /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_bitmap_linear_win( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     pixel_grid: *mut PixelGrid, | ||||
|     compression_code: CompressionCode, | ||||
| ) -> *mut Command { | ||||
|     let byte_grid = *Box::from_raw(pixel_grid); | ||||
|     Box::into_raw(Box::new(Command::BitmapLinearWin( | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     compression_code: SPCompressionCode, | ||||
| ) -> *mut SPCommand { | ||||
|     let byte_grid = (*Box::from_raw(pixel_grid)).0; | ||||
|     Box::into_raw(Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( | ||||
|         Origin::new(x, y), | ||||
|         byte_grid, | ||||
|         compression_code, | ||||
|     ))) | ||||
|         compression_code | ||||
|             .try_into() | ||||
|             .expect("invalid compression code"), | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `Command`.
 | ||||
| /// Deallocates a `SPCommand`.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// SPCommand c = sp_command_clear();
 | ||||
| /// sp_command_free(c);
 | ||||
| /// ```
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Command`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Packet`
 | ||||
| /// - `command` points to a valid `SPCommand`
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| /// - `command` was not passed to another consuming function, e.g. to create a `SPPacket`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_dealloc(ptr: *mut Command) { | ||||
|     _ = Box::from_raw(ptr); | ||||
| pub unsafe extern "C" fn sp_command_free(command: *mut SPCommand) { | ||||
|     _ = Box::from_raw(command); | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,24 @@ | |||
| //! C functions for interacting with `Connection`s
 | ||||
| //! C functions for interacting with `SPConnection`s
 | ||||
| //!
 | ||||
| //! prefix `sp_connection_`
 | ||||
| 
 | ||||
| use std::ffi::{c_char, CStr}; | ||||
| use std::ptr::null_mut; | ||||
| 
 | ||||
| use servicepoint::{Connection, Packet}; | ||||
| use crate::{SPCommand, SPPacket}; | ||||
| 
 | ||||
| /// Creates a new instance of `Connection`.
 | ||||
| /// A connection to the display.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// CConnection connection = sp_connection_open("172.23.42.29:2342");
 | ||||
| /// if (connection != NULL)
 | ||||
| ///     sp_connection_send_command(connection, sp_command_clear());
 | ||||
| /// ```
 | ||||
| pub struct SPConnection(pub(crate) servicepoint::Connection); | ||||
| 
 | ||||
| /// Creates a new instance of `SPConnection`.
 | ||||
| ///
 | ||||
| /// returns: NULL if connection fails, or connected instance
 | ||||
| ///
 | ||||
|  | @ -20,22 +31,23 @@ use servicepoint::{Connection, Packet}; | |||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_connection_dealloc`.
 | ||||
| ///   by explicitly calling `sp_connection_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_open( | ||||
|     host: *const c_char, | ||||
| ) -> *mut Connection { | ||||
| ) -> *mut SPConnection { | ||||
|     let host = CStr::from_ptr(host).to_str().expect("Bad encoding"); | ||||
|     let connection = match Connection::open(host) { | ||||
|     let connection = match servicepoint::Connection::open(host) { | ||||
|         Err(_) => return null_mut(), | ||||
|         Ok(value) => value, | ||||
|     }; | ||||
| 
 | ||||
|     Box::into_raw(Box::new(connection)) | ||||
|     Box::into_raw(Box::new(SPConnection(connection))) | ||||
| } | ||||
| 
 | ||||
| /// Sends a `Packet` to the display using the `Connection`.
 | ||||
| /// The passed `Packet` gets consumed.
 | ||||
| /// Sends a `SPPacket` to the display using the `SPConnection`.
 | ||||
| ///
 | ||||
| /// The passed `packet` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
|  | @ -43,27 +55,49 @@ pub unsafe extern "C" fn sp_connection_open( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid instance of `Connection`
 | ||||
| /// - `packet` points to a valid instance of `Packet`
 | ||||
| /// - `connection` points to a valid instance of `SPConnection`
 | ||||
| /// - `packet` points to a valid instance of `SPPacket`
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_send( | ||||
|     connection: *const Connection, | ||||
|     packet: *mut Packet, | ||||
| pub unsafe extern "C" fn sp_connection_send_packet( | ||||
|     connection: *const SPConnection, | ||||
|     packet: *mut SPPacket, | ||||
| ) -> bool { | ||||
|     let packet = Box::from_raw(packet); | ||||
|     (*connection).send(*packet).is_ok() | ||||
|     (*connection).0.send((*packet).0).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Closes and deallocates a `Connection`.
 | ||||
| /// Sends a `SPCommand` to the display using the `SPConnection`.
 | ||||
| ///
 | ||||
| /// The passed `command` gets consumed.
 | ||||
| ///
 | ||||
| /// returns: true in case of success
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Connection`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `connection` points to a valid instance of `SPConnection`
 | ||||
| /// - `command` points to a valid instance of `SPPacket`
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_dealloc(ptr: *mut Connection) { | ||||
|     _ = Box::from_raw(ptr); | ||||
| pub unsafe extern "C" fn sp_connection_send_command( | ||||
|     connection: *const SPConnection, | ||||
|     command: *mut SPCommand, | ||||
| ) -> bool { | ||||
|     let command = (*Box::from_raw(command)).0; | ||||
|     (*connection).0.send(command).is_ok() | ||||
| } | ||||
| 
 | ||||
| /// Closes and deallocates a `SPConnection`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `connection` points to a valid `SPConnection`
 | ||||
| /// - `connection` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_connection_free(connection: *mut SPConnection) { | ||||
|     _ = Box::from_raw(connection); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										48
									
								
								crates/servicepoint_binding_c/src/constants.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								crates/servicepoint_binding_c/src/constants.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| //! re-exported constants for use in C
 | ||||
| 
 | ||||
| use servicepoint::CompressionCode; | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
| pub const SP_TILE_SIZE: usize = 8; | ||||
| 
 | ||||
| /// Display tile count in the x-direction
 | ||||
| pub const SP_TILE_WIDTH: usize = 56; | ||||
| 
 | ||||
| /// Display tile count in the y-direction
 | ||||
| pub const SP_TILE_HEIGHT: usize = 20; | ||||
| 
 | ||||
| /// Display width in pixels
 | ||||
| pub const SP_PIXEL_WIDTH: usize = SP_TILE_WIDTH * SP_TILE_SIZE; | ||||
| 
 | ||||
| /// Display height in pixels
 | ||||
| pub const SP_PIXEL_HEIGHT: usize = SP_TILE_HEIGHT * SP_TILE_SIZE; | ||||
| 
 | ||||
| /// pixel count on whole screen
 | ||||
| pub const SP_PIXEL_COUNT: usize = SP_PIXEL_WIDTH * SP_PIXEL_HEIGHT; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| pub const SP_FRAME_PACING_MS: u128 = Duration::from_millis(30).as_millis(); | ||||
| 
 | ||||
| /// Specifies the kind of compression to use.
 | ||||
| #[repr(u16)] | ||||
| pub enum SPCompressionCode { | ||||
|     /// no compression
 | ||||
|     Uncompressed = 0x0, | ||||
|     /// compress using flate2 with zlib header
 | ||||
|     Zlib = 0x677a, | ||||
|     /// compress using bzip2
 | ||||
|     Bzip2 = 0x627a, | ||||
|     /// compress using lzma
 | ||||
|     Lzma = 0x6c7a, | ||||
|     /// compress using Zstandard
 | ||||
|     Zstd = 0x7a73, | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<SPCompressionCode> for CompressionCode { | ||||
|     type Error = (); | ||||
| 
 | ||||
|     fn try_from(value: SPCompressionCode) -> Result<Self, Self::Error> { | ||||
|         CompressionCode::try_from(value as u16) | ||||
|     } | ||||
| } | ||||
|  | @ -1,36 +1,51 @@ | |||
| //! C functions for interacting with `Cp437Grid`s
 | ||||
| //! C functions for interacting with `SPCp437Grid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_cp437_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{Cp437Grid, DataRef, Grid}; | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| 
 | ||||
| /// A C-wrapper for grid containing codepage 437 characters.
 | ||||
| ///
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| #[derive(Clone)] | ||||
| pub struct CCp437Grid(pub(crate) Cp437Grid); | ||||
| 
 | ||||
| /// Creates a new `Cp437Grid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `Cp437Grid` initialized to 0.
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// Cp437Grid grid = sp_cp437_grid_new(4, 3);
 | ||||
| /// sp_cp437_grid_fill(grid, '?');
 | ||||
| /// sp_cp437_grid_set(grid, 0, 0, '!');
 | ||||
| /// sp_cp437_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPCp437Grid(pub(crate) servicepoint::Cp437Grid); | ||||
| 
 | ||||
| impl Clone for SPCp437Grid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCp437Grid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new `SPCp437Grid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: `SPCp437Grid` initialized to 0.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut CCp437Grid { | ||||
|     Box::into_raw(Box::new(CCp437Grid(Cp437Grid::new(width, height)))) | ||||
| ) -> *mut SPCp437Grid { | ||||
|     Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height)))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `Cp437Grid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a `SPCp437Grid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -43,55 +58,57 @@ pub unsafe extern "C" fn sp_cp437_grid_new( | |||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut CCp437Grid { | ||||
| ) -> *mut SPCp437Grid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(CCp437Grid(Cp437Grid::load(width, height, data)))) | ||||
|     Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::load(width, height, data)))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `Cp437Grid`.
 | ||||
| /// Clones a `SPCp437Grid`.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_cp437_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_cp437_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_clone( | ||||
|     this: *const CCp437Grid, | ||||
| ) -> *mut CCp437Grid { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> *mut SPCp437Grid { | ||||
|     Box::into_raw(Box::new((*cp437_grid).clone())) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `Cp437Grid`.
 | ||||
| /// Deallocates a `SPCp437Grid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` is not used concurrently or after cp437_grid call
 | ||||
| /// - `cp437_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_dealloc(this: *mut CCp437Grid) { | ||||
|     _ = Box::from_raw(this); | ||||
| pub unsafe extern "C" fn sp_cp437_grid_free(cp437_grid: *mut SPCp437Grid) { | ||||
|     _ = Box::from_raw(cp437_grid); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// * `x` and `y`: position of the cell to read
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -101,24 +118,24 @@ pub unsafe extern "C" fn sp_cp437_grid_dealloc(this: *mut CCp437Grid) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_get( | ||||
|     this: *const CCp437Grid, | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u8 { | ||||
|     (*this).0.get(x, y) | ||||
|     (*cp437_grid).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `Cp437Grid`.
 | ||||
| /// Sets the value of the specified position in the `SPCp437Grid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `x` and `y`: position of the cell
 | ||||
| /// * `value`: the value to write to the cell
 | ||||
| /// - `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
 | ||||
| ///
 | ||||
|  | @ -130,85 +147,92 @@ pub unsafe extern "C" fn sp_cp437_grid_get( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `BitVec`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `cp437_grid` points to a valid `SPBitVec`
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_set( | ||||
|     this: *mut CCp437Grid, | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u8, | ||||
| ) { | ||||
|     (*this).0.set(x, y, value); | ||||
|     (*cp437_grid).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the `Cp437Grid`.
 | ||||
| /// Sets the value of all cells in the `SPCp437Grid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `value`: the value to set all cells to
 | ||||
| /// - `cp437_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - `cp437_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_fill(this: *mut CCp437Grid, value: u8) { | ||||
|     (*this).0.fill(value); | ||||
| pub unsafe extern "C" fn sp_cp437_grid_fill( | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
|     value: u8, | ||||
| ) { | ||||
|     (*cp437_grid).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the `Cp437Grid` instance.
 | ||||
| /// Gets the width of the `SPCp437Grid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_width(this: *const CCp437Grid) -> usize { | ||||
|     (*this).0.width() | ||||
| pub unsafe extern "C" fn sp_cp437_grid_width( | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> usize { | ||||
|     (*cp437_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the `Cp437Grid` instance.
 | ||||
| /// Gets the height of the `SPCp437Grid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `cp437_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_height( | ||||
|     this: *const CCp437Grid, | ||||
|     cp437_grid: *const SPCp437Grid, | ||||
| ) -> usize { | ||||
|     (*this).0.height() | ||||
|     (*cp437_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `Cp437Grid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the `SPCp437Grid` instance.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Cp437Grid`
 | ||||
| /// - the returned memory range is never accessed after the passed `Cp437Grid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `Cp437Grid` or directly
 | ||||
| /// - `cp437_grid` points to a valid `SPCp437Grid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPCp437Grid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPCp437Grid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_cp437_grid_unsafe_data_ref( | ||||
|     this: *mut CCp437Grid, | ||||
| ) -> CByteSlice { | ||||
|     let data = (*this).0.data_ref_mut(); | ||||
|     CByteSlice { | ||||
|     cp437_grid: *mut SPCp437Grid, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*cp437_grid).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
|  |  | |||
|  | @ -1,27 +1,46 @@ | |||
| //! C API wrapper for the `servicepoint` crate.
 | ||||
| //! C API wrapper for the [servicepoint](https://docs.rs/servicepoint/latest/servicepoint/) crate.
 | ||||
| //!
 | ||||
| //! # Examples
 | ||||
| //!
 | ||||
| //! Make sure to check out [this GitHub repo](https://github.com/arfst23/ServicePoint) as well!
 | ||||
| //!
 | ||||
| //! ```C
 | ||||
| //! #include <stdio.h>
 | ||||
| //! #include "servicepoint.h"
 | ||||
| //!
 | ||||
| //! int main(void) {
 | ||||
| //!     SPConnection *connection = sp_connection_open("172.23.42.29:2342");
 | ||||
| //!     if (connection == NULL)
 | ||||
| //!         return 1;
 | ||||
| //!
 | ||||
| //!     SPPixelGrid *pixels = sp_pixel_grid_new(SP_PIXEL_WIDTH, SP_PIXEL_HEIGHT);
 | ||||
| //!     sp_pixel_grid_fill(pixels, true);
 | ||||
| //!
 | ||||
| //!     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
 | ||||
| //!     while (sp_connection_send_command(connection, sp_command_clone(command)));
 | ||||
| //!
 | ||||
| //!     sp_command_free(command);
 | ||||
| //!     sp_connection_free(connection);
 | ||||
| //!     return 0;
 | ||||
| //! }
 | ||||
| //! ```
 | ||||
| 
 | ||||
| pub use servicepoint::{ | ||||
|     CompressionCode, PIXEL_COUNT, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_HEIGHT, | ||||
|     TILE_SIZE, TILE_WIDTH, | ||||
| }; | ||||
| pub use crate::bit_vec::*; | ||||
| pub use crate::brightness_grid::*; | ||||
| pub use crate::byte_slice::*; | ||||
| pub use crate::command::*; | ||||
| pub use crate::connection::*; | ||||
| pub use crate::constants::*; | ||||
| pub use crate::cp437_grid::*; | ||||
| pub use crate::packet::*; | ||||
| pub use crate::pixel_grid::*; | ||||
| 
 | ||||
| pub use crate::c_slice::CByteSlice; | ||||
| 
 | ||||
| pub mod bit_vec; | ||||
| 
 | ||||
| pub mod brightness_grid; | ||||
| 
 | ||||
| pub mod command; | ||||
| 
 | ||||
| pub mod connection; | ||||
| 
 | ||||
| pub mod packet; | ||||
| 
 | ||||
| pub mod pixel_grid; | ||||
| 
 | ||||
| pub mod c_slice; | ||||
| 
 | ||||
| pub mod cp437_grid; | ||||
| 
 | ||||
| /// The minimum time needed for the display to refresh the screen in ms.
 | ||||
| pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32; | ||||
| mod bit_vec; | ||||
| mod brightness_grid; | ||||
| mod byte_slice; | ||||
| mod command; | ||||
| mod connection; | ||||
| mod constants; | ||||
| mod cp437_grid; | ||||
| mod packet; | ||||
| mod pixel_grid; | ||||
|  |  | |||
|  | @ -1,32 +1,37 @@ | |||
| //! C functions for interacting with `Packet`s
 | ||||
| //! C functions for interacting with `SPPacket`s
 | ||||
| //!
 | ||||
| //! prefix `sp_packet_`
 | ||||
| 
 | ||||
| use std::ptr::null_mut; | ||||
| 
 | ||||
| use servicepoint::{Command, Packet}; | ||||
| use crate::SPCommand; | ||||
| 
 | ||||
| /// Turns a `Command` into a `Packet`.
 | ||||
| /// The `Command` gets consumed.
 | ||||
| /// The raw packet
 | ||||
| pub struct SPPacket(pub(crate) servicepoint::Packet); | ||||
| 
 | ||||
| /// Turns a `SPCommand` into a `SPPacket`.
 | ||||
| /// The `SPCommand` gets consumed.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `command` points to a valid instance of `Command`
 | ||||
| /// - `command` is not used concurrently or after this call
 | ||||
| /// - the returned `Packet` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_dealloc`.
 | ||||
| /// - `SPCommand` points to a valid instance of `SPCommand`
 | ||||
| /// - `SPCommand` is not used concurrently or after this call
 | ||||
| /// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_command( | ||||
|     command: *mut Command, | ||||
| ) -> *mut Packet { | ||||
|     command: *mut SPCommand, | ||||
| ) -> *mut SPPacket { | ||||
|     let command = *Box::from_raw(command); | ||||
|     let packet = command.into(); | ||||
|     let packet = SPPacket(command.0.into()); | ||||
|     Box::into_raw(Box::new(packet)) | ||||
| } | ||||
| 
 | ||||
| /// Tries to load a `Packet` from the passed array with the specified length.
 | ||||
| /// Tries to load a `SPPacket` from the passed array with the specified length.
 | ||||
| ///
 | ||||
| /// returns: NULL in case of an error, pointer to the allocated packet otherwise
 | ||||
| ///
 | ||||
|  | @ -36,29 +41,48 @@ pub unsafe extern "C" fn sp_packet_from_command( | |||
| ///
 | ||||
| /// - `data` points to a valid memory region of at least `length` bytes
 | ||||
| /// - `data` is not written to concurrently
 | ||||
| /// - the returned `Packet` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_dealloc`.
 | ||||
| /// - the returned `SPPacket` instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_try_load( | ||||
|     data: *const u8, | ||||
|     length: usize, | ||||
| ) -> *mut Packet { | ||||
| ) -> *mut SPPacket { | ||||
|     let data = std::slice::from_raw_parts(data, length); | ||||
|     match Packet::try_from(data) { | ||||
|     match servicepoint::Packet::try_from(data) { | ||||
|         Err(_) => null_mut(), | ||||
|         Ok(packet) => Box::into_raw(Box::new(packet)), | ||||
|         Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `Packet`.
 | ||||
| /// Clones a `SPPacket`.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `Packet`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `packet` points to a valid `SPPacket`
 | ||||
| /// - `packet` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_packet_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_dealloc(this: *mut Packet) { | ||||
|     _ = Box::from_raw(this) | ||||
| pub unsafe extern "C" fn sp_packet_clone( | ||||
|     packet: *const SPPacket, | ||||
| ) -> *mut SPPacket { | ||||
|     Box::into_raw(Box::new(SPPacket((*packet).0.clone()))) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `SPPacket`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `packet` points to a valid `SPPacket`
 | ||||
| /// - `packet` is not used concurrently or after this call
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_free(packet: *mut SPPacket) { | ||||
|     _ = Box::from_raw(packet) | ||||
| } | ||||
|  |  | |||
|  | @ -1,19 +1,31 @@ | |||
| //! C functions for interacting with `PixelGrid`s
 | ||||
| //! C functions for interacting with `SPPixelGrid`s
 | ||||
| //!
 | ||||
| //! prefix `sp_pixel_grid_`
 | ||||
| 
 | ||||
| use servicepoint::{DataRef, Grid, PixelGrid}; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| 
 | ||||
| use crate::c_slice::CByteSlice; | ||||
| use crate::byte_slice::SPByteSlice; | ||||
| 
 | ||||
| /// Creates a new `PixelGrid` with the specified dimensions.
 | ||||
| /// A grid of pixels.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// Cp437Grid grid = sp_pixel_grid_new(8, 3);
 | ||||
| /// sp_pixel_grid_fill(grid, true);
 | ||||
| /// sp_pixel_grid_set(grid, 0, 0, false);
 | ||||
| /// sp_pixel_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPPixelGrid(pub(crate) servicepoint::PixelGrid); | ||||
| 
 | ||||
| /// Creates a new `SPPixelGrid` with the specified dimensions.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `width`: size in pixels in x-direction
 | ||||
| /// * `height`: size in pixels in y-direction
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: `PixelGrid` initialized to all pixels off
 | ||||
| /// returns: `SPPixelGrid` initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -24,23 +36,25 @@ use crate::c_slice::CByteSlice; | |||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> *mut PixelGrid { | ||||
|     Box::into_raw(Box::new(PixelGrid::new(width, height))) | ||||
| ) -> *mut SPPixelGrid { | ||||
|     Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::new( | ||||
|         width, height, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Loads a `PixelGrid` with the specified dimensions from the provided data.
 | ||||
| /// Loads a `SPPixelGrid` with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `width`: size in pixels in x-direction
 | ||||
| /// * `height`: size in pixels in y-direction
 | ||||
| /// - `width`: size in pixels in x-direction
 | ||||
| /// - `height`: size in pixels in y-direction
 | ||||
| ///
 | ||||
| /// returns: `PixelGrid` that contains a copy of the provided data
 | ||||
| /// returns: `SPPixelGrid` that contains a copy of the provided data. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -53,55 +67,59 @@ pub unsafe extern "C" fn sp_pixel_grid_new( | |||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length` bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> *mut PixelGrid { | ||||
| ) -> *mut SPPixelGrid { | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     Box::into_raw(Box::new(PixelGrid::load(width, height, data))) | ||||
|     Box::into_raw(Box::new(SPPixelGrid(servicepoint::PixelGrid::load( | ||||
|         width, height, data, | ||||
|     )))) | ||||
| } | ||||
| 
 | ||||
| /// Clones a `PixelGrid`.
 | ||||
| /// Clones a `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_pixel_grid_dealloc`.
 | ||||
| ///   by explicitly calling `sp_pixel_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_clone( | ||||
|     this: *const PixelGrid, | ||||
| ) -> *mut PixelGrid { | ||||
|     Box::into_raw(Box::new((*this).clone())) | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> *mut SPPixelGrid { | ||||
|     Box::into_raw(Box::new(SPPixelGrid((*pixel_grid).0.clone()))) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a `PixelGrid`.
 | ||||
| /// Deallocates a `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `this` is not used concurrently or after this call
 | ||||
| /// - `this` was not passed to another consuming function, e.g. to create a `Command`
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not used concurrently or after pixel_grid call
 | ||||
| /// - `pixel_grid` was not passed to another consuming function, e.g. to create a `SPCommand`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_dealloc(this: *mut PixelGrid) { | ||||
|     _ = Box::from_raw(this); | ||||
| pub unsafe extern "C" fn sp_pixel_grid_free(pixel_grid: *mut SPPixelGrid) { | ||||
|     _ = Box::from_raw(pixel_grid); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position in the `PixelGrid`.
 | ||||
| /// Gets the current value at the specified position in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// * `x` and `y`: position of the cell to read
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
|  | @ -111,24 +129,24 @@ pub unsafe extern "C" fn sp_pixel_grid_dealloc(this: *mut PixelGrid) { | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `this` is not written to concurrently
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_get( | ||||
|     this: *const PixelGrid, | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> bool { | ||||
|     (*this).get(x, y) | ||||
|     (*pixel_grid).0.get(x, y) | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the `PixelGrid`.
 | ||||
| /// Sets the value of the specified position in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `x` and `y`: position of the cell
 | ||||
| /// * `value`: the value to write to the cell
 | ||||
| /// - `pixel_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
 | ||||
| ///
 | ||||
|  | @ -140,83 +158,90 @@ pub unsafe extern "C" fn sp_pixel_grid_get( | |||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_set( | ||||
|     this: *mut PixelGrid, | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: bool, | ||||
| ) { | ||||
|     (*this).set(x, y, value); | ||||
|     (*pixel_grid).0.set(x, y, value); | ||||
| } | ||||
| 
 | ||||
| /// Sets the state of all pixels in the `PixelGrid`.
 | ||||
| /// Sets the state of all pixels in the `SPPixelGrid`.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to write to
 | ||||
| /// * `value`: the value to set all pixels to
 | ||||
| /// - `pixel_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all pixels to
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `this` is not written to or read from concurrently
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - `pixel_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_fill(this: *mut PixelGrid, value: bool) { | ||||
|     (*this).fill(value); | ||||
| pub unsafe extern "C" fn sp_pixel_grid_fill( | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
|     value: bool, | ||||
| ) { | ||||
|     (*pixel_grid).0.fill(value); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width in pixels of the `PixelGrid` instance.
 | ||||
| /// Gets the width in pixels of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_width(this: *const PixelGrid) -> usize { | ||||
|     (*this).width() | ||||
| pub unsafe extern "C" fn sp_pixel_grid_width( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> usize { | ||||
|     (*pixel_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height in pixels of the `PixelGrid` instance.
 | ||||
| /// Gets the height in pixels of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `this`: instance to read from
 | ||||
| /// - `pixel_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_height(this: *const PixelGrid) -> usize { | ||||
|     (*this).height() | ||||
| pub unsafe extern "C" fn sp_pixel_grid_height( | ||||
|     pixel_grid: *const SPPixelGrid, | ||||
| ) -> usize { | ||||
|     (*pixel_grid).0.height() | ||||
| } | ||||
| 
 | ||||
| /// Gets an unsafe reference to the data of the `PixelGrid` instance.
 | ||||
| /// Gets an unsafe reference to the data of the `SPPixelGrid` instance.
 | ||||
| ///
 | ||||
| /// ## Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `this` points to a valid `PixelGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `PixelGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `PixelGrid` or directly
 | ||||
| /// - `pixel_grid` points to a valid `SPPixelGrid`
 | ||||
| /// - the returned memory range is never accessed after the passed `SPPixelGrid` has been freed
 | ||||
| /// - the returned memory range is never accessed concurrently, either via the `SPPixelGrid` or directly
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_pixel_grid_unsafe_data_ref( | ||||
|     this: *mut PixelGrid, | ||||
| ) -> CByteSlice { | ||||
|     let data = (*this).data_ref_mut(); | ||||
|     CByteSlice { | ||||
|     pixel_grid: *mut SPPixelGrid, | ||||
| ) -> SPByteSlice { | ||||
|     let data = (*pixel_grid).0.data_ref_mut(); | ||||
|     SPByteSlice { | ||||
|         start: data.as_mut_ptr_range().start, | ||||
|         length: data.len(), | ||||
|     } | ||||
|  |  | |||
|  | @ -10,11 +10,11 @@ crate-type = ["cdylib"] | |||
| test = false | ||||
| 
 | ||||
| [build-dependencies] | ||||
| csbindgen = "1.8.0" | ||||
| csbindgen = "1.9.3" | ||||
| 
 | ||||
| [dependencies] | ||||
| servicepoint_binding_c = { version = "0.7.0", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.7.0", path = "../servicepoint" } | ||||
| servicepoint_binding_c = { version = "0.8.0", path = "../servicepoint_binding_c" } | ||||
| servicepoint = { version = "0.8.0", path = "../servicepoint" } | ||||
| 
 | ||||
| [lints] | ||||
| workspace = true | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ Because of that, there is no NuGet package you can use directly. | |||
| Including this repository as a submodule and building from source is the recommended way of using the library. | ||||
| 
 | ||||
| ```bash | ||||
| git submodule add https://github.com/kaesaecracker/servicepoint.git | ||||
| git submodule add https://github.com/cccb/servicepoint.git | ||||
| git commit -m "add servicepoint submodule" | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -2,7 +2,7 @@ using ServicePoint.BindGen; | |||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class BitVec : SpNativeInstance<BindGen.CBitVec> | ||||
| public sealed class BitVec : SpNativeInstance<BindGen.BitVec> | ||||
| { | ||||
|     public static BitVec New(int size) | ||||
|     { | ||||
|  | @ -80,12 +80,9 @@ public sealed class BitVec : SpNativeInstance<BindGen.CBitVec> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe BitVec(BindGen.CBitVec* instance) : base(instance) | ||||
|     private unsafe BitVec(BindGen.BitVec* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_bit_vec_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_bit_vec_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ using ServicePoint.BindGen; | |||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class BrightnessGrid : SpNativeInstance<BindGen.CBrightnessGrid> | ||||
| public sealed class BrightnessGrid : SpNativeInstance<BindGen.BrightnessGrid> | ||||
| { | ||||
|     public static BrightnessGrid New(int width, int height) | ||||
|     { | ||||
|  | @ -92,12 +92,9 @@ public sealed class BrightnessGrid : SpNativeInstance<BindGen.CBrightnessGrid> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe BrightnessGrid(BindGen.CBrightnessGrid* instance) : base(instance) | ||||
|     private unsafe BrightnessGrid(BindGen.BrightnessGrid* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_brightness_grid_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_brightness_grid_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -125,8 +125,5 @@ public sealed class Command : SpNativeInstance<BindGen.Command> | |||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_command_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_command_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -20,14 +20,19 @@ public sealed class Connection : SpNativeInstance<BindGen.Connection> | |||
|     { | ||||
|         unsafe | ||||
|         { | ||||
|             return NativeMethods.sp_connection_send(Instance, packet.Into()); | ||||
|             return NativeMethods.sp_connection_send_packet(Instance, packet.Into()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     public bool Send(Command command) | ||||
|     { | ||||
|         NativeMethods.sp_connection_dealloc(Instance); | ||||
|         unsafe | ||||
|         { | ||||
|             return NativeMethods.sp_connection_send_command(Instance, command.Into()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_connection_free(Instance); | ||||
| 
 | ||||
|     private unsafe Connection(BindGen.Connection* instance) : base(instance) | ||||
|     { | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ using ServicePoint.BindGen; | |||
| 
 | ||||
| namespace ServicePoint; | ||||
| 
 | ||||
| public sealed class Cp437Grid : SpNativeInstance<BindGen.CCp437Grid> | ||||
| public sealed class Cp437Grid : SpNativeInstance<BindGen.Cp437Grid> | ||||
| { | ||||
|     public static Cp437Grid New(int width, int height) | ||||
|     { | ||||
|  | @ -123,12 +123,9 @@ public sealed class Cp437Grid : SpNativeInstance<BindGen.CCp437Grid> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unsafe Cp437Grid(BindGen.CCp437Grid* instance) : base(instance) | ||||
|     private unsafe Cp437Grid(BindGen.Cp437Grid* instance) : base(instance) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_cp437_grid_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_cp437_grid_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -32,8 +32,5 @@ public sealed class Packet : SpNativeInstance<BindGen.Packet> | |||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_packet_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_packet_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -96,8 +96,5 @@ public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid> | |||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private protected override unsafe void Dealloc() | ||||
|     { | ||||
|         NativeMethods.sp_pixel_grid_dealloc(Instance); | ||||
|     } | ||||
|     private protected override unsafe void Free() => NativeMethods.sp_pixel_grid_free(Instance); | ||||
| } | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <PackageId>ServicePoint</PackageId> | ||||
|         <Version>0.7.0</Version> | ||||
|         <Version>0.8.0</Version> | ||||
|         <Authors>Repository Authors</Authors> | ||||
|         <Company>None</Company> | ||||
|         <Product>ServicePoint</Product> | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ public abstract class SpNativeInstance<T> | |||
|         _instance = instance; | ||||
|     } | ||||
| 
 | ||||
|     private protected abstract void Dealloc(); | ||||
|     private protected abstract void Free(); | ||||
| 
 | ||||
|     internal unsafe T* Into() | ||||
|     { | ||||
|  | @ -34,7 +34,7 @@ public abstract class SpNativeInstance<T> | |||
|     private unsafe void ReleaseUnmanagedResources() | ||||
|     { | ||||
|         if (_instance != null) | ||||
|             Dealloc(); | ||||
|             Free(); | ||||
|         _instance = null; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,29 +1,35 @@ | |||
| //! Build script generating the C# code needed to call methods from the `servicepoint` C library.
 | ||||
| 
 | ||||
| use std::fs; | ||||
| 
 | ||||
| fn main() { | ||||
|     println!("cargo::rerun-if-changed=../servicepoint_binding_c/src"); | ||||
|     println!("cargo::rerun-if-changed=build.rs"); | ||||
|     csbindgen::Builder::default() | ||||
|         .input_extern_file("../servicepoint_binding_c/src/bit_vec.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/brightness_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/cp437_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/command.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/connection.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/pixel_grid.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/lib.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/c_slice.rs") | ||||
|         .input_extern_file("../servicepoint_binding_c/src/packet.rs") | ||||
|         .input_extern_file("../servicepoint/src/command.rs") | ||||
|         .input_extern_file("../servicepoint/src/connection.rs") | ||||
|         .input_extern_file("../servicepoint/src/pixel_grid.rs") | ||||
|         .input_extern_file("../servicepoint/src/lib.rs") | ||||
|         .input_extern_file("../servicepoint/src/packet.rs") | ||||
|         .input_extern_file("../servicepoint/src/compression_code.rs") | ||||
| 
 | ||||
|     let mut builder = csbindgen::Builder::default(); | ||||
| 
 | ||||
|     for source in fs::read_dir("../servicepoint_binding_c/src").unwrap() { | ||||
|         let path = source.unwrap().path(); | ||||
|         println!("cargo:rerun-if-changed={}", path.display()); | ||||
|         builder = builder.input_extern_file(path); | ||||
|     } | ||||
| 
 | ||||
|     builder | ||||
|         .csharp_dll_name("servicepoint_binding_c") | ||||
|         .csharp_namespace("ServicePoint.BindGen") | ||||
|         .csharp_use_nint_types(true) | ||||
|         .csharp_class_accessibility("public") | ||||
|         .csharp_generate_const_filter(|_| true) | ||||
|         .csharp_type_rename(move |name| { | ||||
|             if name.len() > 2 | ||||
|                 && name.starts_with("SP") | ||||
|                 && name.chars().nth(2).unwrap().is_uppercase() | ||||
|             { | ||||
|                 name[2..].to_string() | ||||
|             } else { | ||||
|                 name | ||||
|             } | ||||
|         }) | ||||
|         .generate_csharp_file("ServicePoint/BindGen/ServicePoint.g.cs") | ||||
|         .unwrap(); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										12
									
								
								shell.nix
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								shell.nix
									
										
									
									
									
								
							|  | @ -1,12 +1,20 @@ | |||
| {pkgs ? import <nixpkgs> {}}: | ||||
| {pkgs ? import <nixpkgs> {}}: let | ||||
|   rust-toolchain = pkgs.symlinkJoin { | ||||
|     name = "rust-toolchain"; | ||||
|     paths = with pkgs; [rustc cargo rustPlatform.rustcSrc rustfmt clippy]; | ||||
|   }; | ||||
| in | ||||
|   pkgs.mkShell { | ||||
|     nativeBuildInputs = with pkgs.buildPackages; [ | ||||
|     rustc cargo gcc rustfmt clippy | ||||
|       rust-toolchain | ||||
| 
 | ||||
|       pkg-config | ||||
|       xe | ||||
|       lzma | ||||
| 
 | ||||
|       cargo-tarpaulin | ||||
| 
 | ||||
|       gcc | ||||
|       gnumake | ||||
| 
 | ||||
|       # dotnet-sdk_8 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter