Merge branch 'utf8'
This commit is contained in:
		
						commit
						b6fa1b161e
					
				
					 52 changed files with 1927 additions and 789 deletions
				
			
		
							
								
								
									
										3
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/rust.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -17,6 +17,9 @@ jobs: | |||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
| 
 | ||||
|     - name: install lzma | ||||
|       run: sudo apt-get update && sudo apt-get install -y liblzma-dev | ||||
| 
 | ||||
|     - name: build default features | ||||
|       run: cargo build --all --verbose | ||||
|     - name: build default features -- examples | ||||
|  |  | |||
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,3 +4,4 @@ out | |||
| .direnv | ||||
| .envrc | ||||
| result | ||||
| mutants.* | ||||
							
								
								
									
										274
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										274
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -44,7 +44,7 @@ version = "1.1.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" | ||||
| dependencies = [ | ||||
|  "windows-sys 0.59.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -54,14 +54,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" | ||||
| dependencies = [ | ||||
|  "anstyle", | ||||
|  "windows-sys 0.59.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "anyhow" | ||||
| version = "1.0.93" | ||||
| version = "1.0.95" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" | ||||
| checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "askama" | ||||
|  | @ -108,7 +108,7 @@ dependencies = [ | |||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -186,9 +186,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | |||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "2.6.0" | ||||
| version = "2.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" | ||||
| checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitvec" | ||||
|  | @ -219,15 +219,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" | |||
| 
 | ||||
| [[package]] | ||||
| name = "bytes" | ||||
| version = "1.8.0" | ||||
| version = "1.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" | ||||
| checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bzip2" | ||||
| version = "0.4.4" | ||||
| version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" | ||||
| checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58" | ||||
| dependencies = [ | ||||
|  "bzip2-sys", | ||||
|  "libc", | ||||
|  | @ -255,9 +255,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "cargo-platform" | ||||
| version = "0.1.8" | ||||
| version = "0.1.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" | ||||
| checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
|  | @ -273,7 +273,7 @@ dependencies = [ | |||
|  "semver", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "thiserror", | ||||
|  "thiserror 1.0.69", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -282,24 +282,24 @@ version = "0.27.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" | ||||
| dependencies = [ | ||||
|  "clap 4.5.20", | ||||
|  "clap 4.5.26", | ||||
|  "heck 0.4.1", | ||||
|  "indexmap 2.6.0", | ||||
|  "indexmap 2.7.0", | ||||
|  "log", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
|  "tempfile", | ||||
|  "toml 0.8.19", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.2.0" | ||||
| version = "1.2.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" | ||||
| checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
|  "libc", | ||||
|  | @ -331,23 +331,23 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap" | ||||
| version = "4.5.20" | ||||
| version = "4.5.26" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" | ||||
| checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" | ||||
| dependencies = [ | ||||
|  "clap_builder", | ||||
|  "clap_derive 4.5.18", | ||||
|  "clap_derive 4.5.24", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "clap_builder" | ||||
| version = "4.5.20" | ||||
| version = "4.5.26" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" | ||||
| checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" | ||||
| dependencies = [ | ||||
|  "anstream", | ||||
|  "anstyle", | ||||
|  "clap_lex 0.7.2", | ||||
|  "clap_lex 0.7.4", | ||||
|  "strsim 0.11.1", | ||||
| ] | ||||
| 
 | ||||
|  | @ -366,14 +366,14 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap_derive" | ||||
| version = "4.5.18" | ||||
| version = "4.5.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" | ||||
| checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" | ||||
| dependencies = [ | ||||
|  "heck 0.5.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -387,9 +387,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "clap_lex" | ||||
| version = "0.7.2" | ||||
| version = "0.7.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" | ||||
| checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "colorchoice" | ||||
|  | @ -399,9 +399,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" | |||
| 
 | ||||
| [[package]] | ||||
| name = "cpufeatures" | ||||
| version = "0.2.15" | ||||
| version = "0.2.16" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" | ||||
| checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | @ -449,12 +449,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | |||
| 
 | ||||
| [[package]] | ||||
| name = "errno" | ||||
| version = "0.3.9" | ||||
| version = "0.3.10" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" | ||||
| checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "windows-sys 0.52.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -465,20 +465,20 @@ checksum = "311a6d2f1f9d60bff73d2c78a0af97ed27f79672f15c238192a5bbb64db56d00" | |||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "2.2.0" | ||||
| version = "2.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" | ||||
| checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "flate2" | ||||
| version = "1.0.34" | ||||
| version = "1.0.35" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" | ||||
| checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" | ||||
| dependencies = [ | ||||
|  "crc32fast", | ||||
|  "miniz_oxide", | ||||
|  | @ -528,9 +528,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "glob" | ||||
| version = "0.3.1" | ||||
| version = "0.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" | ||||
| checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "goblin" | ||||
|  | @ -551,9 +551,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | |||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.15.1" | ||||
| version = "0.15.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" | ||||
| checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "heck" | ||||
|  | @ -578,9 +578,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "http" | ||||
| version = "1.1.0" | ||||
| version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" | ||||
| checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" | ||||
| dependencies = [ | ||||
|  "bytes", | ||||
|  "fnv", | ||||
|  | @ -605,12 +605,12 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "indexmap" | ||||
| version = "2.6.0" | ||||
| version = "2.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" | ||||
| checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" | ||||
| dependencies = [ | ||||
|  "equivalent", | ||||
|  "hashbrown 0.15.1", | ||||
|  "hashbrown 0.15.2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -621,9 +621,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" | |||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
| version = "1.0.11" | ||||
| version = "1.0.14" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" | ||||
| checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "jobserver" | ||||
|  | @ -644,15 +644,15 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.162" | ||||
| version = "0.2.169" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" | ||||
| checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.4.14" | ||||
| version = "0.4.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" | ||||
| checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "log" | ||||
|  | @ -690,9 +690,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" | |||
| 
 | ||||
| [[package]] | ||||
| name = "miniz_oxide" | ||||
| version = "0.8.0" | ||||
| version = "0.8.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" | ||||
| checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" | ||||
| dependencies = [ | ||||
|  "adler2", | ||||
| ] | ||||
|  | @ -778,18 +778,18 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.89" | ||||
| version = "1.0.93" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" | ||||
| checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.37" | ||||
| version = "1.0.38" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" | ||||
| checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
|  | @ -842,15 +842,15 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "0.38.40" | ||||
| version = "0.38.43" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" | ||||
| checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" | ||||
| dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "bitflags 2.7.0", | ||||
|  "errno", | ||||
|  "libc", | ||||
|  "linux-raw-sys", | ||||
|  "windows-sys 0.52.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -876,43 +876,43 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" | |||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "semver" | ||||
| version = "1.0.23" | ||||
| version = "1.0.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" | ||||
| checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.215" | ||||
| version = "1.0.217" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" | ||||
| checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.215" | ||||
| version = "1.0.217" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" | ||||
| checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.132" | ||||
| version = "1.0.135" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" | ||||
| checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "memchr", | ||||
|  | @ -931,24 +931,24 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint" | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "bzip2", | ||||
|  "clap 4.5.20", | ||||
|  "clap 4.5.26", | ||||
|  "flate2", | ||||
|  "log", | ||||
|  "once_cell", | ||||
|  "rand", | ||||
|  "rust-lzma", | ||||
|  "thiserror", | ||||
|  "thiserror 2.0.11", | ||||
|  "tungstenite", | ||||
|  "zstd", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_c" | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| dependencies = [ | ||||
|  "cbindgen", | ||||
|  "servicepoint", | ||||
|  | @ -956,10 +956,10 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint_binding_uniffi" | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| dependencies = [ | ||||
|  "servicepoint", | ||||
|  "thiserror", | ||||
|  "thiserror 2.0.11", | ||||
|  "uniffi", | ||||
|  "uniffi-bindgen-cs", | ||||
|  "uniffi-bindgen-go", | ||||
|  | @ -1025,9 +1025,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.87" | ||||
| version = "2.0.96" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" | ||||
| checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  | @ -1042,15 +1042,16 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" | |||
| 
 | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.14.0" | ||||
| version = "3.15.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" | ||||
| checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "fastrand", | ||||
|  "getrandom", | ||||
|  "once_cell", | ||||
|  "rustix", | ||||
|  "windows-sys 0.59.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1079,7 +1080,16 @@ version = "1.0.69" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" | ||||
| dependencies = [ | ||||
|  "thiserror-impl", | ||||
|  "thiserror-impl 1.0.69", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "thiserror" | ||||
| version = "2.0.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" | ||||
| dependencies = [ | ||||
|  "thiserror-impl 2.0.11", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1090,7 +1100,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" | |||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "thiserror-impl" | ||||
| version = "2.0.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1129,7 +1150,7 @@ version = "0.22.22" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" | ||||
| dependencies = [ | ||||
|  "indexmap 2.6.0", | ||||
|  "indexmap 2.7.0", | ||||
|  "serde", | ||||
|  "serde_spanned", | ||||
|  "toml_datetime", | ||||
|  | @ -1138,9 +1159,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "tungstenite" | ||||
| version = "0.24.0" | ||||
| version = "0.26.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" | ||||
| checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "bytes", | ||||
|  | @ -1150,7 +1171,7 @@ dependencies = [ | |||
|  "log", | ||||
|  "rand", | ||||
|  "sha1", | ||||
|  "thiserror", | ||||
|  "thiserror 2.0.11", | ||||
|  "utf-8", | ||||
| ] | ||||
| 
 | ||||
|  | @ -1162,15 +1183,15 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | |||
| 
 | ||||
| [[package]] | ||||
| name = "unicase" | ||||
| version = "2.8.0" | ||||
| version = "2.8.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" | ||||
| checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.13" | ||||
| version = "1.0.14" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" | ||||
| checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-linebreak" | ||||
|  | @ -1218,8 +1239,8 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "uniffi-bindgen-go" | ||||
| version = "0.2.1+v0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| version = "0.2.2+v0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "askama 0.12.1", | ||||
|  | @ -1234,15 +1255,15 @@ dependencies = [ | |||
|  "serde_json", | ||||
|  "textwrap", | ||||
|  "toml 0.5.11", | ||||
|  "uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "uniffi_bindgen" | ||||
| version = "0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "askama 0.12.1", | ||||
|  | @ -1257,9 +1278,9 @@ dependencies = [ | |||
|  "serde", | ||||
|  "textwrap", | ||||
|  "toml 0.5.11", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1322,10 +1343,10 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "uniffi_checksum_derive" | ||||
| version = "0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1334,7 +1355,7 @@ version = "0.25.0" | |||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce#f68639fbc720b50ebe561ba75c66c84dc456bdce" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1344,7 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1376,7 +1397,7 @@ dependencies = [ | |||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
|  "toml 0.5.11", | ||||
|  "uniffi_build", | ||||
|  "uniffi_meta 0.25.3", | ||||
|  | @ -1385,12 +1406,12 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "uniffi_meta" | ||||
| version = "0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "bytes", | ||||
|  "siphasher", | ||||
|  "uniffi_checksum_derive 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_checksum_derive 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1419,7 +1440,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "uniffi_testing" | ||||
| version = "0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "camino", | ||||
|  | @ -1456,12 +1477,12 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "uniffi_udl" | ||||
| version = "0.25.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "weedle2 4.0.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)", | ||||
|  "uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
|  "weedle2 4.0.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1529,7 +1550,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "weedle2" | ||||
| version = "4.0.0" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| dependencies = [ | ||||
|  "nom", | ||||
| ] | ||||
|  | @ -1564,7 +1585,7 @@ version = "0.1.9" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" | ||||
| dependencies = [ | ||||
|  "windows-sys 0.59.0", | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1573,15 +1594,6 @@ version = "0.4.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-sys" | ||||
| version = "0.52.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | ||||
| dependencies = [ | ||||
|  "windows-targets", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "windows-sys" | ||||
| version = "0.59.0" | ||||
|  | @ -1657,9 +1669,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | |||
| 
 | ||||
| [[package]] | ||||
| name = "winnow" | ||||
| version = "0.6.20" | ||||
| version = "0.6.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" | ||||
| checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" | ||||
| dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
|  | @ -1691,7 +1703,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" | |||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.87", | ||||
|  "syn 2.0.96", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  |  | |||
|  | @ -8,10 +8,10 @@ members = [ | |||
| ] | ||||
| 
 | ||||
| [workspace.package] | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| 
 | ||||
| [workspace.lints.rust] | ||||
| missing-docs = "warn" | ||||
| 
 | ||||
| [workspace.dependencies] | ||||
| thiserror = "1.0.69" | ||||
| thiserror = "2.0" | ||||
|  |  | |||
|  | @ -16,12 +16,12 @@ crate-type = ["rlib"] | |||
| log = "0.4" | ||||
| bitvec = "1.0" | ||||
| flate2 = { version = "1.0", optional = true } | ||||
| bzip2 = { version = "0.4", optional = true } | ||||
| bzip2 = { version = "0.5", optional = true } | ||||
| zstd = { version = "0.13", optional = true } | ||||
| rust-lzma = { version = "0.6.0", optional = true } | ||||
| rust-lzma = { version = "0.6", optional = true } | ||||
| rand = { version = "0.8", optional = true } | ||||
| tungstenite = { version = "0.24.0", optional = true } | ||||
| once_cell = { version = "1.20.2", optional = true } | ||||
| tungstenite = { version = "0.26", optional = true } | ||||
| once_cell = { version = "1.20", optional = true } | ||||
| thiserror.workspace = true | ||||
| 
 | ||||
| [features] | ||||
|  | @ -56,4 +56,4 @@ clap = { version = "4.5", features = ["derive"] } | |||
| workspace = true | ||||
| 
 | ||||
| [package.metadata.docs.rs] | ||||
| all-features = true | ||||
| all-features = true | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ cargo add servicepoint | |||
| or | ||||
| ```toml | ||||
| [dependencies] | ||||
| servicepoint = "0.12.0" | ||||
| servicepoint = "0.13.0" | ||||
| ``` | ||||
| 
 | ||||
| ## Examples | ||||
|  | @ -39,8 +39,11 @@ Execute `cargo run --example` for a list of available examples and `cargo run -- | |||
| 
 | ||||
| ## Note on stability | ||||
| 
 | ||||
| This library is still in early development. | ||||
| You can absolutely use it, and it works, but expect minor breaking changes with every version bump. | ||||
| This library can be used for creative project or just to play around with the display. | ||||
| A decent coverage by unit tests prevents major problems and I also test this with my own projects, which mostly use up-to-date versions. | ||||
| 
 | ||||
| That being said, the API is still being worked on. | ||||
| Expect minor breaking changes with every version bump. | ||||
| Please specify the full version including patch in your Cargo.toml until 1.0 is released. | ||||
| 
 | ||||
| ## Features | ||||
|  |  | |||
|  | @ -1,8 +1,7 @@ | |||
| //! An example for how to send text to the display.
 | ||||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin}; | ||||
| use servicepoint::*; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|  | @ -42,10 +41,8 @@ fn main() { | |||
|     } | ||||
| 
 | ||||
|     let text = cli.text.join("\n"); | ||||
|     let grid = CharGrid::from(text); | ||||
|     let grid = Cp437Grid::from(grid); | ||||
| 
 | ||||
|     let grid = CharGrid::wrap_str(TILE_WIDTH, &text); | ||||
|     connection | ||||
|         .send(Command::Cp437Data(Origin::ZERO, grid)) | ||||
|         .send(Command::Utf8Data(Origin::ZERO, grid)) | ||||
|         .expect("sending text failed"); | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| //! Show a brightness level test pattern on screen
 | ||||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::*; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
|  |  | |||
|  | @ -1,11 +1,9 @@ | |||
| //! A simple game of life implementation to show how to render graphics to the display.
 | ||||
| 
 | ||||
| use std::thread; | ||||
| 
 | ||||
| use clap::Parser; | ||||
| use rand::{distributions, Rng}; | ||||
| 
 | ||||
| use servicepoint::*; | ||||
| use std::thread; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|  |  | |||
|  | @ -1,10 +1,8 @@ | |||
| //! A simple example for how to send pixel data to the display.
 | ||||
| 
 | ||||
| use std::thread; | ||||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::*; | ||||
| use std::thread; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|  |  | |||
|  | @ -1,13 +1,10 @@ | |||
| //! A simple example for how to set brightnesses for tiles on the screen.
 | ||||
| //! Continuously changes the tiles in a random window to random brightnesses.
 | ||||
| 
 | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| use clap::Parser; | ||||
| use rand::Rng; | ||||
| 
 | ||||
| use servicepoint::Command::{BitmapLinearWin, Brightness, CharBrightness}; | ||||
| use servicepoint::*; | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|  | @ -31,7 +28,7 @@ fn main() { | |||
|         let mut filled_grid = Bitmap::max_sized(); | ||||
|         filled_grid.fill(true); | ||||
| 
 | ||||
|         let command = BitmapLinearWin( | ||||
|         let command = Command::BitmapLinearWin( | ||||
|             Origin::ZERO, | ||||
|             filled_grid, | ||||
|             CompressionCode::Lzma, | ||||
|  | @ -41,7 +38,7 @@ fn main() { | |||
| 
 | ||||
|     // set all pixels to the same random brightness
 | ||||
|     let mut rng = rand::thread_rng(); | ||||
|     connection.send(Brightness(rng.gen())).unwrap(); | ||||
|     connection.send(Command::Brightness(rng.gen())).unwrap(); | ||||
| 
 | ||||
|     // continuously update random windows to new random brightness
 | ||||
|     loop { | ||||
|  | @ -61,7 +58,9 @@ fn main() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         connection.send(CharBrightness(origin, luma)).unwrap(); | ||||
|         connection | ||||
|             .send(Command::CharBrightness(origin, luma)) | ||||
|             .unwrap(); | ||||
|         std::thread::sleep(wait_duration); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,11 +1,9 @@ | |||
| //! An example on how to modify the image on screen without knowing the current content.
 | ||||
| use clap::Parser; | ||||
| use servicepoint::*; | ||||
| use std::thread; | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| use clap::Parser; | ||||
| 
 | ||||
| use servicepoint::*; | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| struct Cli { | ||||
|     #[arg(short, long, default_value = "localhost:2342")] | ||||
|  | @ -34,7 +32,11 @@ fn main() { | |||
|         } | ||||
| 
 | ||||
|         connection | ||||
|             .send(Command::BitmapLinearWin(Origin::ZERO, enabled_pixels.clone(), CompressionCode::Lzma)) | ||||
|             .send(Command::BitmapLinearWin( | ||||
|                 Origin::ZERO, | ||||
|                 enabled_pixels.clone(), | ||||
|                 CompressionCode::Lzma, | ||||
|             )) | ||||
|             .expect("could not send command to display"); | ||||
|         thread::sleep(sleep_duration); | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								crates/servicepoint/src/bit_vec.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								crates/servicepoint/src/bit_vec.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| /// A byte-packed vector of booleans.
 | ||||
| ///
 | ||||
| /// The implementation is provided by [bitvec].
 | ||||
| /// This is an alias for the specific type of [bitvec::BitVec] used in this crate.
 | ||||
| pub type BitVec = bitvec::BitVec<u8, bitvec::Msb0>; | ||||
| 
 | ||||
| pub mod bitvec { | ||||
|     //! Re-export of the used library [mod@bitvec].
 | ||||
|     pub use bitvec::prelude::*; | ||||
| } | ||||
|  | @ -1,10 +1,23 @@ | |||
| use bitvec::order::Msb0; | ||||
| use bitvec::prelude::BitSlice; | ||||
| use bitvec::slice::IterMut; | ||||
| use crate::data_ref::DataRef; | ||||
| use crate::BitVec; | ||||
| use crate::*; | ||||
| use ::bitvec::order::Msb0; | ||||
| use ::bitvec::prelude::BitSlice; | ||||
| use ::bitvec::slice::IterMut; | ||||
| 
 | ||||
| use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH}; | ||||
| 
 | ||||
| /// A grid of pixels stored in packed bytes.
 | ||||
| /// A fixed-size 2D grid of booleans.
 | ||||
| ///
 | ||||
| /// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels.
 | ||||
| /// This means that no conversion is necessary for sending the data to the display.
 | ||||
| /// The downside is that the width has to be a multiple of 8.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// use servicepoint::Bitmap;
 | ||||
| /// let mut bitmap = Bitmap::new(8, 2);
 | ||||
| ///
 | ||||
| /// ```
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct Bitmap { | ||||
|     width: usize, | ||||
|  | @ -26,7 +39,11 @@ impl Bitmap { | |||
|     ///
 | ||||
|     /// - when the width is not dividable by 8
 | ||||
|     pub fn new(width: usize, height: usize) -> Self { | ||||
|         assert_eq!(width % 8, 0); | ||||
|         assert_eq!( | ||||
|             width % 8, | ||||
|             0, | ||||
|             "width must be a multiple of 8, but is {width}" | ||||
|         ); | ||||
|         Self { | ||||
|             width, | ||||
|             height, | ||||
|  | @ -176,11 +193,38 @@ impl From<Bitmap> for Vec<u8> { | |||
| } | ||||
| 
 | ||||
| impl From<Bitmap> for BitVec { | ||||
|     /// Turns a [Bitmap] into the underlying [BitVec].
 | ||||
|     fn from(value: Bitmap) -> Self { | ||||
|         value.bit_vec | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&ValueGrid<bool>> for Bitmap { | ||||
|     /// Converts a grid of [bool]s into a [Bitmap].
 | ||||
|     /// 
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// - when the width of `value` is not dividable by 8
 | ||||
|     fn from(value: &ValueGrid<bool>) -> Self { | ||||
|         let mut result = Self::new(value.width(), value.height()); | ||||
|         for (mut to, from) in result.iter_mut().zip(value.iter()) { | ||||
|             *to = *from; | ||||
|         } | ||||
|         result | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&Bitmap> for ValueGrid<bool> { | ||||
|     /// Converts a [Bitmap] into a grid of [bool]s.
 | ||||
|     fn from(value: &Bitmap) -> Self { | ||||
|         let mut result = Self::new(value.width(), value.height()); | ||||
|         for (to, from) in result.iter_mut().zip(value.iter()) { | ||||
|             *to = *from; | ||||
|         } | ||||
|         result | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IterRows<'t> { | ||||
|     bitmap: &'t Bitmap, | ||||
|     row: usize, | ||||
|  | @ -203,7 +247,7 @@ impl<'t> Iterator for IterRows<'t> { | |||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::{Bitmap, DataRef, Grid}; | ||||
|     use crate::{BitVec, Bitmap, DataRef, Grid, ValueGrid}; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn fill() { | ||||
|  | @ -295,4 +339,24 @@ mod tests { | |||
|         data[1] = 0x0F; | ||||
|         assert!(grid.get(7, 1)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn to_bitvec() { | ||||
|         let mut grid = Bitmap::new(8, 2); | ||||
|         grid.set(0, 0, true); | ||||
|         let bitvec: BitVec = grid.into(); | ||||
|         assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn from_bool_grid() { | ||||
|         let original = ValueGrid::load( | ||||
|             8, | ||||
|             1, | ||||
|             &[true, false, true, false, true, false, true, false], | ||||
|         ); | ||||
|         let converted = Bitmap::from(&original); | ||||
|         let reconverted = ValueGrid::from(&converted); | ||||
|         assert_eq!(original, reconverted); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,3 @@ | |||
| use crate::{Grid, PrimitiveGrid}; | ||||
| 
 | ||||
| #[cfg(feature = "rand")] | ||||
| use rand::{ | ||||
|     distributions::{Distribution, Standard}, | ||||
|  | @ -22,28 +20,6 @@ use rand::{ | |||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] | ||||
| pub struct Brightness(u8); | ||||
| 
 | ||||
| /// A grid containing brightness values.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
 | ||||
| /// let mut grid = BrightnessGrid::new(2,2);
 | ||||
| /// grid.set(0, 0, Brightness::MIN);
 | ||||
| /// grid.set(1, 1, Brightness::MIN);
 | ||||
| ///
 | ||||
| /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
| /// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
 | ||||
| /// ```
 | ||||
| pub type BrightnessGrid = PrimitiveGrid<Brightness>; | ||||
| 
 | ||||
| impl BrightnessGrid { | ||||
|     /// Like [Self::load], but ignoring any out-of-range brightness values
 | ||||
|     pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { | ||||
|         PrimitiveGrid::load(width, height, data).map(Brightness::saturating_from) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Brightness> for u8 { | ||||
|     fn from(brightness: Brightness) -> Self { | ||||
|         Self::from(&brightness) | ||||
|  | @ -92,41 +68,6 @@ impl Default for Brightness { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BrightnessGrid> for Vec<u8> { | ||||
|     fn from(value: PrimitiveGrid<Brightness>) -> Self { | ||||
|         value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&BrightnessGrid> for PrimitiveGrid<u8> { | ||||
|     fn from(value: &PrimitiveGrid<Brightness>) -> Self { | ||||
|         let u8s = value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect::<Vec<u8>>(); | ||||
|         PrimitiveGrid::load(value.width(), value.height(), &u8s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<PrimitiveGrid<u8>> for BrightnessGrid { | ||||
|     type Error = u8; | ||||
| 
 | ||||
|     fn try_from(value: PrimitiveGrid<u8>) -> Result<Self, Self::Error> { | ||||
|         let brightnesses = value | ||||
|             .iter() | ||||
|             .map(|b| Brightness::try_from(*b)) | ||||
|             .collect::<Result<Vec<_>, _>>()?; | ||||
|         Ok(BrightnessGrid::load( | ||||
|             value.width(), | ||||
|             value.height(), | ||||
|             &brightnesses, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "rand")] | ||||
| impl Distribution<Brightness> for Standard { | ||||
|     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Brightness { | ||||
|  | @ -137,7 +78,6 @@ impl Distribution<Brightness> for Standard { | |||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::DataRef; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn brightness_from_u8() { | ||||
|  | @ -154,24 +94,9 @@ mod tests { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn to_u8_grid() { | ||||
|         let mut grid = BrightnessGrid::new(2, 2); | ||||
|         grid.set(1, 0, Brightness::MIN); | ||||
|         grid.set(0, 1, Brightness::MAX); | ||||
|         let actual = PrimitiveGrid::from(&grid); | ||||
|         assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn saturating_convert() { | ||||
|         assert_eq!(Brightness::MAX, Brightness::saturating_from(100)); | ||||
|         assert_eq!(Brightness(5), Brightness::saturating_from(5)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn saturating_load() { | ||||
|         assert_eq!(BrightnessGrid::load(2,2, &[Brightness::MAX, Brightness::MAX, Brightness::MIN, Brightness::MAX]), | ||||
|             BrightnessGrid::saturating_load(2,2, &[255u8, 23, 0, 42])); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										93
									
								
								crates/servicepoint/src/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								crates/servicepoint/src/brightness_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | |||
| use crate::brightness::Brightness; | ||||
| use crate::grid::Grid; | ||||
| use crate::value_grid::ValueGrid; | ||||
| use crate::ByteGrid; | ||||
| 
 | ||||
| /// A grid containing brightness values.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
 | ||||
| /// let mut grid = BrightnessGrid::new(2,2);
 | ||||
| /// grid.set(0, 0, Brightness::MIN);
 | ||||
| /// grid.set(1, 1, Brightness::MIN);
 | ||||
| ///
 | ||||
| /// # let connection = Connection::open("127.0.0.1:2342").unwrap();
 | ||||
| /// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
 | ||||
| /// ```
 | ||||
| pub type BrightnessGrid = ValueGrid<Brightness>; | ||||
| 
 | ||||
| impl BrightnessGrid { | ||||
|     /// Like [Self::load], but ignoring any out-of-range brightness values
 | ||||
|     pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self { | ||||
|         ValueGrid::load(width, height, data).map(Brightness::saturating_from) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<BrightnessGrid> for Vec<u8> { | ||||
|     fn from(value: ValueGrid<Brightness>) -> Self { | ||||
|         value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&BrightnessGrid> for ByteGrid { | ||||
|     fn from(value: &ValueGrid<Brightness>) -> Self { | ||||
|         let u8s = value | ||||
|             .iter() | ||||
|             .map(|brightness| (*brightness).into()) | ||||
|             .collect::<Vec<u8>>(); | ||||
|         ValueGrid::load(value.width(), value.height(), &u8s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<ByteGrid> for BrightnessGrid { | ||||
|     type Error = u8; | ||||
| 
 | ||||
|     fn try_from(value: ByteGrid) -> Result<Self, Self::Error> { | ||||
|         let brightnesses = value | ||||
|             .iter() | ||||
|             .map(|b| Brightness::try_from(*b)) | ||||
|             .collect::<Result<Vec<_>, _>>()?; | ||||
|         Ok(BrightnessGrid::load( | ||||
|             value.width(), | ||||
|             value.height(), | ||||
|             &brightnesses, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::value_grid::ValueGrid; | ||||
|     use crate::{Brightness, BrightnessGrid, DataRef, Grid}; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn to_u8_grid() { | ||||
|         let mut grid = BrightnessGrid::new(2, 2); | ||||
|         grid.set(1, 0, Brightness::MIN); | ||||
|         grid.set(0, 1, Brightness::MAX); | ||||
|         let actual = ValueGrid::from(&grid); | ||||
|         assert_eq!(actual.data_ref(), &[11, 0, 11, 11]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn saturating_load() { | ||||
|         assert_eq!( | ||||
|             BrightnessGrid::load( | ||||
|                 2, | ||||
|                 2, | ||||
|                 &[ | ||||
|                     Brightness::MAX, | ||||
|                     Brightness::MAX, | ||||
|                     Brightness::MIN, | ||||
|                     Brightness::MAX | ||||
|                 ] | ||||
|             ), | ||||
|             BrightnessGrid::saturating_load(2, 2, &[255u8, 23, 0, 42]) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										4
									
								
								crates/servicepoint/src/byte_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								crates/servicepoint/src/byte_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| use crate::ValueGrid; | ||||
| 
 | ||||
| /// A 2d grid of bytes - see [ValueGrid].
 | ||||
| pub type ByteGrid = ValueGrid<u8>; | ||||
|  | @ -1,13 +1,71 @@ | |||
| use crate::primitive_grid::SeriesError; | ||||
| use crate::{Grid, PrimitiveGrid}; | ||||
| use crate::{Grid, SetValueSeriesError, TryLoadValueGridError, ValueGrid}; | ||||
| use std::string::FromUtf8Error; | ||||
| 
 | ||||
| /// A grid containing UTF-8 characters.
 | ||||
| pub type CharGrid = PrimitiveGrid<char>; | ||||
| ///
 | ||||
| /// To send a CharGrid to the display, use [Command::Utf8Data](crate::Command::Utf8Data).
 | ||||
| ///
 | ||||
| /// Also see [ValueGrid] for the non-specialized operations and examples.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{CharGrid, Command, Connection, Origin};
 | ||||
| /// let grid = CharGrid::from("You can\nload multiline\nstrings directly");
 | ||||
| /// assert_eq!(grid.get_row_str(1), Some("load multiline\0\0".to_string()));
 | ||||
| ///
 | ||||
| /// # let connection = Connection::Fake;
 | ||||
| /// let command = Command::Utf8Data(Origin::ZERO, grid);
 | ||||
| /// ```
 | ||||
| pub type CharGrid = ValueGrid<char>; | ||||
| 
 | ||||
| impl CharGrid { | ||||
|     /// Loads a [CharGrid] with the specified width from the provided text, wrapping to as many rows as needed.
 | ||||
|     ///
 | ||||
|     /// The passed rows are extended with '\0' if needed.
 | ||||
|     ///
 | ||||
|     /// returns: [CharGrid] that contains a copy of the provided data.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let grid = CharGrid::wrap_str(2, "abc\ndef");
 | ||||
|     /// ```
 | ||||
|     pub fn wrap_str(width: usize, text: &str) -> Self { | ||||
|         let lines = text | ||||
|             .split('\n') | ||||
|             .flat_map(move |x| { | ||||
|                 x.chars() | ||||
|                     .collect::<Vec<char>>() | ||||
|                     .chunks(width) | ||||
|                     .map(|c| { | ||||
|                         let mut s = String::from_iter(c); | ||||
|                         s.push_str(&"\0".repeat(width - s.chars().count())); | ||||
|                         s | ||||
|                     }) | ||||
|                     .collect::<Vec<String>>() | ||||
|             }) | ||||
|             .collect::<Vec<String>>(); | ||||
|         let height = lines.len(); | ||||
|         let mut result = Self::new(width, height); | ||||
|         for (row, text_line) in lines.iter().enumerate() { | ||||
|             result.set_row_str(row, text_line).unwrap() | ||||
|         } | ||||
|         result | ||||
|     } | ||||
| 
 | ||||
|     /// Copies a column from the grid as a String.
 | ||||
|     ///
 | ||||
|     /// Returns [None] if x is out of bounds.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let grid = CharGrid::from("ab\ncd");
 | ||||
|     /// let col = grid.get_col_str(0).unwrap(); // "ac"
 | ||||
|     /// ```
 | ||||
|     pub fn get_col_str(&self, x: usize) -> Option<String> { | ||||
|         Some(String::from_iter(self.get_col(x)?)) | ||||
|     } | ||||
|  | @ -15,42 +73,91 @@ impl CharGrid { | |||
|     /// Copies a row from the grid as a String.
 | ||||
|     ///
 | ||||
|     /// Returns [None] if y is out of bounds.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let grid = CharGrid::from("ab\ncd");
 | ||||
|     /// let row = grid.get_row_str(0).unwrap(); // "ab"
 | ||||
|     /// ```
 | ||||
|     pub fn get_row_str(&self, y: usize) -> Option<String> { | ||||
|         Some(String::from_iter(self.get_row(y)?)) | ||||
|     } | ||||
| 
 | ||||
|     /// Overwrites a row in the grid with a str.
 | ||||
|     ///
 | ||||
|     /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
 | ||||
|     /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let mut grid = CharGrid::from("ab\ncd");
 | ||||
|     /// grid.set_row_str(0, "ef").unwrap();
 | ||||
|     /// ```
 | ||||
|     pub fn set_row_str( | ||||
|         &mut self, | ||||
|         y: usize, | ||||
|         value: &str, | ||||
|     ) -> Result<(), SeriesError> { | ||||
|     ) -> Result<(), SetValueSeriesError> { | ||||
|         self.set_row(y, value.chars().collect::<Vec<_>>().as_ref()) | ||||
|     } | ||||
| 
 | ||||
|     /// Overwrites a column in the grid with a str.
 | ||||
|     ///
 | ||||
|     /// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
 | ||||
|     /// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let mut grid = CharGrid::from("ab\ncd");
 | ||||
|     /// grid.set_col_str(0, "ef").unwrap();
 | ||||
|     /// ```
 | ||||
|     pub fn set_col_str( | ||||
|         &mut self, | ||||
|         x: usize, | ||||
|         value: &str, | ||||
|     ) -> Result<(), SeriesError> { | ||||
|     ) -> Result<(), SetValueSeriesError> { | ||||
|         self.set_col(x, value.chars().collect::<Vec<_>>().as_ref()) | ||||
|     } | ||||
| 
 | ||||
|     /// Loads a [CharGrid] with the specified dimensions from the provided UTF-8 bytes.
 | ||||
|     ///
 | ||||
|     /// returns: [CharGrid] that contains the provided data, or [FromUtf8Error] if the data is invalid.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let grid = CharGrid::load_utf8(2, 2, [97u8, 98, 99, 100].to_vec());
 | ||||
|     /// ```
 | ||||
|     pub fn load_utf8( | ||||
|         width: usize, | ||||
|         height: usize, | ||||
|         bytes: Vec<u8>, | ||||
|     ) -> Result<CharGrid, LoadUtf8Error> { | ||||
|         let s: Vec<char> = String::from_utf8(bytes)?.chars().collect(); | ||||
|         Ok(CharGrid::try_load(width, height, s)?) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, thiserror::Error)] | ||||
| pub enum LoadUtf8Error { | ||||
|     #[error(transparent)] | ||||
|     FromUtf8Error(#[from] FromUtf8Error), | ||||
|     #[error(transparent)] | ||||
|     TryLoadError(#[from] TryLoadValueGridError), | ||||
| } | ||||
| 
 | ||||
| impl From<&str> for CharGrid { | ||||
|     fn from(value: &str) -> Self { | ||||
|         let value = value.replace("\r\n", "\n"); | ||||
|         let mut lines = value | ||||
|             .split('\n') | ||||
|             .map(move |line| line.trim_end()) | ||||
|             .collect::<Vec<_>>(); | ||||
|         let width = | ||||
|             lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len())); | ||||
|         let mut lines = value.split('\n').collect::<Vec<_>>(); | ||||
|         let width = lines | ||||
|             .iter() | ||||
|             .fold(0, move |a, x| std::cmp::max(a, x.chars().count())); | ||||
| 
 | ||||
|         while lines.last().is_some_and(move |line| line.is_empty()) { | ||||
|             _ = lines.pop(); | ||||
|  | @ -73,26 +180,63 @@ impl From<String> for CharGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CharGrid> for String { | ||||
|     fn from(grid: CharGrid) -> Self { | ||||
|         String::from(&grid) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&CharGrid> for String { | ||||
|     /// Converts a [CharGrid] into a [String].
 | ||||
|     ///
 | ||||
|     /// Rows are separated by '\n'.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::CharGrid;
 | ||||
|     /// let grid = CharGrid::from("ab\ncd");
 | ||||
|     /// let string = String::from(grid);
 | ||||
|     /// let grid = CharGrid::from(string);
 | ||||
|     /// ```
 | ||||
|     fn from(value: &CharGrid) -> Self { | ||||
|         value | ||||
|             .iter_rows() | ||||
|             .map(move |chars| { | ||||
|                 chars | ||||
|                     .collect::<String>() | ||||
|                     .replace('\0', " ") | ||||
|                     .trim_end() | ||||
|                     .to_string() | ||||
|             }) | ||||
|             .collect::<Vec<_>>() | ||||
|             .map(String::from_iter) | ||||
|             .collect::<Vec<String>>() | ||||
|             .join("\n") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&CharGrid> for Vec<u8> { | ||||
|     /// Converts a [CharGrid] into a [`Vec<u8>`].
 | ||||
|     ///
 | ||||
|     /// Rows are not separated.
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{CharGrid, Grid};
 | ||||
|     /// let grid = CharGrid::from("ab\ncd");
 | ||||
|     /// let height = grid.height();
 | ||||
|     /// let width = grid.width();
 | ||||
|     /// let grid = CharGrid::load_utf8(width, height, grid.into());
 | ||||
|     /// ```
 | ||||
|     fn from(value: &CharGrid) -> Self { | ||||
|         String::from_iter(value.iter()).into_bytes() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CharGrid> for Vec<u8> { | ||||
|     /// See [`From<&CharGrid>::from`].
 | ||||
|     fn from(value: CharGrid) -> Self { | ||||
|         Self::from(&value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use super::*; | ||||
|     use crate::Grid; | ||||
|     #[test] | ||||
|     fn col_str() { | ||||
|         let mut grid = CharGrid::new(2, 3); | ||||
|  | @ -109,7 +253,7 @@ mod test { | |||
|         assert_eq!(grid.get_row_str(1), Some(String::from("\0\0"))); | ||||
|         assert_eq!( | ||||
|             grid.set_row_str(1, "abc"), | ||||
|             Err(SeriesError::InvalidLength { | ||||
|             Err(SetValueSeriesError::InvalidLength { | ||||
|                 expected: 2, | ||||
|                 actual: 3 | ||||
|             }) | ||||
|  | @ -120,10 +264,35 @@ mod test { | |||
| 
 | ||||
|     #[test] | ||||
|     fn str_to_char_grid() { | ||||
|         let original = "Hello\r\nWorld!\n...\n"; | ||||
|         // conversion with .to_string() covers one more line
 | ||||
|         let original = "Hello\r\nWorld!\n...\n".to_string(); | ||||
| 
 | ||||
|         let grid = CharGrid::from(original); | ||||
|         assert_eq!(3, grid.height()); | ||||
|         let actual = String::from(&grid); | ||||
|         assert_eq!("Hello\nWorld!\n...", actual); | ||||
|         assert_eq!("Hello\0\nWorld!\n...\0\0\0", String::from(grid)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_bytes() { | ||||
|         let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0"); | ||||
|         let bytes: Vec<u8> = grid.clone().into(); | ||||
|         let copy = | ||||
|             CharGrid::load_utf8(grid.width(), grid.height(), bytes).unwrap(); | ||||
|         assert_eq!(grid, copy); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_string() { | ||||
|         let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0"); | ||||
|         let str: String = grid.clone().into(); | ||||
|         let copy = CharGrid::from(str); | ||||
|         assert_eq!(grid, copy); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn wrap_str() { | ||||
|         let grid = CharGrid::wrap_str(2, "abc\ndef"); | ||||
|         assert_eq!(4, grid.height()); | ||||
|         assert_eq!("ab\nc\0\nde\nf\0", String::from(grid)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,10 +1,7 @@ | |||
| use crate::{ | ||||
|     command_code::CommandCode, | ||||
|     compression::into_decompressed, | ||||
|     packet::{Header, Packet}, | ||||
|     Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, | ||||
|     Pixels, PrimitiveGrid, BitVec, Tiles, TILE_SIZE, | ||||
| }; | ||||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::into_decompressed; | ||||
| use crate::value_grid::ValueGrid; | ||||
| use crate::*; | ||||
| 
 | ||||
| /// Type alias for documenting the meaning of the u16 in enum values
 | ||||
| pub type Offset = usize; | ||||
|  | @ -41,7 +38,7 @@ pub type Offset = usize; | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Brightness, Command, Connection, packet::Packet};
 | ||||
| /// use servicepoint::{Brightness, Command, Connection, Packet};
 | ||||
| /// #
 | ||||
| /// // create command
 | ||||
| /// let command = Command::Brightness(Brightness::MAX);
 | ||||
|  | @ -74,14 +71,29 @@ pub enum Command { | |||
| 
 | ||||
|     /// Show text on the screen.
 | ||||
|     ///
 | ||||
|     /// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
 | ||||
|     /// The text is sent in the form of a 2D grid of UTF-8 encoded characters (the default encoding in rust).
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection, Origin};
 | ||||
|     /// # use servicepoint::{Command, Connection, Origin, CharGrid};
 | ||||
|     /// # let connection = Connection::Fake;
 | ||||
|     /// let grid = CharGrid::from("Hello,\nWorld!");
 | ||||
|     /// connection.send(Command::Utf8Data(Origin::ZERO, grid)).expect("send failed");
 | ||||
|     /// ```
 | ||||
|     Utf8Data(Origin<Tiles>, CharGrid), | ||||
| 
 | ||||
|     /// Show text on the screen.
 | ||||
|     ///
 | ||||
|     /// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
 | ||||
|     ///
 | ||||
|     /// <div class="warning">You probably want to use [Command::Utf8Data] instead</div>
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use servicepoint::{Command, Connection, Origin, CharGrid, Cp437Grid};
 | ||||
|     /// # let connection = Connection::Fake;
 | ||||
|     /// use servicepoint::{CharGrid, Cp437Grid};
 | ||||
|     /// let grid = CharGrid::from("Hello,\nWorld!");
 | ||||
|     /// let grid = Cp437Grid::from(&grid);
 | ||||
|     /// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed");
 | ||||
|  | @ -234,6 +246,8 @@ pub enum TryFromPacketError { | |||
|     /// The given brightness value is out of bounds
 | ||||
|     #[error("The given brightness value {0} is out of bounds.")] | ||||
|     InvalidBrightness(u8), | ||||
|     #[error(transparent)] | ||||
|     InvalidUtf8(#[from] std::string::FromUtf8Error), | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<Packet> for Command { | ||||
|  | @ -269,6 +283,7 @@ impl TryFrom<Packet> for Command { | |||
|             CommandCode::CharBrightness => { | ||||
|                 Self::packet_into_char_brightness(&packet) | ||||
|             } | ||||
|             CommandCode::Utf8Data => Self::packet_into_utf8(&packet), | ||||
|             #[allow(deprecated)] | ||||
|             CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy), | ||||
|             CommandCode::BitmapLinear => { | ||||
|  | @ -426,8 +441,7 @@ impl Command { | |||
|             payload, | ||||
|         } = packet; | ||||
| 
 | ||||
|         let grid = | ||||
|             PrimitiveGrid::load(*width as usize, *height as usize, payload); | ||||
|         let grid = ValueGrid::load(*width as usize, *height as usize, payload); | ||||
|         let grid = match BrightnessGrid::try_from(grid) { | ||||
|             Ok(grid) => grid, | ||||
|             Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)), | ||||
|  | @ -489,18 +503,37 @@ impl Command { | |||
|             Cp437Grid::load(*c as usize, *d as usize, payload), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn packet_into_utf8( | ||||
|         packet: &Packet, | ||||
|     ) -> Result<Command, TryFromPacketError> { | ||||
|         let Packet { | ||||
|             header: | ||||
|                 Header { | ||||
|                     command_code: _, | ||||
|                     a, | ||||
|                     b, | ||||
|                     c, | ||||
|                     d, | ||||
|                 }, | ||||
|             payload, | ||||
|         } = packet; | ||||
|         let payload: Vec<_> = | ||||
|             String::from_utf8(payload.clone())?.chars().collect(); | ||||
|         Ok(Command::Utf8Data( | ||||
|             Origin::new(*a as usize, *b as usize), | ||||
|             CharGrid::load(*c as usize, *d as usize, &payload), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::command::TryFromPacketError; | ||||
|     use crate::command_code::CommandCode; | ||||
|     use crate::{ | ||||
|         bitvec::prelude::BitVec, | ||||
|         command::TryFromPacketError, | ||||
|         command_code::CommandCode, | ||||
|         origin::Pixels, | ||||
|         packet::{Header, Packet}, | ||||
|         Bitmap, Brightness, BrightnessGrid, Command, CompressionCode, Origin, | ||||
|         PrimitiveGrid, | ||||
|         BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, Command, | ||||
|         CompressionCode, Cp437Grid, Header, Origin, Packet, Pixels, | ||||
|     }; | ||||
| 
 | ||||
|     fn round_trip(original: Command) { | ||||
|  | @ -556,16 +589,18 @@ mod tests { | |||
|     fn round_trip_char_brightness() { | ||||
|         round_trip(Command::CharBrightness( | ||||
|             Origin::new(5, 2), | ||||
|             PrimitiveGrid::new(7, 5), | ||||
|             BrightnessGrid::new(7, 5), | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_cp437_data() { | ||||
|         round_trip(Command::Cp437Data( | ||||
|             Origin::new(5, 2), | ||||
|             PrimitiveGrid::new(7, 5), | ||||
|         )); | ||||
|         round_trip(Command::Cp437Data(Origin::new(5, 2), Cp437Grid::new(7, 5))); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_utf8_data() { | ||||
|         round_trip(Command::Utf8Data(Origin::new(5, 2), CharGrid::new(7, 5))); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ pub(crate) enum CommandCode { | |||
|     BitmapLinearWinBzip2 = 0x0018, | ||||
|     #[cfg(feature = "compression_lzma")] | ||||
|     BitmapLinearWinLzma = 0x0019, | ||||
|     Utf8Data = 0x0020, | ||||
|     #[cfg(feature = "compression_zstd")] | ||||
|     BitmapLinearWinZstd = 0x001A, | ||||
| } | ||||
|  | @ -93,6 +94,9 @@ impl TryFrom<u16> for CommandCode { | |||
|             value if value == CommandCode::BitmapLinearWinBzip2 as u16 => { | ||||
|                 Ok(CommandCode::BitmapLinearWinBzip2) | ||||
|             } | ||||
|             value if value == CommandCode::Utf8Data as u16 => { | ||||
|                 Ok(CommandCode::Utf8Data) | ||||
|             } | ||||
|             _ => Err(()), | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ use flate2::{FlushCompress, FlushDecompress, Status}; | |||
| #[cfg(feature = "compression_zstd")] | ||||
| use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder}; | ||||
| 
 | ||||
| use crate::{packet::Payload, CompressionCode}; | ||||
| use crate::{CompressionCode, Payload}; | ||||
| 
 | ||||
| pub(crate) fn into_decompressed( | ||||
|     kind: CompressionCode, | ||||
|  |  | |||
|  | @ -107,9 +107,7 @@ impl Connection { | |||
| 
 | ||||
|         let request = ClientRequestBuilder::new(uri).into_client_request()?; | ||||
|         let (sock, _) = connect(request)?; | ||||
|         Ok(Self::WebSocket(std::sync::Mutex::new( | ||||
|             sock, | ||||
|         ))) | ||||
|         Ok(Self::WebSocket(std::sync::Mutex::new(sock))) | ||||
|     } | ||||
| 
 | ||||
|     /// Send something packet-like to the display. Usually this is in the form of a Command.
 | ||||
|  | @ -144,7 +142,7 @@ impl Connection { | |||
|             Connection::WebSocket(socket) => { | ||||
|                 let mut socket = socket.lock().unwrap(); | ||||
|                 socket | ||||
|                     .send(tungstenite::Message::Binary(data)) | ||||
|                     .send(tungstenite::Message::Binary(data.into())) | ||||
|                     .map_err(SendError::WebsocketError) | ||||
|             } | ||||
|             Connection::Fake => { | ||||
|  | @ -159,9 +157,7 @@ impl Drop for Connection { | |||
|     fn drop(&mut self) { | ||||
|         #[cfg(feature = "protocol_websocket")] | ||||
|         if let Connection::WebSocket(sock) = self { | ||||
|             _ = sock | ||||
|                 .try_lock() | ||||
|                 .map(move |mut sock| sock.close(None)); | ||||
|             _ = sock.try_lock().map(move |mut sock| sock.close(None)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -169,7 +165,6 @@ impl Drop for Connection { | |||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::packet::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn send_fake() { | ||||
|  |  | |||
							
								
								
									
										75
									
								
								crates/servicepoint/src/constants.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								crates/servicepoint/src/constants.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| use std::time::Duration; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
| pub const TILE_SIZE: usize = 8; | ||||
| 
 | ||||
| /// Display tile count in the x-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_WIDTH: usize = 56; | ||||
| 
 | ||||
| /// Display tile count in the y-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_HEIGHT: usize = 20; | ||||
| 
 | ||||
| /// Display width in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||
| 
 | ||||
| /// Display height in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | ||||
| 
 | ||||
| /// pixel count on whole screen
 | ||||
| pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use std::time::Instant;
 | ||||
| /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
 | ||||
| /// # let connection = servicepoint::Connection::Fake;
 | ||||
| /// # let pixels = Bitmap::max_sized();
 | ||||
| /// loop {
 | ||||
| ///    let start = Instant::now();
 | ||||
| ///
 | ||||
| ///    // Change pixels here
 | ||||
| ///
 | ||||
| ///    connection.send(Command::BitmapLinearWin(
 | ||||
| ///            Origin::new(0,0),
 | ||||
| ///            pixels,
 | ||||
| ///            CompressionCode::Lzma
 | ||||
| ///        ))
 | ||||
| ///        .expect("send failed");
 | ||||
| ///
 | ||||
| ///    // warning: will crash if resulting duration is negative, e.g. when resuming from standby
 | ||||
| ///    std::thread::sleep(FRAME_PACING - start.elapsed());
 | ||||
| ///    # break; // prevent doctest from hanging
 | ||||
| /// }
 | ||||
| /// ```
 | ||||
| pub const FRAME_PACING: Duration = Duration::from_millis(30); | ||||
|  | @ -1,153 +1,54 @@ | |||
| //! Conversion between UTF-8 and CP-437.
 | ||||
| //!
 | ||||
| //! Most of the functionality is only available with feature "cp437" enabled.
 | ||||
| 
 | ||||
| use crate::{Grid, PrimitiveGrid}; | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| /// A grid containing codepage 437 characters.
 | ||||
| /// Contains functions to convert between UTF-8 and Codepage 437.
 | ||||
| ///
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| pub type Cp437Grid = PrimitiveGrid<u8>; | ||||
| /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
 | ||||
| pub struct Cp437Converter; | ||||
| 
 | ||||
| /// The error occurring when loading an invalid character
 | ||||
| #[derive(Debug, PartialEq, thiserror::Error)] | ||||
| #[error("The character {char:?} at position {index} is not a valid CP437 character")] | ||||
| pub struct InvalidCharError { | ||||
|     /// invalid character is at this position in input
 | ||||
|     index: usize, | ||||
|     /// the invalid character
 | ||||
|     char: char, | ||||
| } | ||||
| 
 | ||||
| impl Cp437Grid { | ||||
|     /// Load an ASCII-only [&str] into a [Cp437Grid] of specified width.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// - for width == 0
 | ||||
|     /// - on empty strings
 | ||||
|     pub fn load_ascii( | ||||
|         value: &str, | ||||
|         width: usize, | ||||
|         wrap: bool, | ||||
|     ) -> Result<Self, InvalidCharError> { | ||||
|         assert!(width > 0); | ||||
|         assert!(!value.is_empty()); | ||||
| 
 | ||||
|         let mut chars = { | ||||
|             let mut x = 0; | ||||
|             let mut y = 0; | ||||
| 
 | ||||
|             for (index, char) in value.chars().enumerate() { | ||||
|                 if !char.is_ascii() { | ||||
|                     return Err(InvalidCharError { index, char }); | ||||
|                 } | ||||
| 
 | ||||
|                 let is_lf = char == '\n'; | ||||
|                 if is_lf || (wrap && x == width) { | ||||
|                     y += 1; | ||||
|                     x = 0; | ||||
|                     if is_lf { | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 x += 1; | ||||
|             } | ||||
| 
 | ||||
|             Cp437Grid::new(width, y + 1) | ||||
|         }; | ||||
| 
 | ||||
|         let mut x = 0; | ||||
|         let mut y = 0; | ||||
|         for char in value.chars().map(move |c| c as u8) { | ||||
|             let is_lf = char == b'\n'; | ||||
|             if is_lf || (wrap && x == width) { | ||||
|                 y += 1; | ||||
|                 x = 0; | ||||
|                 if is_lf { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if wrap || x < width { | ||||
|                 chars.set(x, y, char); | ||||
|             } | ||||
|             x += 1; | ||||
|         } | ||||
| 
 | ||||
|         Ok(chars) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[allow(unused)] // depends on features
 | ||||
| pub use feature_cp437::*; | ||||
| 
 | ||||
| #[cfg(feature = "cp437")] | ||||
| mod feature_cp437 { | ||||
|     use super::*; | ||||
|     use crate::CharGrid; | ||||
| 
 | ||||
|     /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
 | ||||
|     ///
 | ||||
|     /// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
 | ||||
|     ///
 | ||||
|     /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
 | ||||
|     ///
 | ||||
|     /// Mostly copied from <https://github.com/kip93/cp437-tools>. License: GPL-3.0
 | ||||
|     #[rustfmt::skip] | ||||
|     pub const CP437_TO_UTF8: [char; 256] = [ | ||||
|         /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', | ||||
|         /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔',  '▲', '▼', | ||||
|         /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', | ||||
|         /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', | ||||
|         /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||||
|         /* 5X */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_', | ||||
|         /* 6X */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||||
|         /* 7X */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂', | ||||
|         /* 8X */ 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å', | ||||
|         /* 9X */ 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ', | ||||
|         /* AX */ 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»', | ||||
|         /* BX */ '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐', | ||||
|         /* CX */ '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧', | ||||
|         /* DX */ '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀', | ||||
|         /* EX */ 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩', | ||||
|         /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', | ||||
|     ]; | ||||
| 
 | ||||
|     static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> = | ||||
|         once_cell::sync::Lazy::new(|| { | ||||
|             let pairs = CP437_TO_UTF8 | ||||
|                 .iter() | ||||
|                 .enumerate() | ||||
|                 .map(move |(index, char)| (*char, index as u8)); | ||||
|             HashMap::from_iter(pairs) | ||||
|         }); | ||||
| /// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
 | ||||
| ///
 | ||||
| /// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
 | ||||
| ///
 | ||||
| /// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
 | ||||
| ///
 | ||||
| /// Mostly copied from <https://github.com/kip93/cp437-tools>. License: GPL-3.0
 | ||||
| #[rustfmt::skip] | ||||
| const CP437_TO_UTF8: [char; 256] = [ | ||||
|     /* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼', | ||||
|     /* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔',  '▲', '▼', | ||||
|     /* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', | ||||
|     /* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', | ||||
|     /* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||||
|     /* 5X */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_', | ||||
|     /* 6X */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||||
|     /* 7X */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂', | ||||
|     /* 8X */ 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å', | ||||
|     /* 9X */ 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ', | ||||
|     /* AX */ 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»', | ||||
|     /* BX */ '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐', | ||||
|     /* CX */ '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧', | ||||
|     /* DX */ '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀', | ||||
|     /* EX */ 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩', | ||||
|     /* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ', | ||||
| ]; | ||||
| static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> = | ||||
|     once_cell::sync::Lazy::new(|| { | ||||
|         let pairs = CP437_TO_UTF8 | ||||
|             .iter() | ||||
|             .enumerate() | ||||
|             .map(move |(index, char)| (*char, index as u8)); | ||||
|         HashMap::from_iter(pairs) | ||||
|     }); | ||||
| 
 | ||||
| impl Cp437Converter { | ||||
|     const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
 | ||||
| 
 | ||||
|     impl From<&Cp437Grid> for CharGrid { | ||||
|         fn from(value: &Cp437Grid) -> Self { | ||||
|             value.map(cp437_to_char) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<&CharGrid> for Cp437Grid { | ||||
|         fn from(value: &CharGrid) -> Self { | ||||
|             value.map(char_to_cp437) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<CharGrid> for Cp437Grid { | ||||
|         fn from(value: CharGrid) -> Self { | ||||
|             Cp437Grid::from(&value) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Convert the provided bytes to UTF-8.
 | ||||
|     pub fn cp437_to_str(cp437: &[u8]) -> String { | ||||
|         cp437.iter().map(move |char| cp437_to_char(*char)).collect() | ||||
|         cp437 | ||||
|             .iter() | ||||
|             .map(move |char| Self::cp437_to_char(*char)) | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a single CP-437 character to UTF-8.
 | ||||
|  | @ -159,66 +60,20 @@ mod feature_cp437 { | |||
|     ///
 | ||||
|     /// Characters that are not available are mapped to '?'.
 | ||||
|     pub fn str_to_cp437(utf8: &str) -> Vec<u8> { | ||||
|         utf8.chars().map(char_to_cp437).collect() | ||||
|         utf8.chars().map(Self::char_to_cp437).collect() | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a single UTF-8 character to CP-437.
 | ||||
|     pub fn char_to_cp437(utf8: char) -> u8 { | ||||
|         *UTF8_TO_CP437.get(&utf8).unwrap_or(&MISSING_CHAR_CP437) | ||||
|         *UTF8_TO_CP437 | ||||
|             .get(&utf8) | ||||
|             .unwrap_or(&Self::MISSING_CHAR_CP437) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_nowrap() { | ||||
|         let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] | ||||
|             .map(move |c| c as u8); | ||||
|         let expected = Cp437Grid::load(5, 2, &chars); | ||||
| 
 | ||||
|         let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap(); | ||||
|         // comma will be removed because line is too long and wrap is off
 | ||||
|         assert_eq!(actual, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_wrap() { | ||||
|         let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] | ||||
|             .map(move |c| c as u8); | ||||
|         let expected = Cp437Grid::load(5, 2, &chars); | ||||
| 
 | ||||
|         let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap(); | ||||
|         // line break will be added
 | ||||
|         assert_eq!(actual, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_invalid() { | ||||
|         assert_eq!( | ||||
|             Err(InvalidCharError { | ||||
|                 char: '🥶', | ||||
|                 index: 2 | ||||
|             }), | ||||
|             Cp437Grid::load_ascii("?#🥶42", 3, false) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[cfg(feature = "cp437")] | ||||
| mod tests_feature_cp437 { | ||||
|     use super::*; | ||||
|     use crate::CharGrid; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_cp437() { | ||||
|         let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); | ||||
|         let cp437 = Cp437Grid::from(&utf8); | ||||
|         let actual = CharGrid::from(&cp437); | ||||
|         assert_eq!(actual, utf8); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn convert_str() { | ||||
|  | @ -245,13 +100,16 @@ mod tests_feature_cp437 { | |||
|         │dx ≡ Σ √x²ⁿ·δx | ||||
|         ⌡"#;
 | ||||
| 
 | ||||
|         let cp437 = str_to_cp437(utf8); | ||||
|         let actual = cp437_to_str(&*cp437); | ||||
|         let cp437 = Cp437Converter::str_to_cp437(utf8); | ||||
|         let actual = Cp437Converter::cp437_to_str(&*cp437); | ||||
|         assert_eq!(utf8, actual) | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn convert_invalid() { | ||||
|         assert_eq!(cp437_to_char(char_to_cp437('😜')), '?'); | ||||
|         assert_eq!( | ||||
|             Cp437Converter::cp437_to_char(Cp437Converter::char_to_cp437('😜')), | ||||
|             '?' | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										163
									
								
								crates/servicepoint/src/cp437_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								crates/servicepoint/src/cp437_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | |||
| /// A grid containing codepage 437 characters.
 | ||||
| ///
 | ||||
| /// The encoding is currently not enforced.
 | ||||
| pub type Cp437Grid = crate::value_grid::ValueGrid<u8>; | ||||
| 
 | ||||
| /// The error occurring when loading an invalid character
 | ||||
| #[derive(Debug, PartialEq, thiserror::Error)] | ||||
| #[error(
 | ||||
|     "The character {char:?} at position {index} is not a valid CP437 character" | ||||
| )] | ||||
| pub struct InvalidCharError { | ||||
|     /// invalid character is at this position in input
 | ||||
|     index: usize, | ||||
|     /// the invalid character
 | ||||
|     char: char, | ||||
| } | ||||
| 
 | ||||
| impl Cp437Grid { | ||||
|     /// Load an ASCII-only [&str] into a [Cp437Grid] of specified width.
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// - for width == 0
 | ||||
|     /// - on empty strings
 | ||||
|     pub fn load_ascii( | ||||
|         value: &str, | ||||
|         width: usize, | ||||
|         wrap: bool, | ||||
|     ) -> Result<Self, InvalidCharError> { | ||||
|         assert!(width > 0); | ||||
|         assert!(!value.is_empty()); | ||||
| 
 | ||||
|         let mut chars = { | ||||
|             let mut x = 0; | ||||
|             let mut y = 0; | ||||
| 
 | ||||
|             for (index, char) in value.chars().enumerate() { | ||||
|                 if !char.is_ascii() { | ||||
|                     return Err(InvalidCharError { index, char }); | ||||
|                 } | ||||
| 
 | ||||
|                 let is_lf = char == '\n'; | ||||
|                 if is_lf || (wrap && x == width) { | ||||
|                     y += 1; | ||||
|                     x = 0; | ||||
|                     if is_lf { | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 x += 1; | ||||
|             } | ||||
| 
 | ||||
|             Cp437Grid::new(width, y + 1) | ||||
|         }; | ||||
| 
 | ||||
|         let mut x = 0; | ||||
|         let mut y = 0; | ||||
|         for char in value.chars().map(move |c| c as u8) { | ||||
|             let is_lf = char == b'\n'; | ||||
|             if is_lf || (wrap && x == width) { | ||||
|                 y += 1; | ||||
|                 x = 0; | ||||
|                 if is_lf { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if wrap || x < width { | ||||
|                 chars.set(x, y, char); | ||||
|             } | ||||
|             x += 1; | ||||
|         } | ||||
| 
 | ||||
|         Ok(chars) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| use crate::Grid; | ||||
| #[allow(unused)] // depends on features
 | ||||
| pub use feature_cp437::*; | ||||
| 
 | ||||
| #[cfg(feature = "cp437")] | ||||
| mod feature_cp437 { | ||||
|     use super::*; | ||||
|     use crate::{CharGrid, Cp437Converter}; | ||||
| 
 | ||||
|     impl From<&Cp437Grid> for CharGrid { | ||||
|         fn from(value: &Cp437Grid) -> Self { | ||||
|             value.map(Cp437Converter::cp437_to_char) | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     impl From<Cp437Grid> for CharGrid { | ||||
|         fn from(value: Cp437Grid) -> Self { | ||||
|             Self::from(&value) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<&CharGrid> for Cp437Grid { | ||||
|         fn from(value: &CharGrid) -> Self { | ||||
|             value.map(Cp437Converter::char_to_cp437) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl From<CharGrid> for Cp437Grid { | ||||
|         fn from(value: CharGrid) -> Self { | ||||
|             Self::from(&value) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_nowrap() { | ||||
|         let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] | ||||
|             .map(move |c| c as u8); | ||||
|         let expected = Cp437Grid::load(5, 2, &chars); | ||||
| 
 | ||||
|         let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap(); | ||||
|         // comma will be removed because line is too long and wrap is off
 | ||||
|         assert_eq!(actual, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_wrap() { | ||||
|         let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'] | ||||
|             .map(move |c| c as u8); | ||||
|         let expected = Cp437Grid::load(5, 2, &chars); | ||||
| 
 | ||||
|         let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap(); | ||||
|         // line break will be added
 | ||||
|         assert_eq!(actual, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn load_ascii_invalid() { | ||||
|         assert_eq!( | ||||
|             Err(InvalidCharError { | ||||
|                 char: '🥶', | ||||
|                 index: 2 | ||||
|             }), | ||||
|             Cp437Grid::load_ascii("?#🥶42", 3, false) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| #[cfg(feature = "cp437")] | ||||
| mod tests_feature_cp437 { | ||||
|     use super::*; | ||||
|     use crate::CharGrid; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn round_trip_cp437() { | ||||
|         let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); | ||||
|         let cp437 = Cp437Grid::from(utf8.clone()); | ||||
|         let actual = CharGrid::from(cp437); | ||||
|         assert_eq!(actual, utf8); | ||||
|     } | ||||
| } | ||||
|  | @ -2,14 +2,17 @@ | |||
| //!
 | ||||
| //! Your starting point is a [Connection] to the display.
 | ||||
| //! With a connection, you can send [Command]s.
 | ||||
| //! When received, the display will update the state of the pixels.
 | ||||
| //! When received, the display will update the state of its pixels.
 | ||||
| //!
 | ||||
| //! # Examples
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||
| //! ### Clear display
 | ||||
| //!
 | ||||
| //! let connection = servicepoint::Connection::open("127.0.0.1:2342")
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Connection, Command};
 | ||||
| //!
 | ||||
| //! // establish a connection
 | ||||
| //! let connection = Connection::open("127.0.0.1:2342")
 | ||||
| //!     .expect("connection failed");
 | ||||
| //!
 | ||||
| //!  // turn off all pixels on display
 | ||||
|  | @ -17,6 +20,8 @@ | |||
| //!     .expect("send failed");
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! ### Set all pixels to on
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
 | ||||
| //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
 | ||||
|  | @ -34,114 +39,63 @@ | |||
| //!  // send command to display
 | ||||
| //!  connection.send(command).expect("send failed");
 | ||||
| //! ```
 | ||||
| //!
 | ||||
| //! ### Send text
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! # use servicepoint::{Command, CompressionCode, Grid, Bitmap, CharGrid};
 | ||||
| //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
 | ||||
| //! // create a text grid
 | ||||
| //! let mut grid = CharGrid::from("Hello\nCCCB?");
 | ||||
| //! // modify the grid
 | ||||
| //! grid.set(grid.width() - 1, 1, '!');
 | ||||
| //! // create the command to send the data
 | ||||
| //! let command = Command::Utf8Data(servicepoint::Origin::ZERO, grid);
 | ||||
| //! // send command to display
 | ||||
| //! connection.send(command).expect("send failed");
 | ||||
| //! ```
 | ||||
| 
 | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| pub use bitvec; | ||||
| 
 | ||||
| pub use crate::bit_vec::{bitvec, BitVec}; | ||||
| pub use crate::bitmap::Bitmap; | ||||
| pub use crate::brightness::{Brightness, BrightnessGrid}; | ||||
| pub use crate::brightness::Brightness; | ||||
| pub use crate::brightness_grid::BrightnessGrid; | ||||
| pub use crate::byte_grid::ByteGrid; | ||||
| pub use crate::char_grid::CharGrid; | ||||
| pub use crate::command::{Command, Offset}; | ||||
| pub use crate::compression_code::CompressionCode; | ||||
| pub use crate::connection::Connection; | ||||
| pub use crate::cp437::Cp437Grid; | ||||
| pub use crate::constants::*; | ||||
| pub use crate::cp437::Cp437Converter; | ||||
| pub use crate::cp437_grid::Cp437Grid; | ||||
| pub use crate::data_ref::DataRef; | ||||
| pub use crate::grid::Grid; | ||||
| pub use crate::origin::{Origin, Pixels, Tiles}; | ||||
| pub use crate::primitive_grid::{PrimitiveGrid, SeriesError}; | ||||
| 
 | ||||
| /// An alias for the specific type of [bitvec::prelude::BitVec] used.
 | ||||
| pub type BitVec = bitvec::prelude::BitVec<u8, bitvec::prelude::Msb0>; | ||||
| pub use crate::packet::{Header, Packet, Payload}; | ||||
| pub use crate::value_grid::{ | ||||
|     IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid, | ||||
| }; | ||||
| 
 | ||||
| mod bit_vec; | ||||
| mod bitmap; | ||||
| mod brightness; | ||||
| mod brightness_grid; | ||||
| mod byte_grid; | ||||
| mod char_grid; | ||||
| mod command; | ||||
| mod command_code; | ||||
| mod compression; | ||||
| mod compression_code; | ||||
| mod connection; | ||||
| pub mod cp437; | ||||
| mod constants; | ||||
| mod cp437_grid; | ||||
| mod data_ref; | ||||
| mod grid; | ||||
| mod origin; | ||||
| pub mod packet; | ||||
| mod primitive_grid; | ||||
| mod packet; | ||||
| mod value_grid; | ||||
| 
 | ||||
| /// size of a single tile in one dimension
 | ||||
| pub const TILE_SIZE: usize = 8; | ||||
| 
 | ||||
| /// Display tile count in the x-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_WIDTH: usize = 56; | ||||
| 
 | ||||
| /// Display tile count in the y-direction
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
| /// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const TILE_HEIGHT: usize = 20; | ||||
| 
 | ||||
| /// Display width in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE; | ||||
| 
 | ||||
| /// Display height in pixels
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
 | ||||
| /// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | ||||
| /// ```
 | ||||
| pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE; | ||||
| 
 | ||||
| /// pixel count on whole screen
 | ||||
| pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT; | ||||
| 
 | ||||
| /// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use std::time::Instant;
 | ||||
| /// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
 | ||||
| /// # let connection = servicepoint::Connection::Fake;
 | ||||
| /// # let pixels = Bitmap::max_sized();
 | ||||
| /// loop {
 | ||||
| ///    let start = Instant::now();
 | ||||
| ///
 | ||||
| ///    // Change pixels here
 | ||||
| ///
 | ||||
| ///    connection.send(Command::BitmapLinearWin(
 | ||||
| ///            Origin::new(0,0),
 | ||||
| ///            pixels,
 | ||||
| ///            CompressionCode::Lzma
 | ||||
| ///        ))
 | ||||
| ///        .expect("send failed");
 | ||||
| ///
 | ||||
| ///    // warning: will crash if resulting duration is negative, e.g. when resuming from standby
 | ||||
| ///    std::thread::sleep(FRAME_PACING - start.elapsed());
 | ||||
| ///    # break; // prevent doctest from hanging
 | ||||
| /// }
 | ||||
| /// ```
 | ||||
| pub const FRAME_PACING: Duration = Duration::from_millis(30); | ||||
| #[cfg(feature = "cp437")] | ||||
| mod cp437; | ||||
| 
 | ||||
| // include README.md in doctest
 | ||||
| #[doc = include_str!("../README.md")] | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| //! Converting a packet to a command and back:
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Command, packet::Packet};
 | ||||
| //! use servicepoint::{Command, Packet};
 | ||||
| //! # let command = Command::Clear;
 | ||||
| //! let packet: Packet = command.into();
 | ||||
| //! let command: Command = Command::try_from(packet).expect("could not read command from packet");
 | ||||
|  | @ -16,20 +16,20 @@ | |||
| //! Converting a packet to bytes and back:
 | ||||
| //!
 | ||||
| //! ```rust
 | ||||
| //! use servicepoint::{Command, packet::Packet};
 | ||||
| //! 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");
 | ||||
| //! ```
 | ||||
| 
 | ||||
| use std::mem::size_of; | ||||
| 
 | ||||
| use crate::command_code::CommandCode; | ||||
| use crate::compression::into_compressed; | ||||
| use crate::{ | ||||
|     command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset, | ||||
|     Origin, Pixels, Tiles, TILE_SIZE, | ||||
|     Bitmap, Command, CompressionCode, Grid, Offset, Origin, Pixels, Tiles, | ||||
|     TILE_SIZE, | ||||
| }; | ||||
| use std::mem::size_of; | ||||
| 
 | ||||
| /// A raw header.
 | ||||
| ///
 | ||||
|  | @ -209,6 +209,9 @@ impl From<Command> for Packet { | |||
|                 grid, | ||||
|                 CommandCode::Cp437Data, | ||||
|             ), | ||||
|             Command::Utf8Data(origin, grid) => { | ||||
|                 Self::origin_grid_to_packet(origin, grid, CommandCode::Utf8Data) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,20 @@ | |||
| use std::fmt::Debug; | ||||
| use std::slice::{Iter, IterMut}; | ||||
| 
 | ||||
| use crate::{DataRef, Grid}; | ||||
| use crate::*; | ||||
| 
 | ||||
| pub trait PrimitiveGridType: Sized + Default + Copy + Clone {} | ||||
| impl<T: Sized + Default + Copy + Clone> PrimitiveGridType for T {} | ||||
| /// A type that can be stored in a [ValueGrid], e.g. [char], [u8].
 | ||||
| pub trait Value: Sized + Default + Copy + Clone + Debug {} | ||||
| impl<T: Sized + Default + Copy + Clone + Debug> Value for T {} | ||||
| 
 | ||||
| /// A 2D grid of bytes
 | ||||
| /// A 2D grid of values.
 | ||||
| ///
 | ||||
| /// The memory layout is the one the display expects in [Command]s.
 | ||||
| ///
 | ||||
| /// This structure can be used with any type that implements the [Value] trait.
 | ||||
| /// You can also use the concrete type aliases provided in this crate, e.g. [CharGrid] and [ByteGrid].
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct PrimitiveGrid<T: PrimitiveGridType> { | ||||
| pub struct ValueGrid<T: Value> { | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: Vec<T>, | ||||
|  | @ -15,7 +22,7 @@ pub struct PrimitiveGrid<T: PrimitiveGridType> { | |||
| 
 | ||||
| /// Error type for methods that change a whole column or row at once
 | ||||
| #[derive(thiserror::Error, Debug, PartialEq)] | ||||
| pub enum SeriesError { | ||||
| pub enum SetValueSeriesError { | ||||
|     #[error("The index {index} was out of bounds for size {size}")] | ||||
|     /// The index {index} was out of bounds for size {size}
 | ||||
|     OutOfBounds { | ||||
|  | @ -34,15 +41,15 @@ pub enum SeriesError { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl<T: PrimitiveGridType> PrimitiveGrid<T> { | ||||
|     /// Creates a new [PrimitiveGrid] with the specified dimensions.
 | ||||
| impl<T: Value> ValueGrid<T> { | ||||
|     /// Creates a new [ValueGrid] with the specified dimensions.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|     /// - width: size in x-direction
 | ||||
|     /// - height: size in y-direction
 | ||||
|     ///
 | ||||
|     /// returns: [PrimitiveGrid] initialized to default value.
 | ||||
|     /// returns: [ValueGrid] initialized to default value.
 | ||||
|     pub fn new(width: usize, height: usize) -> Self { | ||||
|         Self { | ||||
|             data: vec![Default::default(); width * height], | ||||
|  | @ -51,16 +58,20 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Loads a [PrimitiveGrid] with the specified dimensions from the provided data.
 | ||||
|     /// Loads a [ValueGrid] with the specified dimensions from the provided data.
 | ||||
|     ///
 | ||||
|     /// returns: [PrimitiveGrid] that contains a copy of the provided data
 | ||||
|     /// returns: [ValueGrid] that contains a copy of the provided data
 | ||||
|     ///
 | ||||
|     /// # Panics
 | ||||
|     ///
 | ||||
|     /// - when the dimensions and data size do not match exactly.
 | ||||
|     #[must_use] | ||||
|     pub fn load(width: usize, height: usize, data: &[T]) -> Self { | ||||
|         assert_eq!(width * height, data.len()); | ||||
|         assert_eq!( | ||||
|             width * height, | ||||
|             data.len(), | ||||
|             "dimension mismatch for data {data:?}" | ||||
|         ); | ||||
|         Self { | ||||
|             data: Vec::from(data), | ||||
|             width, | ||||
|  | @ -68,12 +79,52 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all cells in [PrimitiveGrid].
 | ||||
|     /// Loads a [ValueGrid] with the specified width from the provided data, wrapping to as many rows as needed.
 | ||||
|     ///
 | ||||
|     /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
 | ||||
|     ///
 | ||||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::ValueGrid;
 | ||||
|     /// let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap();
 | ||||
|     /// ```
 | ||||
|     pub fn wrap( | ||||
|         width: usize, | ||||
|         data: &[T], | ||||
|     ) -> Result<Self, TryLoadValueGridError> { | ||||
|         let len = data.len(); | ||||
|         if len % width != 0 { | ||||
|             return Err(TryLoadValueGridError::InvalidDimensions); | ||||
|         } | ||||
|         Ok(Self::load(width, len / width, data)) | ||||
|     } | ||||
| 
 | ||||
|     /// Loads a [ValueGrid] with the specified dimensions from the provided data.
 | ||||
|     ///
 | ||||
|     /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
 | ||||
|     pub fn try_load( | ||||
|         width: usize, | ||||
|         height: usize, | ||||
|         data: Vec<T>, | ||||
|     ) -> Result<Self, TryLoadValueGridError> { | ||||
|         if width * height != data.len() { | ||||
|             return Err(TryLoadValueGridError::InvalidDimensions); | ||||
|         } | ||||
| 
 | ||||
|         Ok(Self { | ||||
|             data, | ||||
|             width, | ||||
|             height, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all cells in [ValueGrid].
 | ||||
|     ///
 | ||||
|     /// Order is equivalent to the following loop:
 | ||||
|     /// ```
 | ||||
|     /// # use servicepoint::{PrimitiveGrid, Grid};
 | ||||
|     /// # let grid = PrimitiveGrid::<u8>::new(2,2);
 | ||||
|     /// # use servicepoint::{ByteGrid, Grid};
 | ||||
|     /// # let grid = ByteGrid::new(2,2);
 | ||||
|     /// for y in 0..grid.height() {
 | ||||
|     ///     for x in 0..grid.width() {
 | ||||
|     ///         grid.get(x, y);
 | ||||
|  | @ -84,9 +135,9 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         self.data.iter() | ||||
|     } | ||||
| 
 | ||||
|     /// Iterate over all rows in [PrimitiveGrid] top to bottom.
 | ||||
|     pub fn iter_rows(&self) -> IterRows<T> { | ||||
|         IterRows { | ||||
|     /// Iterate over all rows in [ValueGrid] top to bottom.
 | ||||
|     pub fn iter_rows(&self) -> IterGridRows<T> { | ||||
|         IterGridRows { | ||||
|             byte_grid: self, | ||||
|             row: 0, | ||||
|         } | ||||
|  | @ -132,7 +183,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Convert between PrimitiveGrid types.
 | ||||
|     /// Convert between ValueGrid types.
 | ||||
|     ///
 | ||||
|     /// See also [Iterator::map].
 | ||||
|     ///
 | ||||
|  | @ -140,18 +191,18 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     ///
 | ||||
|     /// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command].
 | ||||
|     /// ```
 | ||||
|     /// # fn foo(grid: &mut PrimitiveGrid<u8>) {}
 | ||||
|     /// # use servicepoint::{Brightness, BrightnessGrid, Command, Origin, PrimitiveGrid, TILE_HEIGHT, TILE_WIDTH};
 | ||||
|     /// let mut grid: PrimitiveGrid<u8> = PrimitiveGrid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
|     /// # fn foo(grid: &mut ByteGrid) {}
 | ||||
|     /// # use servicepoint::{Brightness, BrightnessGrid, ByteGrid, Command, Origin, TILE_HEIGHT, TILE_WIDTH};
 | ||||
|     /// let mut grid: ByteGrid = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT);
 | ||||
|     /// foo(&mut grid);
 | ||||
|     /// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
 | ||||
|     /// let command = Command::CharBrightness(Origin::ZERO, grid);
 | ||||
|     /// ```
 | ||||
|     /// [Brightness]: [crate::Brightness]
 | ||||
|     /// [Command]: [crate::Command]
 | ||||
|     pub fn map<TConverted, F>(&self, f: F) -> PrimitiveGrid<TConverted> | ||||
|     pub fn map<TConverted, F>(&self, f: F) -> ValueGrid<TConverted> | ||||
|     where | ||||
|         TConverted: PrimitiveGridType, | ||||
|         TConverted: Value, | ||||
|         F: Fn(T) -> TConverted, | ||||
|     { | ||||
|         let data = self | ||||
|  | @ -159,7 +210,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|             .iter() | ||||
|             .map(|elem| f(*elem)) | ||||
|             .collect::<Vec<_>>(); | ||||
|         PrimitiveGrid::load(self.width(), self.height(), &data) | ||||
|         ValueGrid::load(self.width(), self.height(), &data) | ||||
|     } | ||||
| 
 | ||||
|     /// Copies a row from the grid.
 | ||||
|  | @ -185,9 +236,13 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     /// Overwrites a column in the grid.
 | ||||
|     ///
 | ||||
|     /// Returns [Err] if x is out of bounds or `col` is not of the correct size.
 | ||||
|     pub fn set_col(&mut self, x: usize, col: &[T]) -> Result<(), SeriesError> { | ||||
|     pub fn set_col( | ||||
|         &mut self, | ||||
|         x: usize, | ||||
|         col: &[T], | ||||
|     ) -> Result<(), SetValueSeriesError> { | ||||
|         if col.len() != self.height() { | ||||
|             return Err(SeriesError::InvalidLength { | ||||
|             return Err(SetValueSeriesError::InvalidLength { | ||||
|                 expected: self.height(), | ||||
|                 actual: col.len(), | ||||
|             }); | ||||
|  | @ -204,7 +259,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         { | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(SeriesError::OutOfBounds { | ||||
|             Err(SetValueSeriesError::OutOfBounds { | ||||
|                 index: x, | ||||
|                 size: width, | ||||
|             }) | ||||
|  | @ -214,10 +269,14 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     /// Overwrites a row in the grid.
 | ||||
|     ///
 | ||||
|     /// Returns [Err] if y is out of bounds or `row` is not of the correct size.
 | ||||
|     pub fn set_row(&mut self, y: usize, row: &[T]) -> Result<(), SeriesError> { | ||||
|     pub fn set_row( | ||||
|         &mut self, | ||||
|         y: usize, | ||||
|         row: &[T], | ||||
|     ) -> Result<(), SetValueSeriesError> { | ||||
|         let width = self.width(); | ||||
|         if row.len() != width { | ||||
|             return Err(SeriesError::InvalidLength { | ||||
|             return Err(SetValueSeriesError::InvalidLength { | ||||
|                 expected: width, | ||||
|                 actual: row.len(), | ||||
|             }); | ||||
|  | @ -226,7 +285,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|         let chunk = match self.data.chunks_exact_mut(width).nth(y) { | ||||
|             Some(row) => row, | ||||
|             None => { | ||||
|                 return Err(SeriesError::OutOfBounds { | ||||
|                 return Err(SetValueSeriesError::OutOfBounds { | ||||
|                     size: self.height(), | ||||
|                     index: y, | ||||
|                 }) | ||||
|  | @ -238,8 +297,16 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | ||||
|     /// Sets the value of the cell at the specified position in the `PrimitiveGrid.
 | ||||
| /// Errors that can occur when loading a grid
 | ||||
| #[derive(Debug, thiserror::Error, PartialEq)] | ||||
| pub enum TryLoadValueGridError { | ||||
|     #[error("The provided dimensions do not match with the data size")] | ||||
|     /// The provided dimensions do not match with the data size
 | ||||
|     InvalidDimensions, | ||||
| } | ||||
| 
 | ||||
| impl<T: Value> Grid<T> for ValueGrid<T> { | ||||
|     /// Sets the value of the cell at the specified position in the `ValueGrid.
 | ||||
|     ///
 | ||||
|     /// # Arguments
 | ||||
|     ///
 | ||||
|  | @ -281,7 +348,7 @@ impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> { | ||||
| impl<T: Value> DataRef<T> for ValueGrid<T> { | ||||
|     /// Get the underlying byte rows mutable
 | ||||
|     fn data_ref_mut(&mut self) -> &mut [T] { | ||||
|         self.data.as_mut_slice() | ||||
|  | @ -293,19 +360,20 @@ impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PrimitiveGridType> From<PrimitiveGrid<T>> for Vec<T> { | ||||
| impl<T: Value> From<ValueGrid<T>> for Vec<T> { | ||||
|     /// Turn into the underlying [`Vec<u8>`] containing the rows of bytes.
 | ||||
|     fn from(value: PrimitiveGrid<T>) -> Self { | ||||
|     fn from(value: ValueGrid<T>) -> Self { | ||||
|         value.data | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct IterRows<'t, T: PrimitiveGridType> { | ||||
|     byte_grid: &'t PrimitiveGrid<T>, | ||||
| /// An iterator iver the rows in a [ValueGrid]
 | ||||
| pub struct IterGridRows<'t, T: Value> { | ||||
|     byte_grid: &'t ValueGrid<T>, | ||||
|     row: usize, | ||||
| } | ||||
| 
 | ||||
| impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { | ||||
| impl<'t, T: Value> Iterator for IterGridRows<'t, T> { | ||||
|     type Item = Iter<'t, T>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|  | @ -323,11 +391,14 @@ impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> { | |||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::{DataRef, Grid, PrimitiveGrid, SeriesError}; | ||||
|     use crate::{ | ||||
|         value_grid::{SetValueSeriesError, ValueGrid}, | ||||
|         *, | ||||
|     }; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn fill() { | ||||
|         let mut grid = PrimitiveGrid::<usize>::new(2, 2); | ||||
|         let mut grid = ValueGrid::<usize>::new(2, 2); | ||||
|         assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]); | ||||
| 
 | ||||
|         grid.fill(42); | ||||
|  | @ -336,7 +407,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn get_set() { | ||||
|         let mut grid = PrimitiveGrid::new(2, 2); | ||||
|         let mut grid = ValueGrid::new(2, 2); | ||||
|         assert_eq!(grid.get(0, 0), 0); | ||||
|         assert_eq!(grid.get(1, 1), 0); | ||||
| 
 | ||||
|  | @ -351,7 +422,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn load() { | ||||
|         let mut grid = PrimitiveGrid::new(2, 3); | ||||
|         let mut grid = ValueGrid::new(2, 3); | ||||
|         for x in 0..grid.width { | ||||
|             for y in 0..grid.height { | ||||
|                 grid.set(x, y, (x + y) as u8); | ||||
|  | @ -362,13 +433,13 @@ mod tests { | |||
| 
 | ||||
|         let data: Vec<u8> = grid.into(); | ||||
| 
 | ||||
|         let grid = PrimitiveGrid::load(2, 3, &data); | ||||
|         let grid = ValueGrid::load(2, 3, &data); | ||||
|         assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn mut_data_ref() { | ||||
|         let mut vec = PrimitiveGrid::new(2, 2); | ||||
|         let mut vec = ValueGrid::new(2, 2); | ||||
| 
 | ||||
|         let data_ref = vec.data_ref_mut(); | ||||
|         data_ref.copy_from_slice(&[1, 2, 3, 4]); | ||||
|  | @ -379,7 +450,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter() { | ||||
|         let mut vec = PrimitiveGrid::new(2, 2); | ||||
|         let mut vec = ValueGrid::new(2, 2); | ||||
|         vec.set(1, 1, 5); | ||||
| 
 | ||||
|         let mut iter = vec.iter(); | ||||
|  | @ -391,7 +462,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter_mut() { | ||||
|         let mut vec = PrimitiveGrid::new(2, 3); | ||||
|         let mut vec = ValueGrid::new(2, 3); | ||||
|         for (index, cell) in vec.iter_mut().enumerate() { | ||||
|             *cell = index as u8; | ||||
|         } | ||||
|  | @ -401,7 +472,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn iter_rows() { | ||||
|         let vec = PrimitiveGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); | ||||
|         let vec = ValueGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]); | ||||
|         for (y, row) in vec.iter_rows().enumerate() { | ||||
|             for (x, val) in row.enumerate() { | ||||
|                 assert_eq!(*val, (x + y) as u8); | ||||
|  | @ -412,20 +483,20 @@ mod tests { | |||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_x() { | ||||
|         let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         vec.set(2, 1, 5); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn out_of_bounds_y() { | ||||
|         let vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         vec.get(1, 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn ref_mut() { | ||||
|         let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
| 
 | ||||
|         let top_left = vec.get_ref_mut(0, 0); | ||||
|         *top_left += 5; | ||||
|  | @ -436,7 +507,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn optional() { | ||||
|         let mut grid = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]); | ||||
|         grid.set_optional(0, 0, 5); | ||||
|         grid.set_optional(-1, 0, 8); | ||||
|         grid.set_optional(0, 8, 42); | ||||
|  | @ -448,18 +519,18 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn col() { | ||||
|         let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); | ||||
|         let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); | ||||
|         assert_eq!(grid.get_col(0), Some(vec![0, 2, 4])); | ||||
|         assert_eq!(grid.get_col(1), Some(vec![1, 3, 5])); | ||||
|         assert_eq!(grid.get_col(2), None); | ||||
|         assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(())); | ||||
|         assert_eq!( | ||||
|             grid.set_col(2, &[5, 7, 9]), | ||||
|             Err(SeriesError::OutOfBounds { size: 2, index: 2 }) | ||||
|             Err(SetValueSeriesError::OutOfBounds { size: 2, index: 2 }) | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             grid.set_col(0, &[5, 7]), | ||||
|             Err(SeriesError::InvalidLength { | ||||
|             Err(SetValueSeriesError::InvalidLength { | ||||
|                 expected: 3, | ||||
|                 actual: 2 | ||||
|             }) | ||||
|  | @ -469,7 +540,7 @@ mod tests { | |||
| 
 | ||||
|     #[test] | ||||
|     fn row() { | ||||
|         let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); | ||||
|         let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]); | ||||
|         assert_eq!(grid.get_row(0), Some(vec![0, 1])); | ||||
|         assert_eq!(grid.get_row(2), Some(vec![4, 5])); | ||||
|         assert_eq!(grid.get_row(3), None); | ||||
|  | @ -477,14 +548,23 @@ mod tests { | |||
|         assert_eq!(grid.get_row(0), Some(vec![5, 7])); | ||||
|         assert_eq!( | ||||
|             grid.set_row(3, &[5, 7]), | ||||
|             Err(SeriesError::OutOfBounds { size: 3, index: 3 }) | ||||
|             Err(SetValueSeriesError::OutOfBounds { size: 3, index: 3 }) | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             grid.set_row(2, &[5, 7, 3]), | ||||
|             Err(SeriesError::InvalidLength { | ||||
|             Err(SetValueSeriesError::InvalidLength { | ||||
|                 expected: 2, | ||||
|                 actual: 3 | ||||
|             }) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn wrap() { | ||||
|         let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap(); | ||||
|         assert_eq!(grid.height(), 3); | ||||
| 
 | ||||
|         let grid = ValueGrid::wrap(4, &[0, 1, 2, 3, 4, 5]); | ||||
|         assert_eq!(grid.err(), Some(TryLoadValueGridError::InvalidDimensions)); | ||||
|     } | ||||
| } | ||||
|  | @ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] | |||
| cbindgen = "0.27.0" | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| path = "../servicepoint" | ||||
| features = ["all_compressions"] | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ publish = false | |||
| test = false | ||||
| 
 | ||||
| [build-dependencies] | ||||
| cc = "1.0" | ||||
| cc = "1.2" | ||||
| 
 | ||||
| [dependencies] | ||||
| servicepoint_binding_c = { path = "../.." } | ||||
|  |  | |||
|  | @ -14,12 +14,12 @@ | |||
| #define SP_BRIGHTNESS_LEVELS 12 | ||||
| 
 | ||||
| /**
 | ||||
|  * see [Brightness::MAX] | ||||
|  * see [servicepoint::Brightness::MAX] | ||||
|  */ | ||||
| #define SP_BRIGHTNESS_MAX 11 | ||||
| 
 | ||||
| /**
 | ||||
|  * see [Brightness::MIN] | ||||
|  * see [servicepoint::Brightness::MIN] | ||||
|  */ | ||||
| #define SP_BRIGHTNESS_MIN 0 | ||||
| 
 | ||||
|  | @ -131,6 +131,25 @@ typedef struct SPBitmap SPBitmap; | |||
|  */ | ||||
| typedef struct SPBrightnessGrid SPBrightnessGrid; | ||||
| 
 | ||||
| /**
 | ||||
|  * A C-wrapper for grid containing UTF-8 characters. | ||||
|  * | ||||
|  * As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers. | ||||
|  * | ||||
|  * The encoding is enforced in most cases by the rust standard library | ||||
|  * and will panic when provided with illegal characters. | ||||
|  * | ||||
|  * # Examples | ||||
|  * | ||||
|  * ```C | ||||
|  * CharGrid grid = sp_char_grid_new(4, 3); | ||||
|  * sp_char_grid_fill(grid, '?'); | ||||
|  * sp_char_grid_set(grid, 0, 0, '!'); | ||||
|  * sp_char_grid_free(grid); | ||||
|  * ``` | ||||
|  */ | ||||
| typedef struct SPCharGrid SPCharGrid; | ||||
| 
 | ||||
| /**
 | ||||
|  * A low-level display command. | ||||
|  * | ||||
|  | @ -367,6 +386,20 @@ SPBitmap *sp_bitmap_load(size_t width, | |||
| SPBitmap *sp_bitmap_new(size_t width, | ||||
|                         size_t height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new [SPBitmap] with a size matching the screen. | ||||
|  * | ||||
|  * returns: [SPBitmap] initialized to all pixels off. Will never return NULL. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling [sp_bitmap_free]. | ||||
|  */ | ||||
| SPBitmap *sp_bitmap_new_screen_sized(void); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of the specified position in the [SPBitmap]. | ||||
|  * | ||||
|  | @ -865,6 +898,196 @@ SPByteSlice sp_brightness_grid_unsafe_data_ref(SPBrightnessGrid *brightness_grid | |||
|  */ | ||||
| size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Clones a [SPCharGrid]. | ||||
|  * | ||||
|  * Will never return NULL. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  * - `char_grid` is not written to concurrently | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_char_grid_free`. | ||||
|  */ | ||||
| SPCharGrid *sp_char_grid_clone(const SPCharGrid *char_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of all cells in the [SPCharGrid]. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `char_grid`: instance to write to | ||||
|  * - `value`: the value to set all cells to | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  * - `char_grid` is not written to or read from concurrently | ||||
|  */ | ||||
| void sp_char_grid_fill(SPCharGrid *char_grid, uint32_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocates a [SPCharGrid]. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  * - `char_grid` is not used concurrently or after char_grid call | ||||
|  * - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand] | ||||
|  * | ||||
|  * [SPCommand]: [crate::SPCommand] | ||||
|  */ | ||||
| void sp_char_grid_free(SPCharGrid *char_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the current value at the specified position. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `char_grid`: instance to read from | ||||
|  * - `x` and `y`: position of the cell to read | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * - when accessing `x` or `y` out of bounds | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  * - `char_grid` is not written to concurrently | ||||
|  */ | ||||
| uint32_t sp_char_grid_get(const SPCharGrid *char_grid, size_t x, size_t y); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the height of the [SPCharGrid] instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `char_grid`: instance to read from | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  */ | ||||
| size_t sp_char_grid_height(const SPCharGrid *char_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a [SPCharGrid] with the specified dimensions from the provided data. | ||||
|  * | ||||
|  * Will never return NULL. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `data` is NULL | ||||
|  * - when the provided `data_length` does not match `height` and `width` | ||||
|  * - when `data` is not valid UTF-8 | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `data` points to a valid memory location of at least `data_length` | ||||
|  *   bytes in size. | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_char_grid_free`. | ||||
|  */ | ||||
| SPCharGrid *sp_char_grid_load(size_t width, | ||||
|                               size_t height, | ||||
|                               const uint8_t *data, | ||||
|                               size_t data_length); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new [SPCharGrid] with the specified dimensions. | ||||
|  * | ||||
|  * returns: [SPCharGrid] initialized to 0. Will never return NULL. | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - the returned instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_char_grid_free`. | ||||
|  */ | ||||
| SPCharGrid *sp_char_grid_new(size_t width, | ||||
|                              size_t height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the value of the specified position in the [SPCharGrid]. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `char_grid`: instance to write to | ||||
|  * - `x` and `y`: position of the cell | ||||
|  * - `value`: the value to write to the cell | ||||
|  * | ||||
|  * returns: old value of the cell | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * - when accessing `x` or `y` out of bounds | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPBitVec] | ||||
|  * - `char_grid` is not written to or read from concurrently | ||||
|  * | ||||
|  * [SPBitVec]: [crate::SPBitVec] | ||||
|  */ | ||||
| void sp_char_grid_set(SPCharGrid *char_grid, | ||||
|                       size_t x, | ||||
|                       size_t y, | ||||
|                       uint32_t value); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the width of the [SPCharGrid] instance. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `char_grid`: instance to read from | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `char_grid` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `char_grid` points to a valid [SPCharGrid] | ||||
|  */ | ||||
| size_t sp_char_grid_width(const SPCharGrid *char_grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Set pixel data starting at the pixel offset on screen. | ||||
|  * | ||||
|  | @ -1101,7 +1324,7 @@ SPCommand *sp_command_clear(void); | |||
| SPCommand *sp_command_clone(const SPCommand *command); | ||||
| 
 | ||||
| /**
 | ||||
|  * Show text on the screen. | ||||
|  * Show codepage 437 encoded text on the screen. | ||||
|  * | ||||
|  * The passed [SPCp437Grid] gets consumed. | ||||
|  * | ||||
|  | @ -1201,6 +1424,30 @@ SPCommand *sp_command_hard_reset(void); | |||
|  */ | ||||
| SPCommand *sp_command_try_from_packet(SPPacket *packet); | ||||
| 
 | ||||
| /**
 | ||||
|  * Show UTF-8 encoded text on the screen. | ||||
|  * | ||||
|  * The passed [SPCharGrid] gets consumed. | ||||
|  * | ||||
|  * Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `grid` is null | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `grid` points to a valid instance of [SPCharGrid] | ||||
|  * - `grid` is not used concurrently or after this call | ||||
|  * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling `sp_command_free`. | ||||
|  */ | ||||
| SPCommand *sp_command_utf8_data(size_t x, | ||||
|                                 size_t y, | ||||
|                                 SPCharGrid *grid); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new instance of [SPConnection] for testing that does not actually send anything. | ||||
|  * | ||||
|  | @ -1528,7 +1775,7 @@ SPPacket *sp_packet_clone(const SPPacket *packet); | |||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `sp_packet_free` is NULL | ||||
|  * - when `packet` is NULL | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  | @ -1560,6 +1807,40 @@ void sp_packet_free(SPPacket *packet); | |||
|  */ | ||||
| SPPacket *sp_packet_from_command(SPCommand *command); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a raw [SPPacket] from parts. | ||||
|  * | ||||
|  * # Arguments | ||||
|  * | ||||
|  * - `command_code` specifies which command this packet contains | ||||
|  * - `a`, `b`, `c` and `d` are command-specific header values | ||||
|  * - `payload` is the optional data that is part of the command | ||||
|  * - `payload_len` is the size of the payload | ||||
|  * | ||||
|  * returns: new instance. Will never return null. | ||||
|  * | ||||
|  * # Panics | ||||
|  * | ||||
|  * - when `payload` is null, but `payload_len` is not zero | ||||
|  * - when `payload_len` is zero, but `payload` is nonnull | ||||
|  * | ||||
|  * # Safety | ||||
|  * | ||||
|  * The caller has to make sure that: | ||||
|  * | ||||
|  * - `payload` points to a valid memory region of at least `payload_len` bytes | ||||
|  * - `payload` is not written to concurrently | ||||
|  * - the returned [SPPacket] instance is freed in some way, either by using a consuming function or | ||||
|  *   by explicitly calling [sp_packet_free]. | ||||
|  */ | ||||
| SPPacket *sp_packet_from_parts(uint16_t command_code, | ||||
|                                uint16_t a, | ||||
|                                uint16_t b, | ||||
|                                uint16_t c, | ||||
|                                uint16_t d, | ||||
|                                const uint8_t *payload, | ||||
|                                size_t payload_len); | ||||
| 
 | ||||
| /**
 | ||||
|  * Tries to load a [SPPacket] from the passed array with the specified length. | ||||
|  * | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| #include "servicepoint.h" | ||||
| 
 | ||||
| int main(void) { | ||||
|     SPConnection *connection = sp_connection_open("172.23.42.29:2342"); | ||||
|     SPConnection *connection = sp_connection_open("localhost:2342"); | ||||
|     if (connection == NULL) | ||||
|         return 1; | ||||
| 
 | ||||
|  | @ -10,9 +10,8 @@ int main(void) { | |||
|     sp_bitmap_fill(pixels, true); | ||||
| 
 | ||||
|     SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED); | ||||
|     while (sp_connection_send_command(connection, sp_command_clone(command))); | ||||
|     sp_connection_send_command(connection, command); | ||||
| 
 | ||||
|     sp_command_free(command); | ||||
|     sp_connection_free(connection); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ | |||
| //!
 | ||||
| //! prefix `sp_bitmap_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| use crate::byte_slice::SPByteSlice; | ||||
| 
 | ||||
|  | @ -43,9 +43,23 @@ pub unsafe extern "C" fn sp_bitmap_new( | |||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPBitmap> { | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::new( | ||||
|         width, height, | ||||
|     ))); | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Creates a new [SPBitmap] with a size matching the screen.
 | ||||
| ///
 | ||||
| /// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling [sp_bitmap_free].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull<SPBitmap> { | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::max_sized())); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -80,9 +94,8 @@ pub unsafe extern "C" fn sp_bitmap_load( | |||
| ) -> NonNull<SPBitmap> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPBitmap(servicepoint::Bitmap::load( | ||||
|         width, height, data, | ||||
|     ))); | ||||
|     let result = | ||||
|         Box::new(SPBitmap(servicepoint::Bitmap::load(width, height, data))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,9 +2,8 @@ | |||
| //!
 | ||||
| //! prefix `sp_bitvec_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::bitvec::prelude::{BitVec, Msb0}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A vector of bits
 | ||||
| ///
 | ||||
|  | @ -14,15 +13,15 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0}; | |||
| /// sp_bitvec_set(vec, 5, true);
 | ||||
| /// sp_bitvec_free(vec);
 | ||||
| /// ```
 | ||||
| pub struct SPBitVec(BitVec<u8, Msb0>); | ||||
| pub struct SPBitVec(servicepoint::BitVec); | ||||
| 
 | ||||
| impl From<BitVec<u8, Msb0>> for SPBitVec { | ||||
|     fn from(actual: BitVec<u8, Msb0>) -> Self { | ||||
| impl From<servicepoint::BitVec> for SPBitVec { | ||||
|     fn from(actual: servicepoint::BitVec) -> Self { | ||||
|         Self(actual) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SPBitVec> for BitVec<u8, Msb0> { | ||||
| impl From<SPBitVec> for servicepoint::BitVec { | ||||
|     fn from(value: SPBitVec) -> Self { | ||||
|         value.0 | ||||
|     } | ||||
|  | @ -54,7 +53,7 @@ impl Clone for SPBitVec { | |||
| ///   by explicitly calling `sp_bitvec_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> { | ||||
|     let result = Box::new(SPBitVec(BitVec::repeat(false, size))); | ||||
|     let result = Box::new(SPBitVec(servicepoint::BitVec::repeat(false, size))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -81,7 +80,7 @@ pub unsafe extern "C" fn sp_bitvec_load( | |||
| ) -> NonNull<SPBitVec> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPBitVec(BitVec::from_slice(data))); | ||||
|     let result = Box::new(SPBitVec(servicepoint::BitVec::from_slice(data))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,14 +3,14 @@ | |||
| //! prefix `sp_brightness_grid_`
 | ||||
| 
 | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid}; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use std::convert::Into; | ||||
| use std::intrinsics::transmute; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// see [Brightness::MIN]
 | ||||
| /// see [servicepoint::Brightness::MIN]
 | ||||
| pub const SP_BRIGHTNESS_MIN: u8 = 0; | ||||
| /// see [Brightness::MAX]
 | ||||
| /// see [servicepoint::Brightness::MAX]
 | ||||
| pub const SP_BRIGHTNESS_MAX: u8 = 11; | ||||
| /// Count of possible brightness values
 | ||||
| pub const SP_BRIGHTNESS_LEVELS: u8 = 12; | ||||
|  | @ -48,9 +48,9 @@ pub unsafe extern "C" fn sp_brightness_grid_new( | |||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     let result = Box::new(SPBrightnessGrid( | ||||
|         servicepoint::BrightnessGrid::new(width, height), | ||||
|     )); | ||||
|     let result = Box::new(SPBrightnessGrid(servicepoint::BrightnessGrid::new( | ||||
|         width, height, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -80,7 +80,7 @@ pub unsafe extern "C" fn sp_brightness_grid_load( | |||
| ) -> NonNull<SPBrightnessGrid> { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let grid = PrimitiveGrid::load(width, height, data); | ||||
|     let grid = servicepoint::ByteGrid::load(width, height, data); | ||||
|     let grid = servicepoint::BrightnessGrid::try_from(grid) | ||||
|         .expect("invalid brightness value"); | ||||
|     let result = Box::new(SPBrightnessGrid(grid)); | ||||
|  | @ -203,8 +203,8 @@ pub unsafe extern "C" fn sp_brightness_grid_set( | |||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     let brightness = servicepoint::Brightness::try_from(value) | ||||
|         .expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.set(x, y, brightness); | ||||
| } | ||||
| 
 | ||||
|  | @ -232,8 +232,8 @@ pub unsafe extern "C" fn sp_brightness_grid_fill( | |||
|     value: u8, | ||||
| ) { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     let brightness = | ||||
|         Brightness::try_from(value).expect("invalid brightness value"); | ||||
|     let brightness = servicepoint::Brightness::try_from(value) | ||||
|         .expect("invalid brightness value"); | ||||
|     (*brightness_grid).0.fill(brightness); | ||||
| } | ||||
| 
 | ||||
|  | @ -311,7 +311,7 @@ pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref( | |||
|     brightness_grid: *mut SPBrightnessGrid, | ||||
| ) -> SPByteSlice { | ||||
|     assert!(!brightness_grid.is_null()); | ||||
|     assert_eq!(core::mem::size_of::<Brightness>(), 1); | ||||
|     assert_eq!(core::mem::size_of::<servicepoint::Brightness>(), 1); | ||||
|     let data = (*brightness_grid).0.data_ref_mut(); | ||||
|     // this assumes more about the memory layout than rust guarantees. yikes!
 | ||||
|     let data: &mut [u8] = transmute(data); | ||||
|  |  | |||
							
								
								
									
										263
									
								
								crates/servicepoint_binding_c/src/char_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								crates/servicepoint_binding_c/src/char_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,263 @@ | |||
| //! C functions for interacting with [SPCharGrid]s
 | ||||
| //!
 | ||||
| //! prefix `sp_char_grid_`
 | ||||
| 
 | ||||
| use servicepoint::Grid; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A C-wrapper for grid containing UTF-8 characters.
 | ||||
| ///
 | ||||
| /// As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers.
 | ||||
| ///
 | ||||
| /// The encoding is enforced in most cases by the rust standard library
 | ||||
| /// and will panic when provided with illegal characters.
 | ||||
| ///
 | ||||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```C
 | ||||
| /// CharGrid grid = sp_char_grid_new(4, 3);
 | ||||
| /// sp_char_grid_fill(grid, '?');
 | ||||
| /// sp_char_grid_set(grid, 0, 0, '!');
 | ||||
| /// sp_char_grid_free(grid);
 | ||||
| /// ```
 | ||||
| pub struct SPCharGrid(pub(crate) servicepoint::CharGrid); | ||||
| 
 | ||||
| impl Clone for SPCharGrid { | ||||
|     fn clone(&self) -> Self { | ||||
|         SPCharGrid(self.0.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new [SPCharGrid] with the specified dimensions.
 | ||||
| ///
 | ||||
| /// returns: [SPCharGrid] initialized to 0. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_new( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     let result = | ||||
|         Box::new(SPCharGrid(servicepoint::CharGrid::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Loads a [SPCharGrid] with the specified dimensions from the provided data.
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `data` is NULL
 | ||||
| /// - when the provided `data_length` does not match `height` and `width`
 | ||||
| /// - when `data` is not valid UTF-8
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `data` points to a valid memory location of at least `data_length`
 | ||||
| ///   bytes in size.
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_load( | ||||
|     width: usize, | ||||
|     height: usize, | ||||
|     data: *const u8, | ||||
|     data_length: usize, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     assert!(data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPCharGrid( | ||||
|         servicepoint::CharGrid::load_utf8(width, height, data.to_vec()) | ||||
|             .unwrap(), | ||||
|     )); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPCharGrid].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to concurrently
 | ||||
| /// - the returned instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_char_grid_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_clone( | ||||
|     char_grid: *const SPCharGrid, | ||||
| ) -> NonNull<SPCharGrid> { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     let result = Box::new((*char_grid).clone()); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Deallocates a [SPCharGrid].
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not used concurrently or after char_grid call
 | ||||
| /// - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
 | ||||
| ///
 | ||||
| /// [SPCommand]: [crate::SPCommand]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_free(char_grid: *mut SPCharGrid) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     _ = Box::from_raw(char_grid); | ||||
| } | ||||
| 
 | ||||
| /// Gets the current value at the specified position.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to read from
 | ||||
| /// - `x` and `y`: position of the cell to read
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_get( | ||||
|     char_grid: *const SPCharGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
| ) -> u32 { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.get(x, y) as u32 | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of the specified position in the [SPCharGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to write to
 | ||||
| /// - `x` and `y`: position of the cell
 | ||||
| /// - `value`: the value to write to the cell
 | ||||
| ///
 | ||||
| /// returns: old value of the cell
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| /// - when accessing `x` or `y` out of bounds
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPBitVec]
 | ||||
| /// - `char_grid` is not written to or read from concurrently
 | ||||
| ///
 | ||||
| /// [SPBitVec]: [crate::SPBitVec]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_set( | ||||
|     char_grid: *mut SPCharGrid, | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     value: u32, | ||||
| ) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.set(x, y, char::from_u32(value).unwrap()); | ||||
| } | ||||
| 
 | ||||
| /// Sets the value of all cells in the [SPCharGrid].
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to write to
 | ||||
| /// - `value`: the value to set all cells to
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| /// - `char_grid` is not written to or read from concurrently
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_fill( | ||||
|     char_grid: *mut SPCharGrid, | ||||
|     value: u32, | ||||
| ) { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.fill(char::from_u32(value).unwrap()); | ||||
| } | ||||
| 
 | ||||
| /// Gets the width of the [SPCharGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_width( | ||||
|     char_grid: *const SPCharGrid, | ||||
| ) -> usize { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.width() | ||||
| } | ||||
| 
 | ||||
| /// Gets the height of the [SPCharGrid] instance.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `char_grid`: instance to read from
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `char_grid` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `char_grid` points to a valid [SPCharGrid]
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_char_grid_height( | ||||
|     char_grid: *const SPCharGrid, | ||||
| ) -> usize { | ||||
|     assert!(!char_grid.is_null()); | ||||
|     (*char_grid).0.height() | ||||
| } | ||||
|  | @ -4,11 +4,9 @@ | |||
| 
 | ||||
| use std::ptr::{null_mut, NonNull}; | ||||
| 
 | ||||
| use servicepoint::{Brightness, Origin}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, | ||||
|     SPPacket, | ||||
|     SPBitVec, SPBitmap, SPBrightnessGrid, SPCharGrid, SPCompressionCode, | ||||
|     SPCp437Grid, SPPacket, | ||||
| }; | ||||
| 
 | ||||
| /// A low-level display command.
 | ||||
|  | @ -164,11 +162,10 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> { | |||
| pub unsafe extern "C" fn sp_command_brightness( | ||||
|     brightness: u8, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     let brightness = | ||||
|         Brightness::try_from(brightness).expect("invalid brightness"); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::Brightness(brightness), | ||||
|     )); | ||||
|     let brightness = servicepoint::Brightness::try_from(brightness) | ||||
|         .expect("invalid brightness"); | ||||
|     let result = | ||||
|         Box::new(SPCommand(servicepoint::Command::Brightness(brightness))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -198,9 +195,10 @@ pub unsafe extern "C" fn sp_command_char_brightness( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let byte_grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::CharBrightness( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         byte_grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -237,13 +235,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::BitmapLinear( | ||||
|             offset, | ||||
|             bit_vec.into(), | ||||
|             compression.try_into().expect("invalid compression code"), | ||||
|         ), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinear( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -280,13 +276,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::BitmapLinearAnd( | ||||
|             offset, | ||||
|             bit_vec.into(), | ||||
|             compression.try_into().expect("invalid compression code"), | ||||
|         ), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -323,13 +317,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::BitmapLinearOr( | ||||
|             offset, | ||||
|             bit_vec.into(), | ||||
|             compression.try_into().expect("invalid compression code"), | ||||
|         ), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearOr( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -366,17 +358,15 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bit_vec.is_null()); | ||||
|     let bit_vec = *Box::from_raw(bit_vec); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::BitmapLinearXor( | ||||
|             offset, | ||||
|             bit_vec.into(), | ||||
|             compression.try_into().expect("invalid compression code"), | ||||
|         ), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearXor( | ||||
|         offset, | ||||
|         bit_vec.into(), | ||||
|         compression.try_into().expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Show text on the screen.
 | ||||
| /// Show codepage 437 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [SPCp437Grid] gets consumed.
 | ||||
| ///
 | ||||
|  | @ -402,9 +392,43 @@ pub unsafe extern "C" fn sp_command_cp437_data( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Cp437Data( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Show UTF-8 encoded text on the screen.
 | ||||
| ///
 | ||||
| /// The passed [SPCharGrid] gets consumed.
 | ||||
| ///
 | ||||
| /// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `grid` is null
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `grid` points to a valid instance of [SPCharGrid]
 | ||||
| /// - `grid` is not used concurrently or after this call
 | ||||
| /// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling `sp_command_free`.
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_command_utf8_data( | ||||
|     x: usize, | ||||
|     y: usize, | ||||
|     grid: *mut SPCharGrid, | ||||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!grid.is_null()); | ||||
|     let grid = *Box::from_raw(grid); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::Utf8Data( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         grid.0, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -437,15 +461,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win( | |||
| ) -> NonNull<SPCommand> { | ||||
|     assert!(!bitmap.is_null()); | ||||
|     let byte_grid = (*Box::from_raw(bitmap)).0; | ||||
|     let result = Box::new(SPCommand( | ||||
|         servicepoint::Command::BitmapLinearWin( | ||||
|             Origin::new(x, y), | ||||
|             byte_grid, | ||||
|             compression_code | ||||
|                 .try_into() | ||||
|                 .expect("invalid compression code"), | ||||
|         ), | ||||
|     )); | ||||
|     let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearWin( | ||||
|         servicepoint::Origin::new(x, y), | ||||
|         byte_grid, | ||||
|         compression_code | ||||
|             .try_into() | ||||
|             .expect("invalid compression code"), | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ | |||
| //!
 | ||||
| //! prefix `sp_cp437_grid_`
 | ||||
| 
 | ||||
| use std::ptr::NonNull; | ||||
| use crate::SPByteSlice; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use std::ptr::NonNull; | ||||
| 
 | ||||
| /// A C-wrapper for grid containing codepage 437 characters.
 | ||||
| ///
 | ||||
|  | @ -41,9 +41,8 @@ pub unsafe extern "C" fn sp_cp437_grid_new( | |||
|     width: usize, | ||||
|     height: usize, | ||||
| ) -> NonNull<SPCp437Grid> { | ||||
|     let result = Box::new(SPCp437Grid( | ||||
|         servicepoint::Cp437Grid::new(width, height), | ||||
|     )); | ||||
|     let result = | ||||
|         Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  | @ -73,9 +72,9 @@ pub unsafe extern "C" fn sp_cp437_grid_load( | |||
| ) -> NonNull<SPCp437Grid> { | ||||
|     assert!(data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, data_length); | ||||
|     let result = Box::new(SPCp437Grid( | ||||
|         servicepoint::Cp437Grid::load(width, height, data), | ||||
|     )); | ||||
|     let result = Box::new(SPCp437Grid(servicepoint::Cp437Grid::load( | ||||
|         width, height, data, | ||||
|     ))); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,20 +25,22 @@ | |||
| //! }
 | ||||
| //! ```
 | ||||
| 
 | ||||
| pub use crate::bitvec::*; | ||||
| pub use crate::bitmap::*; | ||||
| pub use crate::bitvec::*; | ||||
| pub use crate::brightness_grid::*; | ||||
| pub use crate::byte_slice::*; | ||||
| pub use crate::char_grid::*; | ||||
| pub use crate::command::*; | ||||
| pub use crate::connection::*; | ||||
| pub use crate::constants::*; | ||||
| pub use crate::cp437_grid::*; | ||||
| pub use crate::packet::*; | ||||
| 
 | ||||
| mod bitvec; | ||||
| mod bitmap; | ||||
| mod bitvec; | ||||
| mod brightness_grid; | ||||
| mod byte_slice; | ||||
| mod char_grid; | ||||
| mod command; | ||||
| mod connection; | ||||
| mod constants; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ use std::ptr::{null_mut, NonNull}; | |||
| use crate::SPCommand; | ||||
| 
 | ||||
| /// The raw packet
 | ||||
| pub struct SPPacket(pub(crate) servicepoint::packet::Packet); | ||||
| pub struct SPPacket(pub(crate) servicepoint::Packet); | ||||
| 
 | ||||
| /// Turns a [SPCommand] into a [SPPacket].
 | ||||
| /// The [SPCommand] gets consumed.
 | ||||
|  | @ -59,12 +59,69 @@ pub unsafe extern "C" fn sp_packet_try_load( | |||
| ) -> *mut SPPacket { | ||||
|     assert!(!data.is_null()); | ||||
|     let data = std::slice::from_raw_parts(data, length); | ||||
|     match servicepoint::packet::Packet::try_from(data) { | ||||
|     match servicepoint::Packet::try_from(data) { | ||||
|         Err(_) => null_mut(), | ||||
|         Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a raw [SPPacket] from parts.
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// - `command_code` specifies which command this packet contains
 | ||||
| /// - `a`, `b`, `c` and `d` are command-specific header values
 | ||||
| /// - `payload` is the optional data that is part of the command
 | ||||
| /// - `payload_len` is the size of the payload
 | ||||
| ///
 | ||||
| /// returns: new instance. Will never return null.
 | ||||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `payload` is null, but `payload_len` is not zero
 | ||||
| /// - when `payload_len` is zero, but `payload` is nonnull
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The caller has to make sure that:
 | ||||
| ///
 | ||||
| /// - `payload` points to a valid memory region of at least `payload_len` bytes
 | ||||
| /// - `payload` is not written to concurrently
 | ||||
| /// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
 | ||||
| ///   by explicitly calling [sp_packet_free].
 | ||||
| #[no_mangle] | ||||
| pub unsafe extern "C" fn sp_packet_from_parts( | ||||
|     command_code: u16, | ||||
|     a: u16, | ||||
|     b: u16, | ||||
|     c: u16, | ||||
|     d: u16, | ||||
|     payload: *const u8, | ||||
|     payload_len: usize, | ||||
| ) -> NonNull<SPPacket> { | ||||
|     assert_eq!(payload.is_null(), payload_len == 0); | ||||
| 
 | ||||
|     let payload = if payload.is_null() { | ||||
|         vec![] | ||||
|     } else { | ||||
|         let payload = std::slice::from_raw_parts(payload, payload_len); | ||||
|         Vec::from(payload) | ||||
|     }; | ||||
| 
 | ||||
|     let packet = servicepoint::Packet { | ||||
|         header: servicepoint::Header { | ||||
|             command_code, | ||||
|             a, | ||||
|             b, | ||||
|             c, | ||||
|             d, | ||||
|         }, | ||||
|         payload, | ||||
|     }; | ||||
|     let result = Box::new(SPPacket(packet)); | ||||
|     NonNull::from(Box::leak(result)) | ||||
| } | ||||
| 
 | ||||
| /// Clones a [SPPacket].
 | ||||
| ///
 | ||||
| /// Will never return NULL.
 | ||||
|  | @ -94,7 +151,7 @@ pub unsafe extern "C" fn sp_packet_clone( | |||
| ///
 | ||||
| /// # Panics
 | ||||
| ///
 | ||||
| /// - when `sp_packet_free` is NULL
 | ||||
| /// - when `packet` is NULL
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ uniffi = { version = "0.25.3" } | |||
| thiserror.workspace = true | ||||
| 
 | ||||
| [dependencies.servicepoint] | ||||
| version = "0.12.0" | ||||
| version = "0.13.0" | ||||
| path = "../servicepoint" | ||||
| features = ["all_compressions"] | ||||
| 
 | ||||
|  | @ -32,8 +32,8 @@ optional = true | |||
| 
 | ||||
| [dependencies.uniffi-bindgen-go] | ||||
| git = "https://github.com/NordSecurity/uniffi-bindgen-go.git" | ||||
| # tag = "0.2.1+v0.25.0" | ||||
| rev = "a77dc0462dc18d53846c758155ab4e0a42e5b240" | ||||
| # tag = "0.2.2+v0.25.0" | ||||
| rev = "ba23bab72f1a9bcc39ce81924d3d9265598e017c" | ||||
| optional = true | ||||
| 
 | ||||
| [lints] | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| 
 | ||||
|     <PropertyGroup> | ||||
|         <PackageId>ServicePoint</PackageId> | ||||
|         <Version>0.12.0</Version> | ||||
|         <Version>0.13.0</Version> | ||||
|         <Authors>Repository Authors</Authors> | ||||
|         <Company>None</Company> | ||||
|         <Product>ServicePoint</Product> | ||||
|  |  | |||
|  | @ -681,6 +681,10 @@ static class _UniFFILib { | |||
|     public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset(ref RustCallStatus _uniffi_out_err | ||||
|     ); | ||||
| 
 | ||||
|     [DllImport("servicepoint_binding_uniffi")] | ||||
|     public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data(ulong @offsetX,ulong @offsetY,CharGridSafeHandle @grid,ref RustCallStatus _uniffi_out_err | ||||
|     ); | ||||
| 
 | ||||
|     [DllImport("servicepoint_binding_uniffi")] | ||||
|     public static extern sbyte uniffi_servicepoint_binding_uniffi_fn_method_command_equals(CommandSafeHandle @ptr,CommandSafeHandle @other,ref RustCallStatus _uniffi_out_err | ||||
|     ); | ||||
|  | @ -1255,6 +1259,10 @@ static class _UniFFILib { | |||
|     public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset( | ||||
|     ); | ||||
| 
 | ||||
|     [DllImport("servicepoint_binding_uniffi")] | ||||
|     public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data( | ||||
|     ); | ||||
| 
 | ||||
|     [DllImport("servicepoint_binding_uniffi")] | ||||
|     public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new( | ||||
|     ); | ||||
|  | @ -1697,6 +1705,12 @@ static class _UniFFILib { | |||
|                 throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset` checksum `62130`, library returned `{checksum}`"); | ||||
|             } | ||||
|         } | ||||
|         { | ||||
|             var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data(); | ||||
|             if (checksum != 2263) { | ||||
|                 throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data` checksum `2263`, library returned `{checksum}`"); | ||||
|             } | ||||
|         } | ||||
|         { | ||||
|             var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new(); | ||||
|             if (checksum != 30445) { | ||||
|  | @ -2671,6 +2685,13 @@ public class Command: FFIObject<CommandSafeHandle>, ICommand { | |||
| )); | ||||
|     } | ||||
|      | ||||
|     public static Command Utf8Data(ulong @offsetX, ulong @offsetY, CharGrid @grid) { | ||||
|         return new Command( | ||||
|     _UniffiHelpers.RustCall( (ref RustCallStatus _status) => | ||||
|     _UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data(FfiConverterUInt64.INSTANCE.Lower(@offsetX), FfiConverterUInt64.INSTANCE.Lower(@offsetY), FfiConverterTypeCharGrid.INSTANCE.Lower(@grid), ref _status) | ||||
| )); | ||||
|     } | ||||
|      | ||||
|      | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -918,6 +918,9 @@ module UniFFILib | |||
|   attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset, | ||||
|     [RustCallStatus.by_ref], | ||||
|     :pointer | ||||
|   attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data, | ||||
|     [:uint64, :uint64, :pointer, RustCallStatus.by_ref], | ||||
|     :pointer | ||||
|   attach_function :uniffi_servicepoint_binding_uniffi_fn_method_command_equals, | ||||
|     [:pointer, :pointer, RustCallStatus.by_ref], | ||||
|     :int8 | ||||
|  | @ -1188,6 +1191,9 @@ module UniFFILib | |||
|   attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset, | ||||
|     [RustCallStatus.by_ref], | ||||
|     :uint16 | ||||
|   attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data, | ||||
|     [RustCallStatus.by_ref], | ||||
|     :uint16 | ||||
|   attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new, | ||||
|     [RustCallStatus.by_ref], | ||||
|     :uint16 | ||||
|  | @ -1817,6 +1823,15 @@ end | |||
|     # and just create a new instance with the required pointer. | ||||
|     return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,)) | ||||
|   end | ||||
|   def self.utf8_data(offset_x, offset_y, grid) | ||||
|         offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64) | ||||
|         offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64) | ||||
|         grid = grid | ||||
|     # Call the (fallible) function before creating any half-baked object instances. | ||||
|     # Lightly yucky way to bypass the usual "initialize" logic | ||||
|     # and just create a new instance with the required pointer. | ||||
|     return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data,offset_x,offset_y,(CharGrid._uniffi_lower grid))) | ||||
|   end | ||||
|    | ||||
| 
 | ||||
|   def equals(other) | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| Gem::Specification.new do |s| | ||||
|   s.name        = "servicepoint" | ||||
|   s.version     = "0.12.0" | ||||
|   s.version     = "0.13.0" | ||||
|   s.summary     = "" | ||||
|   s.description = "" | ||||
|   s.authors     = ["kaesaecracker"] | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| use servicepoint::{Grid, SeriesError}; | ||||
| use crate::cp437_grid::Cp437Grid; | ||||
| use servicepoint::{Grid, SetValueSeriesError}; | ||||
| use std::convert::Into; | ||||
| use std::sync::{Arc, RwLock}; | ||||
| use crate::cp437_grid::Cp437Grid; | ||||
| 
 | ||||
| #[derive(uniffi::Object)] | ||||
| pub struct CharGrid { | ||||
|  | @ -107,7 +107,10 @@ impl CharGrid { | |||
|             .unwrap() | ||||
|             .get_row(y as usize) | ||||
|             .map(String::from_iter) | ||||
|             .ok_or(CharGridError::OutOfBounds {index: y, size: self.height()}) | ||||
|             .ok_or(CharGridError::OutOfBounds { | ||||
|                 index: y, | ||||
|                 size: self.height(), | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_col(&self, x: u64) -> Result<String, CharGridError> { | ||||
|  | @ -116,11 +119,16 @@ impl CharGrid { | |||
|             .unwrap() | ||||
|             .get_col(x as usize) | ||||
|             .map(String::from_iter) | ||||
|             .ok_or(CharGridError::OutOfBounds {index: x, size: self.width()}) | ||||
|             .ok_or(CharGridError::OutOfBounds { | ||||
|                 index: x, | ||||
|                 size: self.width(), | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn to_cp437(&self) -> Arc<Cp437Grid> { | ||||
|         Cp437Grid::internal_new(servicepoint::Cp437Grid::from(&*self.actual.read().unwrap())) | ||||
|         Cp437Grid::internal_new(servicepoint::Cp437Grid::from( | ||||
|             &*self.actual.read().unwrap(), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -133,9 +141,7 @@ impl CharGrid { | |||
| 
 | ||||
|     fn str_to_char(value: String) -> Result<char, CharGridError> { | ||||
|         if value.len() != 1 { | ||||
|             return Err(CharGridError::StringNotOneChar { | ||||
|                 value, | ||||
|             }); | ||||
|             return Err(CharGridError::StringNotOneChar { value }); | ||||
|         } | ||||
| 
 | ||||
|         let value = value.chars().nth(0).unwrap(); | ||||
|  | @ -143,16 +149,16 @@ impl CharGrid { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SeriesError> for CharGridError { | ||||
|     fn from(e: SeriesError) -> Self { | ||||
| impl From<SetValueSeriesError> for CharGridError { | ||||
|     fn from(e: SetValueSeriesError) -> Self { | ||||
|         match e { | ||||
|             SeriesError::OutOfBounds { index, size } => { | ||||
|             SetValueSeriesError::OutOfBounds { index, size } => { | ||||
|                 CharGridError::OutOfBounds { | ||||
|                     index: index as u64, | ||||
|                     size: size as u64, | ||||
|                 } | ||||
|             } | ||||
|             SeriesError::InvalidLength { actual, expected } => { | ||||
|             SetValueSeriesError::InvalidLength { actual, expected } => { | ||||
|                 CharGridError::InvalidSeriesLength { | ||||
|                     actual: actual as u64, | ||||
|                     expected: expected as u64, | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| use crate::bitmap::Bitmap; | ||||
| use crate::bitvec::BitVec; | ||||
| use crate::brightness_grid::BrightnessGrid; | ||||
| use crate::char_grid::CharGrid; | ||||
| use crate::compression_code::CompressionCode; | ||||
| use crate::cp437_grid::Cp437Grid; | ||||
| use crate::errors::ServicePointError; | ||||
|  | @ -151,6 +152,18 @@ impl Command { | |||
|         Self::internal_new(actual) | ||||
|     } | ||||
| 
 | ||||
|     #[uniffi::constructor] | ||||
|     pub fn utf8_data( | ||||
|         offset_x: u64, | ||||
|         offset_y: u64, | ||||
|         grid: &Arc<CharGrid>, | ||||
|     ) -> Arc<Self> { | ||||
|         let origin = Origin::new(offset_x as usize, offset_y as usize); | ||||
|         let grid = grid.actual.read().unwrap().clone(); | ||||
|         let actual = servicepoint::Command::Utf8Data(origin, grid); | ||||
|         Self::internal_new(actual) | ||||
|     } | ||||
| 
 | ||||
|     #[uniffi::constructor] | ||||
|     pub fn clone(other: &Arc<Self>) -> Arc<Self> { | ||||
|         Self::internal_new(other.actual.clone()) | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record)] | ||||
| #[derive(
 | ||||
|     Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record, | ||||
| )] | ||||
| pub struct Constants { | ||||
|     pub tile_size: u64, | ||||
|     pub tile_width: u64, | ||||
|  | @ -10,7 +12,7 @@ pub struct Constants { | |||
| 
 | ||||
| #[uniffi::export] | ||||
| fn get_constants() -> Constants { | ||||
| Constants { | ||||
|     Constants { | ||||
|         tile_size: servicepoint::TILE_SIZE as u64, | ||||
|         tile_width: servicepoint::TILE_WIDTH as u64, | ||||
|         tile_height: servicepoint::TILE_HEIGHT as u64, | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| use crate::char_grid::CharGrid; | ||||
| use servicepoint::{DataRef, Grid}; | ||||
| use std::sync::{Arc, RwLock}; | ||||
| use crate::char_grid::CharGrid; | ||||
| 
 | ||||
| #[derive(uniffi::Object)] | ||||
| pub struct Cp437Grid { | ||||
|  | @ -72,6 +72,8 @@ impl Cp437Grid { | |||
|     } | ||||
| 
 | ||||
|     pub fn to_utf8(&self) -> Arc<CharGrid> { | ||||
|         CharGrid::internal_new(servicepoint::CharGrid::from(&*self.actual.read().unwrap())) | ||||
|         CharGrid::internal_new(servicepoint::CharGrid::from( | ||||
|             &*self.actual.read().unwrap(), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,6 @@ mod char_grid; | |||
| mod command; | ||||
| mod compression_code; | ||||
| mod connection; | ||||
| mod constants; | ||||
| mod cp437_grid; | ||||
| mod errors; | ||||
| mod constants; | ||||
|  |  | |||
							
								
								
									
										14
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								flake.lock
									
										
									
										generated
									
									
									
								
							|  | @ -7,11 +7,11 @@ | |||
|         ] | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1721727458, | ||||
|         "narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=", | ||||
|         "lastModified": 1736429655, | ||||
|         "narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=", | ||||
|         "owner": "nix-community", | ||||
|         "repo": "naersk", | ||||
|         "rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11", | ||||
|         "rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|  | @ -22,16 +22,16 @@ | |||
|     }, | ||||
|     "nixpkgs": { | ||||
|       "locked": { | ||||
|         "lastModified": 1730963269, | ||||
|         "narHash": "sha256-rz30HrFYCHiWEBCKHMffHbMdWJ35hEkcRVU0h7ms3x0=", | ||||
|         "lastModified": 1736549401, | ||||
|         "narHash": "sha256-ibkQrMHxF/7TqAYcQE+tOnIsSEzXmMegzyBWza6uHKM=", | ||||
|         "owner": "nixos", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "83fb6c028368e465cd19bb127b86f971a5e41ebc", | ||||
|         "rev": "1dab772dd4a68a7bba5d9460685547ff8e17d899", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "nixos", | ||||
|         "ref": "nixos-24.05", | ||||
|         "ref": "nixos-24.11", | ||||
|         "repo": "nixpkgs", | ||||
|         "type": "github" | ||||
|       } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
|   description = "Flake for servicepoint-simulator"; | ||||
| 
 | ||||
|   inputs = { | ||||
|     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; | ||||
|     nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; | ||||
|     naersk = { | ||||
|       url = "github:nix-community/naersk"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|  | @ -51,7 +51,7 @@ | |||
|           ]; | ||||
|           buildInputs = with pkgs; [ | ||||
|             xe | ||||
|             lzma | ||||
|             xz | ||||
|           ]; | ||||
|           makeExample = | ||||
|             { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter