Merge pull request #1 from kaesaecracker/restructure

Restructure, build.rs, move c_api
This commit is contained in:
Vinzenz Schroeter 2024-05-26 15:17:38 +02:00 committed by GitHub
commit 333cf954f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 1643 additions and 1762 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ target
out out
bin bin
obj obj
.direnv

379
Cargo.lock generated
View file

@ -17,15 +17,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "announce"
version = "0.1.0"
dependencies = [
"clap",
"env_logger",
"servicepoint2",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.14" version = "0.6.14"
@ -75,6 +66,35 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -97,10 +117,29 @@ dependencies = [
] ]
[[package]] [[package]]
name = "cc" name = "cbindgen"
version = "1.0.97" version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
dependencies = [
"clap 3.2.25",
"heck 0.4.1",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 1.0.109",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -113,6 +152,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"atty",
"bitflags 1.3.2",
"clap_lex 0.2.4",
"indexmap",
"strsim 0.10.0",
"termcolor",
"textwrap",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.4" version = "4.5.4"
@ -131,8 +185,8 @@ checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex 0.7.0",
"strsim", "strsim 0.11.1",
] ]
[[package]] [[package]]
@ -141,10 +195,19 @@ version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [ dependencies = [
"heck", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.63", "syn 2.0.66",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
] ]
[[package]] [[package]]
@ -161,9 +224,9 @@ checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.0" version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -179,27 +242,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "env_filter" name = "errno"
version = "0.1.0" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [ dependencies = [
"log", "libc",
"regex", "windows-sys",
] ]
[[package]] [[package]]
name = "env_logger" name = "fastrand"
version = "0.11.3" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
@ -211,16 +267,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "game_of_life"
version = "0.1.0"
dependencies = [
"clap",
"env_logger",
"rand",
"servicepoint2",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -232,6 +278,18 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@ -239,10 +297,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "humantime" name = "hermit-abi"
version = "2.1.0" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
@ -250,6 +321,12 @@ version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.31" version = "0.1.31"
@ -259,11 +336,25 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "lang_c"
version = "0.1.0"
dependencies = [
"cc",
"servicepoint_binding_c",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.154" version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "log" name = "log"
@ -279,28 +370,25 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "moving_line"
version = "0.1.0"
dependencies = [
"clap",
"env_logger",
"servicepoint2",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "os_str_bytes"
version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.30" version = "0.3.30"
@ -315,9 +403,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.82" version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -361,16 +449,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "random_brightness"
version = "0.1.0"
dependencies = [
"clap",
"env_logger",
"rand",
"servicepoint2",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.4" version = "1.10.4"
@ -411,24 +489,91 @@ dependencies = [
] ]
[[package]] [[package]]
name = "servicepoint-binding-cs" name = "rustix"
version = "0.4.2" version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [ dependencies = [
"csbindgen", "bitflags 2.5.0",
"servicepoint2", "errno",
"libc",
"linux-raw-sys",
"windows-sys",
] ]
[[package]] [[package]]
name = "servicepoint2" name = "ryu"
version = "0.4.2" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "serde_json"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "servicepoint"
version = "0.5.0"
dependencies = [ dependencies = [
"bzip2", "bzip2",
"clap 4.5.4",
"flate2", "flate2",
"log", "log",
"rand",
"rust-lzma", "rust-lzma",
"zstd", "zstd",
] ]
[[package]]
name = "servicepoint_binding_c"
version = "0.5.0"
dependencies = [
"cbindgen",
"servicepoint",
]
[[package]]
name = "servicepoint_binding_cs"
version = "0.5.0"
dependencies = [
"csbindgen",
"servicepoint",
"servicepoint_binding_c",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -448,15 +593,51 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.63" version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
@ -481,6 +662,37 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"windows-sys",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -554,15 +766,6 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "wiping_clear"
version = "0.1.0"
dependencies = [
"clap",
"env_logger",
"servicepoint2",
]
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.1" version = "0.13.1"

View file

@ -1,11 +1,11 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"servicepoint2", "crates/servicepoint",
"servicepoint2-binding-cs", "crates/servicepoint_binding_c",
"examples/announce", "crates/servicepoint_binding_cs",
"examples/game_of_life", "crates/servicepoint_binding_c/examples/lang_c"
"examples/moving_line",
"examples/wiping_clear",
"examples/random_brightness"
] ]
[workspace.package]
version = "0.5.0"

203
README.md
View file

@ -1,160 +1,73 @@
# servicepoint2 # servicepoint
[![crates.io](https://img.shields.io/crates/v/servicepoint2.svg)](https://crates.io/crates/servicepoint2)
[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint2)](https://crates.io/crates/servicepoint2)
[![docs.rs](https://img.shields.io/docsrs/servicepoint2)](https://docs.rs/servicepoint2/latest/servicepoint2/)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint2)](./LICENSE)
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
Display" or "Airport Display". Display" or "Airport Display".
This repository contains a library for parsing, encoding and sending packets to this display via UDP. This repository contains a library for parsing, encoding and sending packets to this display via UDP in multiple
programming languages.
## Note on stability Take a look at the contained crates for language specific information:
This library is still in early development. | Language | Readme |
You can absolutely use it, and it works, but expect minor breaking changes with every version bump. |----------|---------------------------------------------------------------------|
Please specify the full version including patch in your Cargo.toml until 1.0 is released. | Rust | [servicepoint](crates/servicepoint/README.md) |
| C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) |
Expect bugs and/or missing features in the language bindings for now. If you need something specific, open an issue or a pull request. | C# / F# | [servicepoint_binding_cs](crates/servicepoint_binding_cs/README.md) |
## Rust
This is where the library works the best.
Any API usage accepted by the compiler in a safe context is either safe or buggy (issues welcome)
```bash
cargo add servicepoint2
```
```rust
fn main() {
// establish connection
let connection = servicepoint2::Connection::open("172.23.42.29:2342")
.expect("connection failed");
// clear screen content
connection.send(servicepoint2::Command::Clear.into())
.expect("send failed");
}
```
More examples are available in the repository folder and in the [Projects using the library]() section
## C / C++
The lowest common denominator. Things to keep in mind:
- This is a chainsaw. You will cut your leg.
- function names are: `sp2_` \<struct_name\> \<rust name\>.
- Use the rust documentation.
- Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free.
- Option<T> or Result<T, E> turn into nullable return values - check for NULL!
- There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable.
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
```c++
#include <stdio.h>
#include "servicepoint2.h"
int main(void) {
sp2_Connection *connection = sp2_connection_open("localhost:2342");
if (connection == NULL)
return 1;
sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT);
sp2_pixel_grid_fill(pixels, true);
sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
sp2_Packet *packet = sp2_packet_from_command(command);
if (!sp2_connection_send(connection, packet))
return 1;
sp2_connection_dealloc(connection);
return 0;
}
```
## C# / F#
Uses C bindings internally to provide a similar API to rust. Things to keep in mind:
- You will get a `NullPointerException` when trying to call a method where the native instance has been consumed already (e.g. when `Send`ing a command instance twice). Send a clone instead of the original if you want to keep using it.
- Some lower-level APIs _will_ panic in native code when used improperly.
Example: manipulating the `Span<byte>` of an object after freeing the instance.
- C# specifics are documented in the library. Use the rust documentation for everything else. Naming and semantics are the same apart from CamelCase instead of kebab_case.
- You will only get rust backtraces in debug builds of the native code.
- F# is not explicitly tested. If there are usability or functionality problems, please open an issue.
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
```csharp
using ServicePoint2;
// using statement calls Dispose() on scope exit, which frees unmanaged instances
using var connection = Connection.Open("127.0.0.1:2342");
using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight);
while (true)
{
pixels.Fill(true);
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
Thread.Sleep(5000);
pixels.Fill(false);
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
Thread.Sleep(5000);
}
```
### Installation
NuGet packages are not a good way to distribute native projects ([relevant issue](https://github.com/dotnet/sdk/issues/33845)).
Because of that, there is no NuGet package you can use directly.
Including this repository as a submodule and building from source is the recommended way of using the library.
```bash
git submodule add https://github.com/kaesaecracker/servicepoint.git
git commit -m "add servicepoint submodule"
```
You can now reference `servicepoint2-bindings-cs/src/ServicePoint2.csproj` in your project.
The rust library will automatically be built.
Please provide more information in the form of an issue if you need the build to copy a different library file for your platform.
### Installation
Copy the header to your project and compile against.
You have the choice of linking statically (recommended) or dynamically.
- The C example shows how to link statically against the `staticlib` variant.
- When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI guarantees yet.
## Features
This library has multiple compression libraries as optional dependencies.
If you do not need compression/decompression support you can disable those features.
In the likely case you only need one of them, you can include that one specifically.
```toml
[dependencies.servicepoint2]
git = "https://github.com/kaesaecracker/servicepoint.git"
default-features = false
features = ["compression-bz"]
```
Language bindings will not know which features are available and may fail at runtime.
It is recommended to include all features for builds used outside of rust.
## Projects using the library ## Projects using the library
- screen simulator (rust): https://github.com/kaesaecracker/pixel-receiver-rs - screen simulator (rust): [servicepoint-simulator](https://github.com/kaesaecracker/servicepoint-simulator)
- tanks game (C#): https://github.com/kaesaecracker/cccb-tanks-cs - tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
To add yourself to the list, open a pull request. To add yourself to the list, open a pull request.
## Where is servicepoint1? ## About the display
This library is a spiritual mix of a not-yet-working rust library called `servicepoint` and a bunch of working but also unfinished C# code. Because most of the API concept and a bunch of code is taken from the rust library, the result is called `servicepoint2`. - Resolution: 352x160=56,320 pixels
- Pixels are grouped into 44x20=880 tiles (8x8=64 pixels each)
- Smallest addressable unit: row of pixels inside of a tile (8 pixels = 1 byte)
- The brightness can only be set per tile
- Screen content can be changed using a simple UDP protocol
- Between each row of tiles, there is a gap of around 4 pixels size. This gap changes the aspect ratio of the display.
### Binary format
A UDP package sent to the display has a header size of 10 bytes.
Each header value has a size of two bytes (unsigned 16 bit integer).
Depending on the command, there can be a payload following the header.
The commands are implemented in DisplayCommands.
To change screen contents, these commands are the most relevant:
1. Clear screen
- command: `0x0002`
- (rest does not matter)
2. Send CP437 data: render specified text into rectangular region
- command: `0x0003`
- top left tile x
- top left tile y
- width in tiles
- height in tiles
- payload: (width in tiles * height in tiles) bytes
- 1 byte = 1 character
- each character is rendered into one tile (mono-spaced)
- characters are encoded using code page 437
3. Send bitmap window: set pixel states for a rectangular region
- command: `0x0013`
- top left tile x
- top left _pixel_ y
- width in tiles
- height in _pixels_
- payload: (width in tiles * height in pixels) bytes
- network byte order
- 1 bit = 1 pixel
There are other commands implemented as well, e.g. for changing the brightness.
## What happened to servicepoint2?
After `servicepoint2` has been merged into `servicepoint`, `servicepoint2` will not continue to get any updates.
## Contributing ## Contributing

View file

@ -1,16 +1,16 @@
[package] [package]
name = "servicepoint2" name = "servicepoint"
version = "0.4.2" version.workspace = true
publish = true publish = true
edition = "2021" edition = "2021"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
description = "A rust library for the CCCB Service Point Display." description = "A rust library for the CCCB Service Point Display."
homepage = "https://docs.rs/crate/servicepoint2" homepage = "https://docs.rs/crate/servicepoint"
repository = "https://github.com/kaesaecracker/servicepoint" repository = "https://github.com/cccb/servicepoint"
readme = "../README.md" readme = "README.md"
[lib] [lib]
crate-type = ["staticlib", "rlib", "cdylib"] crate-type = ["rlib"]
[dependencies] [dependencies]
log = "0.4" log = "0.4"
@ -26,4 +26,8 @@ compression_bzip2 = ["dep:bzip2"]
compression_lzma = ["dep:rust-lzma"] compression_lzma = ["dep:rust-lzma"]
compression_zstd = ["dep:zstd"] compression_zstd = ["dep:zstd"]
all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"]
c_api = []
[dev-dependencies]
# for examples
clap = { version = "4.5", features = ["derive"] }
rand = "0.8"

View file

@ -0,0 +1,54 @@
# servicepoint
[![crates.io](https://img.shields.io/crates/v/servicepoint.svg)](https://crates.io/crates/servicepoint)
[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint)](https://crates.io/crates/servicepoint)
[![docs.rs](https://img.shields.io/docsrs/servicepoint)](https://docs.rs/servicepoint/latest/servicepoint/)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint)](../../LICENSE)
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
Display" or "Airport Display".
This crate contains a library for parsing, encoding and sending packets to this display via UDP.
## Examples
```rust
fn main() {
// establish connection
let connection = servicepoint::Connection::open("172.23.42.29:2342")
.expect("connection failed");
// clear screen content
connection.send(servicepoint::Command::Clear.into())
.expect("send failed");
}
```
More examples are available in the crate.
Execute `cargo run --example` for a list of available examples and `cargo run --example <name>` to run one.
## Note on stability
This library is still in early development.
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
## Installation
```bash
cargo add servicepoint
```
## Features
This library has multiple compression libraries as optional dependencies.
If you do not need compression/decompression support you can disable those features.
In the likely case you only need one of them, you can include that one specifically.
```toml
[dependencies]
servicepoint = { version = "0.5.0", default-features = false, features = ["compression-bz"] }
```
## Everything else
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.

View file

@ -1,6 +1,6 @@
use clap::Parser; use clap::Parser;
use servicepoint2::{ByteGrid, Command, Connection, Grid, Origin}; use servicepoint::{ByteGrid, Command, Connection, Grid, Origin};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -14,10 +14,11 @@ struct Cli {
/// example: `cargo run -- --text "Hallo, /// example: `cargo run -- --text "Hallo,
/// CCCB"` /// CCCB"`
fn main() { fn main() {
env_logger::init(); let mut cli = Cli::parse();
let cli = Cli::parse(); if cli.text.is_empty() {
cli.text.push("Hello, CCCB!".to_string());
}
let connection = Connection::open(&cli.destination).unwrap(); let connection = Connection::open(&cli.destination).unwrap();
if cli.clear { if cli.clear {

View file

@ -3,7 +3,7 @@ use std::thread;
use clap::Parser; use clap::Parser;
use rand::{distributions, Rng}; use rand::{distributions, Rng};
use servicepoint2::*; use servicepoint::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -14,7 +14,6 @@ struct Cli {
} }
fn main() { fn main() {
env_logger::init();
let cli = Cli::parse(); let cli = Cli::parse();
let connection = Connection::open(&cli.destination).unwrap(); let connection = Connection::open(&cli.destination).unwrap();
@ -22,13 +21,11 @@ fn main() {
loop { loop {
connection connection
.send( .send(Command::BitmapLinearWin(
Command::BitmapLinearWin(
Origin(0, 0), Origin(0, 0),
field.clone(), field.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
) ))
)
.expect("could not send"); .expect("could not send");
thread::sleep(FRAME_PACING); thread::sleep(FRAME_PACING);
field = iteration(field); field = iteration(field);

View file

@ -2,7 +2,7 @@ use std::thread;
use clap::Parser; use clap::Parser;
use servicepoint2::*; use servicepoint::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -11,8 +11,6 @@ struct Cli {
} }
fn main() { fn main() {
env_logger::init();
let connection = Connection::open(Cli::parse().destination).unwrap(); let connection = Connection::open(Cli::parse().destination).unwrap();
let mut pixels = PixelGrid::max_sized(); let mut pixels = PixelGrid::max_sized();
@ -23,13 +21,11 @@ fn main() {
pixels.set((y + x_offset) % PIXEL_WIDTH, y, true); pixels.set((y + x_offset) % PIXEL_WIDTH, y, true);
} }
connection connection
.send( .send(Command::BitmapLinearWin(
Command::BitmapLinearWin(
Origin(0, 0), Origin(0, 0),
pixels.clone(), pixels.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
) ))
)
.unwrap(); .unwrap();
thread::sleep(FRAME_PACING); thread::sleep(FRAME_PACING);
} }

View file

@ -3,8 +3,8 @@ use std::time::Duration;
use clap::Parser; use clap::Parser;
use rand::Rng; use rand::Rng;
use servicepoint2::Command::{BitmapLinearWin, Brightness, CharBrightness}; use servicepoint::Command::{BitmapLinearWin, Brightness, CharBrightness};
use servicepoint2::*; use servicepoint::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -17,7 +17,6 @@ struct Cli {
} }
fn main() { fn main() {
env_logger::init();
let cli = Cli::parse(); let cli = Cli::parse();
let connection = Connection::open(cli.destination).unwrap(); let connection = Connection::open(cli.destination).unwrap();
@ -55,9 +54,7 @@ fn main() {
} }
} }
connection connection.send(CharBrightness(origin, luma)).unwrap();
.send(CharBrightness(origin, luma))
.unwrap();
std::thread::sleep(wait_duration); std::thread::sleep(wait_duration);
} }
} }

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use clap::Parser; use clap::Parser;
use servicepoint2::*; use servicepoint::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -14,7 +14,6 @@ struct Cli {
} }
fn main() { fn main() {
env_logger::init();
let cli = Cli::parse(); let cli = Cli::parse();
let sleep_duration = Duration::max( let sleep_duration = Duration::max(
@ -37,9 +36,7 @@ fn main() {
let bit_vec = BitVec::from(&*pixel_data); let bit_vec = BitVec::from(&*pixel_data);
connection connection
.send( .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma))
Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma)
)
.unwrap(); .unwrap();
thread::sleep(sleep_duration); thread::sleep(sleep_duration);
} }

View file

@ -72,7 +72,7 @@ impl BitVec {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use servicepoint2::BitVec; /// use servicepoint::BitVec;
/// let mut vec = BitVec::new(8); /// let mut vec = BitVec::new(8);
/// vec.fill(true); /// vec.fill(true);
/// ``` /// ```
@ -135,103 +135,6 @@ impl From<&[u8]> for BitVec {
} }
} }
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::{BitVec, CByteSlice, DataRef};
/// Creates a new `BitVec` instance.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_new(size: usize) -> *mut BitVec {
Box::into_raw(Box::new(BitVec::new(size)))
}
/// Loads a `BitVec` from the provided data.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_load(
data: *const u8,
data_length: usize,
) -> *mut BitVec {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(BitVec::from(data)))
}
/// Clones a `BitVec`.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_clone(
this: *const BitVec,
) -> *mut BitVec {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `BitVec`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_dealloc(this: *mut BitVec) {
_ = Box::from_raw(this);
}
/// Gets the value of a bit from the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_get(
this: *const BitVec,
index: usize,
) -> bool {
(*this).get(index)
}
/// Sets the value of a bit in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_set(
this: *mut BitVec,
index: usize,
value: bool,
) -> bool {
(*this).set(index, value)
}
/// Sets the value of all bits in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_fill(this: *mut BitVec, value: bool) {
(*this).fill(value)
}
/// Gets the length of the `BitVec` in bits.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_len(this: *const BitVec) -> usize {
(*this).len()
}
/// Returns true if length is 0.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_is_empty(this: *const BitVec) -> bool {
(*this).is_empty()
}
/// Gets an unsafe reference to the data of the `BitVec` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `BitVec`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_bit_vec_unsafe_data_ref(
this: *mut BitVec,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{BitVec, DataRef}; use crate::{BitVec, DataRef};

View file

@ -108,119 +108,6 @@ impl From<ByteGrid> for Vec<u8> {
} }
} }
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::data_ref::DataRef;
use crate::grid::Grid;
use crate::{ByteGrid, CByteSlice};
/// Creates a new `ByteGrid` instance.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_new(
width: usize,
height: usize,
) -> *mut ByteGrid {
Box::into_raw(Box::new(ByteGrid::new(width, height)))
}
/// Loads a `ByteGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut ByteGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(ByteGrid::load(width, height, data)))
}
/// Clones a `ByteGrid`.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_clone(
this: *const ByteGrid,
) -> *mut ByteGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `ByteGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_dealloc(this: *mut ByteGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_get(
this: *const ByteGrid,
x: usize,
y: usize,
) -> u8 {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_set(
this: *mut ByteGrid,
x: usize,
y: usize,
value: u8,
) {
(*this).set(x, y, value);
}
/// Fills the whole `ByteGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_fill(
this: *mut ByteGrid,
value: u8,
) {
(*this).fill(value);
}
/// Gets the width in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_width(
this: *const ByteGrid,
) -> usize {
(*this).width
}
/// Gets the height in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_height(
this: *const ByteGrid,
) -> usize {
(*this).height
}
/// Gets an unsafe reference to the data of the `ByteGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `ByteGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_byte_grid_unsafe_data_ref(
this: *mut ByteGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ByteGrid, DataRef, Grid}; use crate::{ByteGrid, DataRef, Grid};

View file

@ -391,179 +391,6 @@ impl Command {
} }
} }
#[cfg(feature = "c_api")]
pub mod c_api {
use std::ptr::null_mut;
use crate::{
BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin,
Packet, PixelGrid,
};
/// Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process.
///
/// Returns: pointer to command or NULL
#[no_mangle]
pub unsafe extern "C" fn sp2_command_try_from_packet(
packet: *mut Packet,
) -> *mut Command {
let packet = *Box::from_raw(packet);
match Command::try_from(packet) {
Err(_) => null_mut(),
Ok(command) => Box::into_raw(Box::new(command)),
}
}
/// Clones a `Command` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_clone(
original: *const Command,
) -> *mut Command {
Box::into_raw(Box::new((*original).clone()))
}
/// Allocates a new `Command::Clear` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_clear() -> *mut Command {
Box::into_raw(Box::new(Command::Clear))
}
/// Allocates a new `Command::HardReset` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_hard_reset() -> *mut Command {
Box::into_raw(Box::new(Command::HardReset))
}
/// Allocates a new `Command::FadeOut` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_fade_out() -> *mut Command {
Box::into_raw(Box::new(Command::FadeOut))
}
/// Allocates a new `Command::Brightness` instance
#[no_mangle]
pub unsafe extern "C" fn sp2_command_brightness(
brightness: Brightness,
) -> *mut Command {
Box::into_raw(Box::new(Command::Brightness(brightness)))
}
/// Allocates a new `Command::CharBrightness` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_char_brightness(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::CharBrightness(
Origin(x, y),
byte_grid,
)))
}
/// Allocates a new `Command::BitmapLinear` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinear(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearAnd` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_and(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearAnd(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearOr` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_or(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearOr(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearXor` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_xor(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearXor(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::Cp437Data` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_cp437_data(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid)))
}
/// Allocates a new `Command::BitmapLinearWin` instance.
/// The passed `PixelGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_bitmap_linear_win(
x: usize,
y: usize,
byte_grid: *mut PixelGrid,
compression_code: CompressionCode,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::BitmapLinearWin(
Origin(x, y),
byte_grid,
compression_code,
)))
}
/// Deallocates a `Command`. Note that connection_send does this implicitly, so you only need
/// to do this if you use the library for parsing commands.
#[no_mangle]
pub unsafe extern "C" fn sp2_command_dealloc(ptr: *mut Command) {
_ = Box::from_raw(ptr);
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::command::TryFromPacketError; use crate::command::TryFromPacketError;

View file

@ -10,8 +10,6 @@ pub struct Connection {
socket: UdpSocket, socket: UdpSocket,
} }
impl Connection { impl Connection {
/// Open a new UDP socket and connect to the provided host. /// Open a new UDP socket and connect to the provided host.
/// ///
@ -23,7 +21,7 @@ impl Connection {
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// let connection = servicepoint2::Connection::open("172.23.42.29:2342") /// let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// .expect("connection failed"); /// .expect("connection failed");
/// ``` /// ```
pub fn open(addr: impl ToSocketAddrs + Debug) -> std::io::Result<Self> { pub fn open(addr: impl ToSocketAddrs + Debug) -> std::io::Result<Self> {
@ -48,8 +46,8 @@ impl Connection {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use servicepoint2::{Command, CompressionCode, Grid, PixelGrid}; /// use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
/// let connection = servicepoint2::Connection::open("172.23.42.29:2342") /// let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// .expect("connection failed"); /// .expect("connection failed");
/// ///
/// // turn off all pixels /// // turn off all pixels
@ -61,10 +59,13 @@ impl Connection {
/// pixels.fill(true); /// pixels.fill(true);
/// ///
/// // send pixels to display /// // send pixels to display
/// connection.send(Command::BitmapLinearWin(servicepoint2::Origin(0, 0), pixels, CompressionCode::Uncompressed)) /// connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed))
/// .expect("send failed"); /// .expect("send failed");
/// ``` /// ```
pub fn send(&self, packet: impl Into<Packet>) -> Result<(), std::io::Error> { pub fn send(
&self,
packet: impl Into<Packet>,
) -> Result<(), std::io::Error> {
let packet = packet.into(); let packet = packet.into();
debug!("sending {packet:?}"); debug!("sending {packet:?}");
let data: Vec<u8> = packet.into(); let data: Vec<u8> = packet.into();
@ -72,46 +73,3 @@ impl Connection {
Ok(()) Ok(())
} }
} }
#[cfg(feature = "c_api")]
pub mod c_api {
use std::ffi::{c_char, CStr};
use std::ptr::null_mut;
use crate::{Connection, Packet};
/// Creates a new instance of Connection.
/// The returned instance has to be deallocated with `connection_dealloc`.
///
/// returns: NULL if connection fails or connected instance
///
/// Panics: bad string encoding
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_open(
host: *const c_char,
) -> *mut Connection {
let host = CStr::from_ptr(host).to_str().expect("Bad encoding");
let connection = match Connection::open(host) {
Err(_) => return null_mut(),
Ok(value) => value,
};
Box::into_raw(Box::new(connection))
}
/// Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_send(
connection: *const Connection,
command_ptr: *mut Packet,
) -> bool {
let packet = Box::from_raw(command_ptr);
(*connection).send(*packet).is_ok()
}
/// Closes and deallocates a connection instance
#[no_mangle]
pub unsafe extern "C" fn sp2_connection_dealloc(ptr: *mut Connection) {
_ = Box::from_raw(ptr);
}
}

View file

@ -35,7 +35,7 @@ pub trait Grid<T> {
/// # Examples /// # Examples
/// To avoid boxing, this example is using the concrete type `ByteGrid`. /// To avoid boxing, this example is using the concrete type `ByteGrid`.
/// ``` /// ```
/// use servicepoint2::{ByteGrid, Grid}; /// use servicepoint::{ByteGrid, Grid};
/// fn split(grid: ByteGrid) -> (ByteGrid, ByteGrid) { /// fn split(grid: ByteGrid) -> (ByteGrid, ByteGrid) {
/// assert!(grid.width() >= 2); /// assert!(grid.width() >= 2);
/// let split_x = grid.width() / 2; /// let split_x = grid.width() / 2;

View file

@ -2,8 +2,6 @@ use std::time::Duration;
pub use crate::bit_vec::BitVec; pub use crate::bit_vec::BitVec;
pub use crate::byte_grid::ByteGrid; pub use crate::byte_grid::ByteGrid;
#[cfg(feature = "c_api")]
pub use crate::c_slice::CByteSlice;
pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::command::{Brightness, Command, Offset, Origin};
pub use crate::compression_code::CompressionCode; pub use crate::compression_code::CompressionCode;
pub use crate::connection::Connection; pub use crate::connection::Connection;
@ -14,7 +12,6 @@ pub use crate::pixel_grid::PixelGrid;
mod bit_vec; mod bit_vec;
mod byte_grid; mod byte_grid;
mod c_slice;
mod command; mod command;
mod command_code; mod command_code;
mod compression; mod compression;

View file

@ -58,46 +58,6 @@ impl TryFrom<&[u8]> for Packet {
} }
} }
#[cfg(feature = "c_api")]
mod c_api {
use std::ptr::null_mut;
use crate::{Command, Packet};
/// Turns a `Command` into a `Packet`. The command gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_from_command(
command: *mut Command,
) -> *mut Packet {
let command = *Box::from_raw(command);
let packet = command.into();
Box::into_raw(Box::new(packet))
}
/// Tries to load a `Packet` from the passed array with the specified length.
///
/// returns: NULL in case of an error, pointer to the allocated packet otherwise
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_try_load(
data: *const u8,
length: usize,
) -> *mut Packet {
let data = std::slice::from_raw_parts(data, length);
match Packet::try_from(data) {
Err(_) => null_mut(),
Ok(packet) => Box::into_raw(Box::new(packet)),
}
}
/// Deallocates a `Packet`.
///
/// Note: do not call this if the instance has been consumed in another way, e.g. by sending it.
#[no_mangle]
pub unsafe extern "C" fn sp2_packet_dealloc(this: *mut Packet) {
_ = Box::from_raw(this)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{Header, Packet}; use crate::{Header, Packet};

View file

@ -127,118 +127,6 @@ impl From<PixelGrid> for Vec<u8> {
} }
} }
#[cfg(feature = "c_api")]
pub mod c_api {
use crate::c_slice::CByteSlice;
use crate::{DataRef, Grid, PixelGrid};
/// Creates a new `PixelGrid` instance.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_new(
width: usize,
height: usize,
) -> *mut PixelGrid {
Box::into_raw(Box::new(PixelGrid::new(width, height)))
}
/// Loads a `PixelGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut PixelGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(PixelGrid::load(width, height, data)))
}
/// Clones a `PixelGrid`.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_clone(
this: *const PixelGrid,
) -> *mut PixelGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `PixelGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_dealloc(this: *mut PixelGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_get(
this: *const PixelGrid,
x: usize,
y: usize,
) -> bool {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_set(
this: *mut PixelGrid,
x: usize,
y: usize,
value: bool,
) {
(*this).set(x, y, value);
}
/// Fills the whole `PixelGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_fill(
this: *mut PixelGrid,
value: bool,
) {
(*this).fill(value);
}
/// Gets the width in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_width(
this: *const PixelGrid,
) -> usize {
(*this).width
}
/// Gets the height in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_height(
this: *const PixelGrid,
) -> usize {
(*this).height
}
/// Gets an unsafe reference to the data of the `PixelGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `PixelGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp2_pixel_grid_unsafe_data_ref(
this: *mut PixelGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{DataRef, Grid, PixelGrid}; use crate::{DataRef, Grid, PixelGrid};

View file

@ -0,0 +1,22 @@
[package]
name = "servicepoint_binding_c"
version.workspace = true
publish = false
edition = "2021"
license = "GPL-3.0-or-later"
description = "C bindings for the servicepoint crate."
homepage = "https://docs.rs/crate/servicepoint"
repository = "https://github.com/cccb/servicepoint"
readme = "README.md"
links = "servicepoint"
[lib]
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
cbindgen = "0.26.0"
[dependencies.servicepoint]
version = "0.5.0"
path = "../servicepoint"
features = ["all_compressions"]

View file

@ -0,0 +1,63 @@
# servicepoint_binding_c
[![crates.io](https://img.shields.io/crates/v/servicepoint_binding_c.svg)](https://crates.io/crates/servicepoint)
[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint_binding_c)](https://crates.io/crates/servicepoint)
[![docs.rs](https://img.shields.io/docsrs/servicepoint_binding_c)](https://docs.rs/servicepoint/latest/servicepoint/)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint_binding_c)](../../LICENSE)
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
Display" or "Airport Display".
This crate contains C bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP.
## Examples
```c++
#include <stdio.h>
#include "servicepoint.h"
int main(void) {
sp2_Connection *connection = sp2_connection_open("localhost:2342");
if (connection == NULL)
return 1;
sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT);
sp2_pixel_grid_fill(pixels, true);
sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
sp2_Packet *packet = sp2_packet_from_command(command);
if (!sp2_connection_send(connection, packet))
return 1;
sp2_connection_dealloc(connection);
return 0;
}
```
A full example including Makefile is available as part of this crate.
## Note on stability
This library is still in early development.
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
## Installation
Copy the header to your project and compile against.
You have the choice of linking statically (recommended) or dynamically.
- The C example shows how to link statically against the `staticlib` variant.
- When linked dynamically, you have to provide the `cdylib` at runtime in the _same_ version, as there are no API/ABI guarantees yet.
## Notes on differences to rust library
- function names are: `sp2_` \<struct_name\> \<rust name\>.
- Use the rust documentation.
- Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free.
- Option<T> or Result<T, E> turn into nullable return values - check for NULL!
- There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable.
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
## Everything else
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.

View file

@ -0,0 +1,27 @@
use std::{env, fs::copy};
use cbindgen::{generate_with_config, Config};
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
println!("cargo::rerun-if-changed={crate_dir}");
let config =
Config::from_file(crate_dir.clone() + "/cbindgen.toml").unwrap();
let output_dir = env::var("OUT_DIR").unwrap();
let header_file = output_dir.clone() + "/servicepoint.h";
generate_with_config(crate_dir, config)
.unwrap()
.write_to_file(&header_file);
println!("cargo:include={output_dir}");
println!("cargo::rerun-if-env-changed=SERVICEPOINT_HEADER_OUT");
if let Ok(header_out) = env::var("SERVICEPOINT_HEADER_OUT") {
let header_copy = header_out + "/servicepoint.h";
println!("cargo:warning=Copying header to {header_copy}");
copy(header_file, &header_copy).unwrap();
println!("cargo::rerun-if-changed={header_copy}");
}
}

View file

@ -19,14 +19,18 @@ sort_by = "Name"
usize_is_size_t = true usize_is_size_t = true
[defines] [defines]
"feature = compression_zlib" = "SP2_FEATURE_compression_zlib" #"feature = compression_zlib" = "SP_FEATURE_compression_zlib"
"feature = compression_bzip2" = "SP2_FEATURE_compression_bzip2" #"feature = compression_bzip2" = "SP_FEATURE_compression_bzip2"
"feature = compression_lzma" = "SP2_FEATURE_compression_lzma" #"feature = compression_lzma" = "SP_FEATURE_compression_lzma"
"feature = compression_zstd" = "SP2_FEATURE_compression_zstd" #"feature = compression_zstd" = "SP_FEATURE_compression_zstd"
[export] [export]
prefix = "sp2_" prefix = "sp_"
[parse]
parse_deps = true
include = ["servicepoint"]
extra_bindings = ["servicepoint"]
[parse.expand] [parse.expand]
features = ["c-api"] #all_features = true
all_features = true

View file

@ -0,0 +1,14 @@
[package]
name = "lang_c"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
test = false
[build-dependencies]
cc = "1.0"
[dependencies]
servicepoint_binding_c = { path = "../.." }

View file

@ -0,0 +1,33 @@
CC := gcc
THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
REPO_ROOT := $(THIS_DIR)/../..
build: out/lang_c
clean:
rm -r out
rm include/servicepoint.h
run: out/lang_c
out/lang_c
PHONY: build clean dependencies run
out/lang_c: dependencies src/main.c
mkdir -p out || true
${CC} src/main.c \
-I include \
-L $(REPO_ROOT)/target/release \
-Wl,-Bstatic -lservicepoint_binding_c \
-Wl,-Bdynamic -llzma \
-o out/lang_c
dependencies: FORCE
mkdir -p include || true
# generate servicepoint header and binary to link against
SERVICEPOINT_HEADER_OUT=$(THIS_DIR)/include cargo build \
--manifest-path=$(REPO_ROOT)/crates/servicepoint_binding_c/Cargo.toml \
--release
FORCE: ;

View file

@ -0,0 +1,16 @@
const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE";
fn main() {
println!("cargo:rerun-if-changed=src/main.c");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed={SP_INCLUDE}");
let sp_include =
std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap();
// this builds a lib, this is only to check that the example compiles
let mut cc = cc::Build::new();
cc.file("src/main.c");
cc.include(&sp_include);
cc.compile("lang_c");
}

View file

@ -9,95 +9,87 @@
/** /**
* pixel count on whole screen * pixel count on whole screen
*/ */
#define sp2_PIXEL_COUNT (sp2_PIXEL_WIDTH * sp2_PIXEL_HEIGHT) #define sp_PIXEL_COUNT (sp_PIXEL_WIDTH * sp_PIXEL_HEIGHT)
/** /**
* screen height in pixels * screen height in pixels
*/ */
#define sp2_PIXEL_HEIGHT (sp2_TILE_HEIGHT * sp2_TILE_SIZE) #define sp_PIXEL_HEIGHT (sp_TILE_HEIGHT * sp_TILE_SIZE)
/** /**
* screen width in pixels * screen width in pixels
*/ */
#define sp2_PIXEL_WIDTH (sp2_TILE_WIDTH * sp2_TILE_SIZE) #define sp_PIXEL_WIDTH (sp_TILE_WIDTH * sp_TILE_SIZE)
/** /**
* tile count in the y-direction * tile count in the y-direction
*/ */
#define sp2_TILE_HEIGHT 20 #define sp_TILE_HEIGHT 20
/** /**
* size of a single tile in one dimension * size of a single tile in one dimension
*/ */
#define sp2_TILE_SIZE 8 #define sp_TILE_SIZE 8
/** /**
* tile count in the x-direction * tile count in the x-direction
*/ */
#define sp2_TILE_WIDTH 56 #define sp_TILE_WIDTH 56
/** /**
* Specifies the kind of compression to use. Availability depends on features. * Specifies the kind of compression to use. Availability depends on features.
*/ */
enum sp2_CompressionCode enum sp_CompressionCode
#ifdef __cplusplus #ifdef __cplusplus
: uint16_t : uint16_t
#endif // __cplusplus #endif // __cplusplus
{ {
Uncompressed = 0, Uncompressed = 0,
#if defined(SP2_FEATURE_compression_zlib)
Zlib = 26490, Zlib = 26490,
#endif
#if defined(SP2_FEATURE_compression_bzip2)
Bzip2 = 25210, Bzip2 = 25210,
#endif
#if defined(SP2_FEATURE_compression_lzma)
Lzma = 27770, Lzma = 27770,
#endif
#if defined(SP2_FEATURE_compression_zstd)
Zstd = 31347, Zstd = 31347,
#endif
}; };
#ifndef __cplusplus #ifndef __cplusplus
typedef uint16_t sp2_CompressionCode; typedef uint16_t sp_CompressionCode;
#endif // __cplusplus #endif // __cplusplus
/** /**
* A vector of bits * A vector of bits
*/ */
typedef struct sp2_BitVec sp2_BitVec; typedef struct sp_BitVec sp_BitVec;
/** /**
* A 2D grid of bytes * A 2D grid of bytes
*/ */
typedef struct sp2_ByteGrid sp2_ByteGrid; typedef struct sp_ByteGrid sp_ByteGrid;
/** /**
* A command to send to the display. * A command to send to the display.
*/ */
typedef struct sp2_Command sp2_Command; typedef struct sp_Command sp_Command;
/** /**
* A connection to the display. * A connection to the display.
*/ */
typedef struct sp2_Connection sp2_Connection; typedef struct sp_Connection sp_Connection;
/** /**
* The raw packet. Should probably not be used directly. * The raw packet. Should probably not be used directly.
*/ */
typedef struct sp2_Packet sp2_Packet; typedef struct sp_Packet sp_Packet;
/** /**
* A grid of pixels stored in packed bytes. * A grid of pixels stored in packed bytes.
*/ */
typedef struct sp2_PixelGrid sp2_PixelGrid; typedef struct sp_PixelGrid sp_PixelGrid;
/** /**
* Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. * Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
* *
* Usage of this type is inherently unsafe. * Usage of this type is inherently unsafe.
*/ */
typedef struct sp2_CByteSlice { typedef struct sp_CByteSlice {
/** /**
* The start address of the memory * The start address of the memory
*/ */
@ -106,17 +98,17 @@ typedef struct sp2_CByteSlice {
* The amount of memory in bytes * The amount of memory in bytes
*/ */
size_t length; size_t length;
} sp2_CByteSlice; } sp_CByteSlice;
/** /**
* Type alias for documenting the meaning of the u16 in enum values * Type alias for documenting the meaning of the u16 in enum values
*/ */
typedef size_t sp2_Offset; typedef size_t sp_Offset;
/** /**
* Type alias for documenting the meaning of the u16 in enum values * Type alias for documenting the meaning of the u16 in enum values
*/ */
typedef uint8_t sp2_Brightness; typedef uint8_t sp_Brightness;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -126,51 +118,51 @@ extern "C" {
* Clones a `BitVec`. * Clones a `BitVec`.
* The returned instance has to be freed with `bit_vec_dealloc`. * The returned instance has to be freed with `bit_vec_dealloc`.
*/ */
struct sp2_BitVec *sp2_bit_vec_clone(const struct sp2_BitVec *this_); struct sp_BitVec *sp_bit_vec_clone(const struct sp_BitVec *this_);
/** /**
* Deallocates a `BitVec`. * Deallocates a `BitVec`.
* *
* Note: do not call this if the grid has been consumed in another way, e.g. to create a command. * Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
*/ */
void sp2_bit_vec_dealloc(struct sp2_BitVec *this_); void sp_bit_vec_dealloc(struct sp_BitVec *this_);
/** /**
* Sets the value of all bits in the `BitVec`. * Sets the value of all bits in the `BitVec`.
*/ */
void sp2_bit_vec_fill(struct sp2_BitVec *this_, bool value); void sp_bit_vec_fill(struct sp_BitVec *this_, bool value);
/** /**
* Gets the value of a bit from the `BitVec`. * Gets the value of a bit from the `BitVec`.
*/ */
bool sp2_bit_vec_get(const struct sp2_BitVec *this_, size_t index); bool sp_bit_vec_get(const struct sp_BitVec *this_, size_t index);
/** /**
* Returns true if length is 0. * Returns true if length is 0.
*/ */
bool sp2_bit_vec_is_empty(const struct sp2_BitVec *this_); bool sp_bit_vec_is_empty(const struct sp_BitVec *this_);
/** /**
* Gets the length of the `BitVec` in bits. * Gets the length of the `BitVec` in bits.
*/ */
size_t sp2_bit_vec_len(const struct sp2_BitVec *this_); size_t sp_bit_vec_len(const struct sp_BitVec *this_);
/** /**
* Loads a `BitVec` from the provided data. * Loads a `BitVec` from the provided data.
* The returned instance has to be freed with `bit_vec_dealloc`. * The returned instance has to be freed with `bit_vec_dealloc`.
*/ */
struct sp2_BitVec *sp2_bit_vec_load(const uint8_t *data, size_t data_length); struct sp_BitVec *sp_bit_vec_load(const uint8_t *data, size_t data_length);
/** /**
* Creates a new `BitVec` instance. * Creates a new `BitVec` instance.
* The returned instance has to be freed with `bit_vec_dealloc`. * The returned instance has to be freed with `bit_vec_dealloc`.
*/ */
struct sp2_BitVec *sp2_bit_vec_new(size_t size); struct sp_BitVec *sp_bit_vec_new(size_t size);
/** /**
* Sets the value of a bit in the `BitVec`. * Sets the value of a bit in the `BitVec`.
*/ */
bool sp2_bit_vec_set(struct sp2_BitVec *this_, size_t index, bool value); bool sp_bit_vec_set(struct sp_BitVec *this_, size_t index, bool value);
/** /**
* Gets an unsafe reference to the data of the `BitVec` instance. * Gets an unsafe reference to the data of the `BitVec` instance.
@ -183,41 +175,41 @@ bool sp2_bit_vec_set(struct sp2_BitVec *this_, size_t index, bool value);
* Reading and writing concurrently to either the original instance or the returned data will * Reading and writing concurrently to either the original instance or the returned data will
* result in undefined behavior. * result in undefined behavior.
*/ */
struct sp2_CByteSlice sp2_bit_vec_unsafe_data_ref(struct sp2_BitVec *this_); struct sp_CByteSlice sp_bit_vec_unsafe_data_ref(struct sp_BitVec *this_);
/** /**
* Clones a `ByteGrid`. * Clones a `ByteGrid`.
* The returned instance has to be freed with `byte_grid_dealloc`. * The returned instance has to be freed with `byte_grid_dealloc`.
*/ */
struct sp2_ByteGrid *sp2_byte_grid_clone(const struct sp2_ByteGrid *this_); struct sp_ByteGrid *sp_byte_grid_clone(const struct sp_ByteGrid *this_);
/** /**
* Deallocates a `ByteGrid`. * Deallocates a `ByteGrid`.
* *
* Note: do not call this if the grid has been consumed in another way, e.g. to create a command. * Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
*/ */
void sp2_byte_grid_dealloc(struct sp2_ByteGrid *this_); void sp_byte_grid_dealloc(struct sp_ByteGrid *this_);
/** /**
* Fills the whole `ByteGrid` with the specified value * Fills the whole `ByteGrid` with the specified value
*/ */
void sp2_byte_grid_fill(struct sp2_ByteGrid *this_, uint8_t value); void sp_byte_grid_fill(struct sp_ByteGrid *this_, uint8_t value);
/** /**
* Get the current value at the specified position * Get the current value at the specified position
*/ */
uint8_t sp2_byte_grid_get(const struct sp2_ByteGrid *this_, size_t x, size_t y); uint8_t sp_byte_grid_get(const struct sp_ByteGrid *this_, size_t x, size_t y);
/** /**
* Gets the height in pixels of the `ByteGrid` instance. * Gets the height in pixels of the `ByteGrid` instance.
*/ */
size_t sp2_byte_grid_height(const struct sp2_ByteGrid *this_); size_t sp_byte_grid_height(const struct sp_ByteGrid *this_);
/** /**
* Loads a `ByteGrid` with the specified dimensions from the provided data. * Loads a `ByteGrid` with the specified dimensions from the provided data.
* The returned instance has to be freed with `byte_grid_dealloc`. * The returned instance has to be freed with `byte_grid_dealloc`.
*/ */
struct sp2_ByteGrid *sp2_byte_grid_load(size_t width, struct sp_ByteGrid *sp_byte_grid_load(size_t width,
size_t height, size_t height,
const uint8_t *data, const uint8_t *data,
size_t data_length); size_t data_length);
@ -226,12 +218,12 @@ struct sp2_ByteGrid *sp2_byte_grid_load(size_t width,
* Creates a new `ByteGrid` instance. * Creates a new `ByteGrid` instance.
* The returned instance has to be freed with `byte_grid_dealloc`. * The returned instance has to be freed with `byte_grid_dealloc`.
*/ */
struct sp2_ByteGrid *sp2_byte_grid_new(size_t width, size_t height); struct sp_ByteGrid *sp_byte_grid_new(size_t width, size_t height);
/** /**
* Sets the current value at the specified position * Sets the current value at the specified position
*/ */
void sp2_byte_grid_set(struct sp2_ByteGrid *this_, void sp_byte_grid_set(struct sp_ByteGrid *this_,
size_t x, size_t x,
size_t y, size_t y,
uint8_t value); uint8_t value);
@ -247,112 +239,112 @@ void sp2_byte_grid_set(struct sp2_ByteGrid *this_,
* Reading and writing concurrently to either the original instance or the returned data will * Reading and writing concurrently to either the original instance or the returned data will
* result in undefined behavior. * result in undefined behavior.
*/ */
struct sp2_CByteSlice sp2_byte_grid_unsafe_data_ref(struct sp2_ByteGrid *this_); struct sp_CByteSlice sp_byte_grid_unsafe_data_ref(struct sp_ByteGrid *this_);
/** /**
* Gets the width in pixels of the `ByteGrid` instance. * Gets the width in pixels of the `ByteGrid` instance.
*/ */
size_t sp2_byte_grid_width(const struct sp2_ByteGrid *this_); size_t sp_byte_grid_width(const struct sp_ByteGrid *this_);
/** /**
* Allocates a new `Command::BitmapLinear` instance. * Allocates a new `Command::BitmapLinear` instance.
* The passed `BitVec` gets deallocated in the process. * The passed `BitVec` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_bitmap_linear(sp2_Offset offset, struct sp_Command *sp_command_bitmap_linear(sp_Offset offset,
struct sp2_BitVec *bit_vec, struct sp_BitVec *bit_vec,
sp2_CompressionCode compression); sp_CompressionCode compression);
/** /**
* Allocates a new `Command::BitmapLinearAnd` instance. * Allocates a new `Command::BitmapLinearAnd` instance.
* The passed `BitVec` gets deallocated in the process. * The passed `BitVec` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_bitmap_linear_and(sp2_Offset offset, struct sp_Command *sp_command_bitmap_linear_and(sp_Offset offset,
struct sp2_BitVec *bit_vec, struct sp_BitVec *bit_vec,
sp2_CompressionCode compression); sp_CompressionCode compression);
/** /**
* Allocates a new `Command::BitmapLinearOr` instance. * Allocates a new `Command::BitmapLinearOr` instance.
* The passed `BitVec` gets deallocated in the process. * The passed `BitVec` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_bitmap_linear_or(sp2_Offset offset, struct sp_Command *sp_command_bitmap_linear_or(sp_Offset offset,
struct sp2_BitVec *bit_vec, struct sp_BitVec *bit_vec,
sp2_CompressionCode compression); sp_CompressionCode compression);
/** /**
* Allocates a new `Command::BitmapLinearWin` instance. * Allocates a new `Command::BitmapLinearWin` instance.
* The passed `PixelGrid` gets deallocated in the process. * The passed `PixelGrid` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_bitmap_linear_win(size_t x, struct sp_Command *sp_command_bitmap_linear_win(size_t x,
size_t y, size_t y,
struct sp2_PixelGrid *byte_grid, struct sp_PixelGrid *byte_grid,
sp2_CompressionCode compression_code); sp_CompressionCode compression_code);
/** /**
* Allocates a new `Command::BitmapLinearXor` instance. * Allocates a new `Command::BitmapLinearXor` instance.
* The passed `BitVec` gets deallocated in the process. * The passed `BitVec` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_bitmap_linear_xor(sp2_Offset offset, struct sp_Command *sp_command_bitmap_linear_xor(sp_Offset offset,
struct sp2_BitVec *bit_vec, struct sp_BitVec *bit_vec,
sp2_CompressionCode compression); sp_CompressionCode compression);
/** /**
* Allocates a new `Command::Brightness` instance * Allocates a new `Command::Brightness` instance
*/ */
struct sp2_Command *sp2_command_brightness(sp2_Brightness brightness); struct sp_Command *sp_command_brightness(sp_Brightness brightness);
/** /**
* Allocates a new `Command::CharBrightness` instance. * Allocates a new `Command::CharBrightness` instance.
* The passed `ByteGrid` gets deallocated in the process. * The passed `ByteGrid` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_char_brightness(size_t x, struct sp_Command *sp_command_char_brightness(size_t x,
size_t y, size_t y,
struct sp2_ByteGrid *byte_grid); struct sp_ByteGrid *byte_grid);
/** /**
* Allocates a new `Command::Clear` instance * Allocates a new `Command::Clear` instance
*/ */
struct sp2_Command *sp2_command_clear(void); struct sp_Command *sp_command_clear(void);
/** /**
* Clones a `Command` instance * Clones a `Command` instance
*/ */
struct sp2_Command *sp2_command_clone(const struct sp2_Command *original); struct sp_Command *sp_command_clone(const struct sp_Command *original);
/** /**
* Allocates a new `Command::Cp437Data` instance. * Allocates a new `Command::Cp437Data` instance.
* The passed `ByteGrid` gets deallocated in the process. * The passed `ByteGrid` gets deallocated in the process.
*/ */
struct sp2_Command *sp2_command_cp437_data(size_t x, struct sp_Command *sp_command_cp437_data(size_t x,
size_t y, size_t y,
struct sp2_ByteGrid *byte_grid); struct sp_ByteGrid *byte_grid);
/** /**
* Deallocates a `Command`. Note that connection_send does this implicitly, so you only need * Deallocates a `Command`. Note that connection_send does this implicitly, so you only need
* to do this if you use the library for parsing commands. * to do this if you use the library for parsing commands.
*/ */
void sp2_command_dealloc(struct sp2_Command *ptr); void sp_command_dealloc(struct sp_Command *ptr);
/** /**
* Allocates a new `Command::FadeOut` instance * Allocates a new `Command::FadeOut` instance
*/ */
struct sp2_Command *sp2_command_fade_out(void); struct sp_Command *sp_command_fade_out(void);
/** /**
* Allocates a new `Command::HardReset` instance * Allocates a new `Command::HardReset` instance
*/ */
struct sp2_Command *sp2_command_hard_reset(void); struct sp_Command *sp_command_hard_reset(void);
/** /**
* Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process. * Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process.
* *
* Returns: pointer to command or NULL * Returns: pointer to command or NULL
*/ */
struct sp2_Command *sp2_command_try_from_packet(struct sp2_Packet *packet); struct sp_Command *sp_command_try_from_packet(struct sp_Packet *packet);
/** /**
* Closes and deallocates a connection instance * Closes and deallocates a connection instance
*/ */
void sp2_connection_dealloc(struct sp2_Connection *ptr); void sp_connection_dealloc(struct sp_Connection *ptr);
/** /**
* Creates a new instance of Connection. * Creates a new instance of Connection.
@ -362,66 +354,66 @@ void sp2_connection_dealloc(struct sp2_Connection *ptr);
* *
* Panics: bad string encoding * Panics: bad string encoding
*/ */
struct sp2_Connection *sp2_connection_open(const char *host); struct sp_Connection *sp_connection_open(const char *host);
/** /**
* Sends the command instance. The instance is consumed / destroyed and cannot be used after this call. * Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.
*/ */
bool sp2_connection_send(const struct sp2_Connection *connection, bool sp_connection_send(const struct sp_Connection *connection,
struct sp2_Packet *command_ptr); struct sp_Packet *command_ptr);
/** /**
* Deallocates a `Packet`. * Deallocates a `Packet`.
* *
* Note: do not call this if the instance has been consumed in another way, e.g. by sending it. * Note: do not call this if the instance has been consumed in another way, e.g. by sending it.
*/ */
void sp2_packet_dealloc(struct sp2_Packet *this_); void sp_packet_dealloc(struct sp_Packet *this_);
/** /**
* Turns a `Command` into a `Packet`. The command gets deallocated in the process. * Turns a `Command` into a `Packet`. The command gets deallocated in the process.
*/ */
struct sp2_Packet *sp2_packet_from_command(struct sp2_Command *command); struct sp_Packet *sp_packet_from_command(struct sp_Command *command);
/** /**
* Tries to load a `Packet` from the passed array with the specified length. * Tries to load a `Packet` from the passed array with the specified length.
* *
* returns: NULL in case of an error, pointer to the allocated packet otherwise * returns: NULL in case of an error, pointer to the allocated packet otherwise
*/ */
struct sp2_Packet *sp2_packet_try_load(const uint8_t *data, size_t length); struct sp_Packet *sp_packet_try_load(const uint8_t *data, size_t length);
/** /**
* Clones a `PixelGrid`. * Clones a `PixelGrid`.
* The returned instance has to be freed with `pixel_grid_dealloc`. * The returned instance has to be freed with `pixel_grid_dealloc`.
*/ */
struct sp2_PixelGrid *sp2_pixel_grid_clone(const struct sp2_PixelGrid *this_); struct sp_PixelGrid *sp_pixel_grid_clone(const struct sp_PixelGrid *this_);
/** /**
* Deallocates a `PixelGrid`. * Deallocates a `PixelGrid`.
* *
* Note: do not call this if the grid has been consumed in another way, e.g. to create a command. * Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
*/ */
void sp2_pixel_grid_dealloc(struct sp2_PixelGrid *this_); void sp_pixel_grid_dealloc(struct sp_PixelGrid *this_);
/** /**
* Fills the whole `PixelGrid` with the specified value * Fills the whole `PixelGrid` with the specified value
*/ */
void sp2_pixel_grid_fill(struct sp2_PixelGrid *this_, bool value); void sp_pixel_grid_fill(struct sp_PixelGrid *this_, bool value);
/** /**
* Get the current value at the specified position * Get the current value at the specified position
*/ */
bool sp2_pixel_grid_get(const struct sp2_PixelGrid *this_, size_t x, size_t y); bool sp_pixel_grid_get(const struct sp_PixelGrid *this_, size_t x, size_t y);
/** /**
* Gets the height in pixels of the `PixelGrid` instance. * Gets the height in pixels of the `PixelGrid` instance.
*/ */
size_t sp2_pixel_grid_height(const struct sp2_PixelGrid *this_); size_t sp_pixel_grid_height(const struct sp_PixelGrid *this_);
/** /**
* Loads a `PixelGrid` with the specified dimensions from the provided data. * Loads a `PixelGrid` with the specified dimensions from the provided data.
* The returned instance has to be freed with `pixel_grid_dealloc`. * The returned instance has to be freed with `pixel_grid_dealloc`.
*/ */
struct sp2_PixelGrid *sp2_pixel_grid_load(size_t width, struct sp_PixelGrid *sp_pixel_grid_load(size_t width,
size_t height, size_t height,
const uint8_t *data, const uint8_t *data,
size_t data_length); size_t data_length);
@ -430,12 +422,12 @@ struct sp2_PixelGrid *sp2_pixel_grid_load(size_t width,
* Creates a new `PixelGrid` instance. * Creates a new `PixelGrid` instance.
* The returned instance has to be freed with `pixel_grid_dealloc`. * The returned instance has to be freed with `pixel_grid_dealloc`.
*/ */
struct sp2_PixelGrid *sp2_pixel_grid_new(size_t width, size_t height); struct sp_PixelGrid *sp_pixel_grid_new(size_t width, size_t height);
/** /**
* Sets the current value at the specified position * Sets the current value at the specified position
*/ */
void sp2_pixel_grid_set(struct sp2_PixelGrid *this_, void sp_pixel_grid_set(struct sp_PixelGrid *this_,
size_t x, size_t x,
size_t y, size_t y,
bool value); bool value);
@ -451,12 +443,12 @@ void sp2_pixel_grid_set(struct sp2_PixelGrid *this_,
* Reading and writing concurrently to either the original instance or the returned data will * Reading and writing concurrently to either the original instance or the returned data will
* result in undefined behavior. * result in undefined behavior.
*/ */
struct sp2_CByteSlice sp2_pixel_grid_unsafe_data_ref(struct sp2_PixelGrid *this_); struct sp_CByteSlice sp_pixel_grid_unsafe_data_ref(struct sp_PixelGrid *this_);
/** /**
* Gets the width in pixels of the `PixelGrid` instance. * Gets the width in pixels of the `PixelGrid` instance.
*/ */
size_t sp2_pixel_grid_width(const struct sp2_PixelGrid *this_); size_t sp_pixel_grid_width(const struct sp_PixelGrid *this_);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View file

@ -0,0 +1,19 @@
#include <stdio.h>
#include "servicepoint.h"
int main(void) {
sp_Connection *connection = sp_connection_open("localhost:2342");
if (connection == NULL)
return 1;
sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT);
sp_pixel_grid_fill(pixels, true);
sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
sp_Packet *packet = sp_packet_from_command(command);
if (!sp_connection_send(connection, packet))
return 1;
sp_connection_dealloc(connection);
return 0;
}

View file

@ -0,0 +1,94 @@
pub use servicepoint::BitVec;
use servicepoint::DataRef;
use crate::c_slice::CByteSlice;
/// Creates a new `BitVec` instance.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec {
Box::into_raw(Box::new(BitVec::new(size)))
}
/// Loads a `BitVec` from the provided data.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_load(
data: *const u8,
data_length: usize,
) -> *mut BitVec {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(BitVec::from(data)))
}
/// Clones a `BitVec`.
/// The returned instance has to be freed with `bit_vec_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_clone(this: *const BitVec) -> *mut BitVec {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `BitVec`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut BitVec) {
_ = Box::from_raw(this);
}
/// Gets the value of a bit from the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_get(
this: *const BitVec,
index: usize,
) -> bool {
(*this).get(index)
}
/// Sets the value of a bit in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_set(
this: *mut BitVec,
index: usize,
value: bool,
) -> bool {
(*this).set(index, value)
}
/// Sets the value of all bits in the `BitVec`.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) {
(*this).fill(value)
}
/// Gets the length of the `BitVec` in bits.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize {
(*this).len()
}
/// Returns true if length is 0.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool {
(*this).is_empty()
}
/// Gets an unsafe reference to the data of the `BitVec` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `BitVec`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref(
this: *mut BitVec,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}

View file

@ -0,0 +1,103 @@
pub use servicepoint::ByteGrid;
use servicepoint::{DataRef, Grid};
use crate::c_slice::CByteSlice;
/// Creates a new `ByteGrid` instance.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_new(
width: usize,
height: usize,
) -> *mut ByteGrid {
Box::into_raw(Box::new(ByteGrid::new(width, height)))
}
/// Loads a `ByteGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut ByteGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(ByteGrid::load(width, height, data)))
}
/// Clones a `ByteGrid`.
/// The returned instance has to be freed with `byte_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_clone(
this: *const ByteGrid,
) -> *mut ByteGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `ByteGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_dealloc(this: *mut ByteGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_get(
this: *const ByteGrid,
x: usize,
y: usize,
) -> u8 {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_set(
this: *mut ByteGrid,
x: usize,
y: usize,
value: u8,
) {
(*this).set(x, y, value);
}
/// Fills the whole `ByteGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_fill(this: *mut ByteGrid, value: u8) {
(*this).fill(value);
}
/// Gets the width in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_width(this: *const ByteGrid) -> usize {
(*this).width()
}
/// Gets the height in pixels of the `ByteGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_height(this: *const ByteGrid) -> usize {
(*this).height()
}
/// Gets an unsafe reference to the data of the `ByteGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `ByteGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp_byte_grid_unsafe_data_ref(
this: *mut ByteGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}

View file

@ -1,4 +1,3 @@
#[cfg(feature = "c_api")]
#[repr(C)] #[repr(C)]
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
/// ///

View file

@ -0,0 +1,166 @@
use std::ptr::null_mut;
use servicepoint::{
BitVec, ByteGrid, CompressionCode, Origin, Packet, PixelGrid,
};
pub use servicepoint::{Brightness, Command, Offset};
/// Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process.
///
/// Returns: pointer to command or NULL
#[no_mangle]
pub unsafe extern "C" fn sp_command_try_from_packet(
packet: *mut Packet,
) -> *mut Command {
let packet = *Box::from_raw(packet);
match Command::try_from(packet) {
Err(_) => null_mut(),
Ok(command) => Box::into_raw(Box::new(command)),
}
}
/// Clones a `Command` instance
#[no_mangle]
pub unsafe extern "C" fn sp_command_clone(
original: *const Command,
) -> *mut Command {
Box::into_raw(Box::new((*original).clone()))
}
/// Allocates a new `Command::Clear` instance
#[no_mangle]
pub unsafe extern "C" fn sp_command_clear() -> *mut Command {
Box::into_raw(Box::new(Command::Clear))
}
/// Allocates a new `Command::HardReset` instance
#[no_mangle]
pub unsafe extern "C" fn sp_command_hard_reset() -> *mut Command {
Box::into_raw(Box::new(Command::HardReset))
}
/// Allocates a new `Command::FadeOut` instance
#[no_mangle]
pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command {
Box::into_raw(Box::new(Command::FadeOut))
}
/// Allocates a new `Command::Brightness` instance
#[no_mangle]
pub unsafe extern "C" fn sp_command_brightness(
brightness: Brightness,
) -> *mut Command {
Box::into_raw(Box::new(Command::Brightness(brightness)))
}
/// Allocates a new `Command::CharBrightness` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_char_brightness(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::CharBrightness(Origin(x, y), byte_grid)))
}
/// Allocates a new `Command::BitmapLinear` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinear(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearAnd` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_and(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearAnd(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearOr` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_or(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearOr(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::BitmapLinearXor` instance.
/// The passed `BitVec` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
offset: Offset,
bit_vec: *mut BitVec,
compression: CompressionCode,
) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearXor(
offset,
bit_vec,
compression,
)))
}
/// Allocates a new `Command::Cp437Data` instance.
/// The passed `ByteGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_cp437_data(
x: usize,
y: usize,
byte_grid: *mut ByteGrid,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid)))
}
/// Allocates a new `Command::BitmapLinearWin` instance.
/// The passed `PixelGrid` gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_win(
x: usize,
y: usize,
byte_grid: *mut PixelGrid,
compression_code: CompressionCode,
) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::BitmapLinearWin(
Origin(x, y),
byte_grid,
compression_code,
)))
}
/// Deallocates a `Command`. Note that connection_send does this implicitly, so you only need
/// to do this if you use the library for parsing commands.
#[no_mangle]
pub unsafe extern "C" fn sp_command_dealloc(ptr: *mut Command) {
_ = Box::from_raw(ptr);
}

View file

@ -0,0 +1,40 @@
use std::ffi::{c_char, CStr};
use std::ptr::null_mut;
pub use servicepoint::Connection;
use servicepoint::Packet;
/// Creates a new instance of Connection.
/// The returned instance has to be deallocated with `connection_dealloc`.
///
/// returns: NULL if connection fails or connected instance
///
/// Panics: bad string encoding
#[no_mangle]
pub unsafe extern "C" fn sp_connection_open(
host: *const c_char,
) -> *mut Connection {
let host = CStr::from_ptr(host).to_str().expect("Bad encoding");
let connection = match Connection::open(host) {
Err(_) => return null_mut(),
Ok(value) => value,
};
Box::into_raw(Box::new(connection))
}
/// Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.
#[no_mangle]
pub unsafe extern "C" fn sp_connection_send(
connection: *const Connection,
command_ptr: *mut Packet,
) -> bool {
let packet = Box::from_raw(command_ptr);
(*connection).send(*packet).is_ok()
}
/// Closes and deallocates a connection instance
#[no_mangle]
pub unsafe extern "C" fn sp_connection_dealloc(ptr: *mut Connection) {
_ = Box::from_raw(ptr);
}

View file

@ -0,0 +1,14 @@
pub use servicepoint::{
CompressionCode, PIXEL_COUNT, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_HEIGHT,
TILE_SIZE, TILE_WIDTH,
};
pub mod bit_vec;
pub mod byte_grid;
pub mod c_slice;
pub mod command;
pub mod connection;
pub mod packet;
pub mod pixel_grid;
pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32;

View file

@ -0,0 +1,37 @@
use std::ptr::null_mut;
use servicepoint::Command;
pub use servicepoint::Packet;
/// Turns a `Command` into a `Packet`. The command gets deallocated in the process.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_from_command(
command: *mut Command,
) -> *mut Packet {
let command = *Box::from_raw(command);
let packet = command.into();
Box::into_raw(Box::new(packet))
}
/// Tries to load a `Packet` from the passed array with the specified length.
///
/// returns: NULL in case of an error, pointer to the allocated packet otherwise
#[no_mangle]
pub unsafe extern "C" fn sp_packet_try_load(
data: *const u8,
length: usize,
) -> *mut Packet {
let data = std::slice::from_raw_parts(data, length);
match Packet::try_from(data) {
Err(_) => null_mut(),
Ok(packet) => Box::into_raw(Box::new(packet)),
}
}
/// Deallocates a `Packet`.
///
/// Note: do not call this if the instance has been consumed in another way, e.g. by sending it.
#[no_mangle]
pub unsafe extern "C" fn sp_packet_dealloc(this: *mut Packet) {
_ = Box::from_raw(this)
}

View file

@ -0,0 +1,102 @@
use servicepoint::{DataRef, Grid, PixelGrid};
use crate::c_slice::CByteSlice;
/// Creates a new `PixelGrid` instance.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_new(
width: usize,
height: usize,
) -> *mut PixelGrid {
Box::into_raw(Box::new(PixelGrid::new(width, height)))
}
/// Loads a `PixelGrid` with the specified dimensions from the provided data.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_load(
width: usize,
height: usize,
data: *const u8,
data_length: usize,
) -> *mut PixelGrid {
let data = std::slice::from_raw_parts(data, data_length);
Box::into_raw(Box::new(PixelGrid::load(width, height, data)))
}
/// Clones a `PixelGrid`.
/// The returned instance has to be freed with `pixel_grid_dealloc`.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_clone(
this: *const PixelGrid,
) -> *mut PixelGrid {
Box::into_raw(Box::new((*this).clone()))
}
/// Deallocates a `PixelGrid`.
///
/// Note: do not call this if the grid has been consumed in another way, e.g. to create a command.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_dealloc(this: *mut PixelGrid) {
_ = Box::from_raw(this);
}
/// Get the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_get(
this: *const PixelGrid,
x: usize,
y: usize,
) -> bool {
(*this).get(x, y)
}
/// Sets the current value at the specified position
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_set(
this: *mut PixelGrid,
x: usize,
y: usize,
value: bool,
) {
(*this).set(x, y, value);
}
/// Fills the whole `PixelGrid` with the specified value
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_fill(this: *mut PixelGrid, value: bool) {
(*this).fill(value);
}
/// Gets the width in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_width(this: *const PixelGrid) -> usize {
(*this).width()
}
/// Gets the height in pixels of the `PixelGrid` instance.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_height(this: *const PixelGrid) -> usize {
(*this).height()
}
/// Gets an unsafe reference to the data of the `PixelGrid` instance.
///
/// ## Safety
///
/// The caller has to make sure to never access the returned memory after the `PixelGrid`
/// instance has been consumed or manually deallocated.
///
/// Reading and writing concurrently to either the original instance or the returned data will
/// result in undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn sp_pixel_grid_unsafe_data_ref(
this: *mut PixelGrid,
) -> CByteSlice {
let data = (*this).data_ref_mut();
CByteSlice {
start: data.as_mut_ptr_range().start,
length: data.len(),
}
}

View file

@ -0,0 +1,17 @@
[package]
name = "servicepoint_binding_cs"
version.workspace = true
edition = "2021"
publish = false
readme = "README.md"
[lib]
crate-type = ["cdylib"]
test = false
[build-dependencies]
csbindgen = "1.8.0"
[dependencies]
servicepoint_binding_c = { version = "0.5.0", path = "../servicepoint_binding_c" }
servicepoint = { version = "0.5.0", path = "../servicepoint" }

View file

@ -0,0 +1,65 @@
# ServicePoint
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
Display" or "Airport Display".
This crate contains C# bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP.
## Examples
```csharp
using ServicePoint;
// using statement calls Dispose() on scope exit, which frees unmanaged instances
using var connection = Connection.Open("127.0.0.1:2342");
using var pixels = PixelGrid.New(Constants.PixelWidth, Constants.PixelHeight);
while (true)
{
pixels.Fill(true);
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
Thread.Sleep(5000);
pixels.Fill(false);
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
Thread.Sleep(5000);
}
```
A full example including project files is available as part of this crate.
## Note on stability
This library is still in early development.
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
## Installation
NuGet packages are not a good way to distribute native projects ([relevant issue](https://github.com/dotnet/sdk/issues/33845)).
Because of that, there is no NuGet package you can use directly.
Including this repository as a submodule and building from source is the recommended way of using the library.
```bash
git submodule add https://github.com/kaesaecracker/servicepoint.git
git commit -m "add servicepoint submodule"
```
You can now reference `servicepoint-bindings-cs/src/ServicePoint.csproj` in your project.
The rust library will automatically be built.
Please provide more information in the form of an issue if you need the build to copy a different library file for your platform.
## Notes on differences to rust library
Uses C bindings internally to provide a similar API to rust. Things to keep in mind:
- You will get a `NullPointerException` when trying to call a method where the native instance has been consumed already (e.g. when `Send`ing a command instance twice). Send a clone instead of the original if you want to keep using it.
- Some lower-level APIs _will_ panic in native code when used improperly.
Example: manipulating the `Span<byte>` of an object after freeing the instance.
- C# specifics are documented in the library. Use the rust documentation for everything else. Naming and semantics are the same apart from CamelCase instead of kebab_case.
- You will only get rust backtraces in debug builds of the native code.
- F# is not explicitly tested. If there are usability or functionality problems, please open an issue.
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
## Everything else
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.

View file

@ -1,8 +1,8 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint2", "src/ServicePoint2.csproj", "{70EFFA3F-012A-4518-9627-466BEAE4252E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoint/ServicePoint.csproj", "{70EFFA3F-012A-4518-9627-466BEAE4252E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "../examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View file

@ -8,222 +8,232 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ServicePoint2.BindGen namespace ServicePoint.BindGen
{ {
public static unsafe partial class NativeMethods public static unsafe partial class NativeMethods
{ {
const string __DllName = "servicepoint2"; const string __DllName = "servicepoint_binding_c";
public const nuint TILE_SIZE = 8;
public const nuint TILE_WIDTH = 56;
public const nuint TILE_HEIGHT = 20;
/// <summary>Creates a new `BitVec` instance. The returned instance has to be freed with `bit_vec_dealloc`.</summary> /// <summary>Creates a new `BitVec` instance. The returned instance has to be freed with `bit_vec_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern BitVec* sp2_bit_vec_new(nuint size); public static extern BitVec* sp_bit_vec_new(nuint size);
/// <summary>Loads a `BitVec` from the provided data. The returned instance has to be freed with `bit_vec_dealloc`.</summary> /// <summary>Loads a `BitVec` from the provided data. The returned instance has to be freed with `bit_vec_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern BitVec* sp2_bit_vec_load(byte* data, nuint data_length); public static extern BitVec* sp_bit_vec_load(byte* data, nuint data_length);
/// <summary>Clones a `BitVec`. The returned instance has to be freed with `bit_vec_dealloc`.</summary> /// <summary>Clones a `BitVec`. The returned instance has to be freed with `bit_vec_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern BitVec* sp2_bit_vec_clone(BitVec* @this); public static extern BitVec* sp_bit_vec_clone(BitVec* @this);
/// <summary>Deallocates a `BitVec`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary> /// <summary>Deallocates a `BitVec`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_bit_vec_dealloc(BitVec* @this); public static extern void sp_bit_vec_dealloc(BitVec* @this);
/// <summary>Gets the value of a bit from the `BitVec`.</summary> /// <summary>Gets the value of a bit from the `BitVec`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
public static extern bool sp2_bit_vec_get(BitVec* @this, nuint index); public static extern bool sp_bit_vec_get(BitVec* @this, nuint index);
/// <summary>Sets the value of a bit in the `BitVec`.</summary> /// <summary>Sets the value of a bit in the `BitVec`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
public static extern bool sp2_bit_vec_set(BitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value); public static extern bool sp_bit_vec_set(BitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value);
/// <summary>Sets the value of all bits in the `BitVec`.</summary> /// <summary>Sets the value of all bits in the `BitVec`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_bit_vec_fill(BitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value); public static extern void sp_bit_vec_fill(BitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value);
/// <summary>Gets the length of the `BitVec` in bits.</summary> /// <summary>Gets the length of the `BitVec` in bits.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp2_bit_vec_len(BitVec* @this); public static extern nuint sp_bit_vec_len(BitVec* @this);
/// <summary>Returns true if length is 0.</summary> /// <summary>Returns true if length is 0.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
public static extern bool sp2_bit_vec_is_empty(BitVec* @this); public static extern bool sp_bit_vec_is_empty(BitVec* @this);
/// <summary>Gets an unsafe reference to the data of the `BitVec` instance. ## Safety The caller has to make sure to never access the returned memory after the `BitVec` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary> /// <summary>Gets an unsafe reference to the data of the `BitVec` instance. ## Safety The caller has to make sure to never access the returned memory after the `BitVec` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary>
[DllImport(__DllName, EntryPoint = "sp2_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp2_bit_vec_unsafe_data_ref(BitVec* @this); public static extern CByteSlice sp_bit_vec_unsafe_data_ref(BitVec* @this);
/// <summary>Creates a new `ByteGrid` instance. The returned instance has to be freed with `byte_grid_dealloc`.</summary> /// <summary>Creates a new `ByteGrid` instance. The returned instance has to be freed with `byte_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp2_byte_grid_new(nuint width, nuint height); public static extern ByteGrid* sp_byte_grid_new(nuint width, nuint height);
/// <summary>Loads a `ByteGrid` with the specified dimensions from the provided data. The returned instance has to be freed with `byte_grid_dealloc`.</summary> /// <summary>Loads a `ByteGrid` with the specified dimensions from the provided data. The returned instance has to be freed with `byte_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp2_byte_grid_load(nuint width, nuint height, byte* data, nuint data_length); public static extern ByteGrid* sp_byte_grid_load(nuint width, nuint height, byte* data, nuint data_length);
/// <summary>Clones a `ByteGrid`. The returned instance has to be freed with `byte_grid_dealloc`.</summary> /// <summary>Clones a `ByteGrid`. The returned instance has to be freed with `byte_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern ByteGrid* sp2_byte_grid_clone(ByteGrid* @this); public static extern ByteGrid* sp_byte_grid_clone(ByteGrid* @this);
/// <summary>Deallocates a `ByteGrid`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary> /// <summary>Deallocates a `ByteGrid`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_byte_grid_dealloc(ByteGrid* @this); public static extern void sp_byte_grid_dealloc(ByteGrid* @this);
/// <summary>Get the current value at the specified position</summary> /// <summary>Get the current value at the specified position</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern byte sp2_byte_grid_get(ByteGrid* @this, nuint x, nuint y); public static extern byte sp_byte_grid_get(ByteGrid* @this, nuint x, nuint y);
/// <summary>Sets the current value at the specified position</summary> /// <summary>Sets the current value at the specified position</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_byte_grid_set(ByteGrid* @this, nuint x, nuint y, byte value); public static extern void sp_byte_grid_set(ByteGrid* @this, nuint x, nuint y, byte value);
/// <summary>Fills the whole `ByteGrid` with the specified value</summary> /// <summary>Fills the whole `ByteGrid` with the specified value</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_byte_grid_fill(ByteGrid* @this, byte value); public static extern void sp_byte_grid_fill(ByteGrid* @this, byte value);
/// <summary>Gets the width in pixels of the `ByteGrid` instance.</summary> /// <summary>Gets the width in pixels of the `ByteGrid` instance.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp2_byte_grid_width(ByteGrid* @this); public static extern nuint sp_byte_grid_width(ByteGrid* @this);
/// <summary>Gets the height in pixels of the `ByteGrid` instance.</summary> /// <summary>Gets the height in pixels of the `ByteGrid` instance.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp2_byte_grid_height(ByteGrid* @this); public static extern nuint sp_byte_grid_height(ByteGrid* @this);
/// <summary>Gets an unsafe reference to the data of the `ByteGrid` instance. ## Safety The caller has to make sure to never access the returned memory after the `ByteGrid` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary> /// <summary>Gets an unsafe reference to the data of the `ByteGrid` instance. ## Safety The caller has to make sure to never access the returned memory after the `ByteGrid` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary>
[DllImport(__DllName, EntryPoint = "sp2_byte_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_byte_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp2_byte_grid_unsafe_data_ref(ByteGrid* @this); public static extern CByteSlice sp_byte_grid_unsafe_data_ref(ByteGrid* @this);
/// <summary>Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process. Returns: pointer to command or NULL</summary> /// <summary>Tries to turn a `Packet` into a `Command`. The packet is gets deallocated in the process. Returns: pointer to command or NULL</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_try_from_packet", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_try_from_packet(Packet* packet); public static extern Command* sp_command_try_from_packet(Packet* packet);
/// <summary>Clones a `Command` instance</summary> /// <summary>Clones a `Command` instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_clone(Command* original); public static extern Command* sp_command_clone(Command* original);
/// <summary>Allocates a new `Command::Clear` instance</summary> /// <summary>Allocates a new `Command::Clear` instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_clear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_clear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_clear(); public static extern Command* sp_command_clear();
/// <summary>Allocates a new `Command::HardReset` instance</summary> /// <summary>Allocates a new `Command::HardReset` instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_hard_reset", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_hard_reset", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_hard_reset(); public static extern Command* sp_command_hard_reset();
/// <summary>Allocates a new `Command::FadeOut` instance</summary> /// <summary>Allocates a new `Command::FadeOut` instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_fade_out(); public static extern Command* sp_command_fade_out();
/// <summary>Allocates a new `Command::Brightness` instance</summary> /// <summary>Allocates a new `Command::Brightness` instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_brightness(byte brightness); public static extern Command* sp_command_brightness(byte brightness);
/// <summary>Allocates a new `Command::CharBrightness` instance. The passed `ByteGrid` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::CharBrightness` instance. The passed `ByteGrid` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_char_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_char_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_char_brightness(nuint x, nuint y, ByteGrid* byte_grid); public static extern Command* sp_command_char_brightness(nuint x, nuint y, ByteGrid* byte_grid);
/// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_bitmap_linear(nuint offset, BitVec* bit_vec, CompressionCode compression); public static extern Command* sp_command_bitmap_linear(nuint offset, BitVec* bit_vec, CompressionCode compression);
/// <summary>Allocates a new `Command::BitmapLinearAnd` instance. The passed `BitVec` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::BitmapLinearAnd` instance. The passed `BitVec` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_bitmap_linear_and", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_and", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_bitmap_linear_and(nuint offset, BitVec* bit_vec, CompressionCode compression); public static extern Command* sp_command_bitmap_linear_and(nuint offset, BitVec* bit_vec, CompressionCode compression);
/// <summary>Allocates a new `Command::BitmapLinearOr` instance. The passed `BitVec` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::BitmapLinearOr` instance. The passed `BitVec` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_bitmap_linear_or", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_or", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_bitmap_linear_or(nuint offset, BitVec* bit_vec, CompressionCode compression); public static extern Command* sp_command_bitmap_linear_or(nuint offset, BitVec* bit_vec, CompressionCode compression);
/// <summary>Allocates a new `Command::BitmapLinearXor` instance. The passed `BitVec` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::BitmapLinearXor` instance. The passed `BitVec` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_bitmap_linear_xor", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_xor", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_bitmap_linear_xor(nuint offset, BitVec* bit_vec, CompressionCode compression); public static extern Command* sp_command_bitmap_linear_xor(nuint offset, BitVec* bit_vec, CompressionCode compression);
/// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_cp437_data(nuint x, nuint y, ByteGrid* byte_grid); public static extern Command* sp_command_cp437_data(nuint x, nuint y, ByteGrid* byte_grid);
/// <summary>Allocates a new `Command::BitmapLinearWin` instance. The passed `PixelGrid` gets deallocated in the process.</summary> /// <summary>Allocates a new `Command::BitmapLinearWin` instance. The passed `PixelGrid` gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_bitmap_linear_win", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_win", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp2_command_bitmap_linear_win(nuint x, nuint y, PixelGrid* byte_grid, CompressionCode compression_code); public static extern Command* sp_command_bitmap_linear_win(nuint x, nuint y, PixelGrid* byte_grid, CompressionCode compression_code);
/// <summary>Deallocates a `Command`. Note that connection_send does this implicitly, so you only need to do this if you use the library for parsing commands.</summary> /// <summary>Deallocates a `Command`. Note that connection_send does this implicitly, so you only need to do this if you use the library for parsing commands.</summary>
[DllImport(__DllName, EntryPoint = "sp2_command_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_command_dealloc(Command* ptr); public static extern void sp_command_dealloc(Command* ptr);
/// <summary>Creates a new instance of Connection. The returned instance has to be deallocated with `connection_dealloc`. returns: NULL if connection fails or connected instance Panics: bad string encoding</summary> /// <summary>Creates a new instance of Connection. The returned instance has to be deallocated with `connection_dealloc`. returns: NULL if connection fails or connected instance Panics: bad string encoding</summary>
[DllImport(__DllName, EntryPoint = "sp2_connection_open", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_connection_open", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Connection* sp2_connection_open(byte* host); public static extern Connection* sp_connection_open(byte* host);
/// <summary>Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.</summary> /// <summary>Sends the command instance. The instance is consumed / destroyed and cannot be used after this call.</summary>
[DllImport(__DllName, EntryPoint = "sp2_connection_send", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_connection_send", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
public static extern bool sp2_connection_send(Connection* connection, Packet* command_ptr); public static extern bool sp_connection_send(Connection* connection, Packet* command_ptr);
/// <summary>Closes and deallocates a connection instance</summary> /// <summary>Closes and deallocates a connection instance</summary>
[DllImport(__DllName, EntryPoint = "sp2_connection_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_connection_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_connection_dealloc(Connection* ptr); public static extern void sp_connection_dealloc(Connection* ptr);
/// <summary>Creates a new `PixelGrid` instance. The returned instance has to be freed with `pixel_grid_dealloc`.</summary> /// <summary>Creates a new `PixelGrid` instance. The returned instance has to be freed with `pixel_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern PixelGrid* sp2_pixel_grid_new(nuint width, nuint height); public static extern PixelGrid* sp_pixel_grid_new(nuint width, nuint height);
/// <summary>Loads a `PixelGrid` with the specified dimensions from the provided data. The returned instance has to be freed with `pixel_grid_dealloc`.</summary> /// <summary>Loads a `PixelGrid` with the specified dimensions from the provided data. The returned instance has to be freed with `pixel_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern PixelGrid* sp2_pixel_grid_load(nuint width, nuint height, byte* data, nuint data_length); public static extern PixelGrid* sp_pixel_grid_load(nuint width, nuint height, byte* data, nuint data_length);
/// <summary>Clones a `PixelGrid`. The returned instance has to be freed with `pixel_grid_dealloc`.</summary> /// <summary>Clones a `PixelGrid`. The returned instance has to be freed with `pixel_grid_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern PixelGrid* sp2_pixel_grid_clone(PixelGrid* @this); public static extern PixelGrid* sp_pixel_grid_clone(PixelGrid* @this);
/// <summary>Deallocates a `PixelGrid`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary> /// <summary>Deallocates a `PixelGrid`. Note: do not call this if the grid has been consumed in another way, e.g. to create a command.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_pixel_grid_dealloc(PixelGrid* @this); public static extern void sp_pixel_grid_dealloc(PixelGrid* @this);
/// <summary>Get the current value at the specified position</summary> /// <summary>Get the current value at the specified position</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [return: MarshalAs(UnmanagedType.U1)]
public static extern bool sp2_pixel_grid_get(PixelGrid* @this, nuint x, nuint y); public static extern bool sp_pixel_grid_get(PixelGrid* @this, nuint x, nuint y);
/// <summary>Sets the current value at the specified position</summary> /// <summary>Sets the current value at the specified position</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_pixel_grid_set(PixelGrid* @this, nuint x, nuint y, [MarshalAs(UnmanagedType.U1)] bool value); public static extern void sp_pixel_grid_set(PixelGrid* @this, nuint x, nuint y, [MarshalAs(UnmanagedType.U1)] bool value);
/// <summary>Fills the whole `PixelGrid` with the specified value</summary> /// <summary>Fills the whole `PixelGrid` with the specified value</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_pixel_grid_fill(PixelGrid* @this, [MarshalAs(UnmanagedType.U1)] bool value); public static extern void sp_pixel_grid_fill(PixelGrid* @this, [MarshalAs(UnmanagedType.U1)] bool value);
/// <summary>Gets the width in pixels of the `PixelGrid` instance.</summary> /// <summary>Gets the width in pixels of the `PixelGrid` instance.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_width", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp2_pixel_grid_width(PixelGrid* @this); public static extern nuint sp_pixel_grid_width(PixelGrid* @this);
/// <summary>Gets the height in pixels of the `PixelGrid` instance.</summary> /// <summary>Gets the height in pixels of the `PixelGrid` instance.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_height", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern nuint sp2_pixel_grid_height(PixelGrid* @this); public static extern nuint sp_pixel_grid_height(PixelGrid* @this);
/// <summary>Gets an unsafe reference to the data of the `PixelGrid` instance. ## Safety The caller has to make sure to never access the returned memory after the `PixelGrid` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary> /// <summary>Gets an unsafe reference to the data of the `PixelGrid` instance. ## Safety The caller has to make sure to never access the returned memory after the `PixelGrid` instance has been consumed or manually deallocated. Reading and writing concurrently to either the original instance or the returned data will result in undefined behavior.</summary>
[DllImport(__DllName, EntryPoint = "sp2_pixel_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_pixel_grid_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern CByteSlice sp2_pixel_grid_unsafe_data_ref(PixelGrid* @this); public static extern CByteSlice sp_pixel_grid_unsafe_data_ref(PixelGrid* @this);
/// <summary>Turns a `Command` into a `Packet`. The command gets deallocated in the process.</summary> /// <summary>Turns a `Command` into a `Packet`. The command gets deallocated in the process.</summary>
[DllImport(__DllName, EntryPoint = "sp2_packet_from_command", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_packet_from_command", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Packet* sp2_packet_from_command(Command* command); public static extern Packet* sp_packet_from_command(Command* command);
/// <summary>Tries to load a `Packet` from the passed array with the specified length. returns: NULL in case of an error, pointer to the allocated packet otherwise</summary> /// <summary>Tries to load a `Packet` from the passed array with the specified length. returns: NULL in case of an error, pointer to the allocated packet otherwise</summary>
[DllImport(__DllName, EntryPoint = "sp2_packet_try_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_packet_try_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Packet* sp2_packet_try_load(byte* data, nuint length); public static extern Packet* sp_packet_try_load(byte* data, nuint length);
/// <summary>Deallocates a `Packet`. Note: do not call this if the instance has been consumed in another way, e.g. by sending it.</summary> /// <summary>Deallocates a `Packet`. Note: do not call this if the instance has been consumed in another way, e.g. by sending it.</summary>
[DllImport(__DllName, EntryPoint = "sp2_packet_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_packet_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void sp2_packet_dealloc(Packet* @this); public static extern void sp_packet_dealloc(Packet* @this);
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CByteSlice
{
public byte* start;
public nuint length;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct BitVec public unsafe partial struct BitVec
{ {
@ -244,13 +254,6 @@ namespace ServicePoint2.BindGen
{ {
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CByteSlice
{
public byte* start;
public nuint length;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct Packet public unsafe partial struct Packet
{ {

View file

@ -1,14 +1,14 @@
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec> public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
{ {
public static BitVec New(int size) public static BitVec New(int size)
{ {
unsafe unsafe
{ {
return new BitVec(NativeMethods.sp2_bit_vec_new((nuint)size)); return new BitVec(NativeMethods.sp_bit_vec_new((nuint)size));
} }
} }
@ -18,7 +18,7 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new BitVec(NativeMethods.sp2_bit_vec_load(bytesPtr, (nuint)bytes.Length)); return new BitVec(NativeMethods.sp_bit_vec_load(bytesPtr, (nuint)bytes.Length));
} }
} }
} }
@ -27,7 +27,7 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return new BitVec(NativeMethods.sp2_bit_vec_clone(Instance)); return new BitVec(NativeMethods.sp_bit_vec_clone(Instance));
} }
} }
@ -37,14 +37,14 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return NativeMethods.sp2_bit_vec_get(Instance, (nuint)index); return NativeMethods.sp_bit_vec_get(Instance, (nuint)index);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp2_bit_vec_set(Instance, (nuint)index, value); NativeMethods.sp_bit_vec_set(Instance, (nuint)index, value);
} }
} }
} }
@ -53,7 +53,7 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
NativeMethods.sp2_bit_vec_fill(Instance, value); NativeMethods.sp_bit_vec_fill(Instance, value);
} }
} }
@ -63,7 +63,7 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp2_bit_vec_len(Instance); return (int)NativeMethods.sp_bit_vec_len(Instance);
} }
} }
} }
@ -74,7 +74,7 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp2_bit_vec_unsafe_data_ref(Instance); var slice = NativeMethods.sp_bit_vec_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
@ -86,6 +86,6 @@ public sealed class BitVec : Sp2NativeInstance<BindGen.BitVec>
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_bit_vec_dealloc(Instance); NativeMethods.sp_bit_vec_dealloc(Instance);
} }
} }

View file

@ -1,15 +1,15 @@
using System.Text; using System.Text;
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid> public sealed class ByteGrid : SpNativeInstance<BindGen.ByteGrid>
{ {
public static ByteGrid New(int width, int height) public static ByteGrid New(int width, int height)
{ {
unsafe unsafe
{ {
return new ByteGrid(NativeMethods.sp2_byte_grid_new((nuint)width, (nuint)height)); return new ByteGrid(NativeMethods.sp_byte_grid_new((nuint)width, (nuint)height));
} }
} }
@ -19,7 +19,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new ByteGrid(NativeMethods.sp2_byte_grid_load((nuint)width, (nuint)height, bytesPtr, return new ByteGrid(NativeMethods.sp_byte_grid_load((nuint)width, (nuint)height, bytesPtr,
(nuint)bytes.Length)); (nuint)bytes.Length));
} }
} }
@ -29,7 +29,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return new ByteGrid(NativeMethods.sp2_byte_grid_clone(Instance)); return new ByteGrid(NativeMethods.sp_byte_grid_clone(Instance));
} }
} }
@ -39,14 +39,14 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return NativeMethods.sp2_byte_grid_get(Instance, (nuint)x, (nuint)y); return NativeMethods.sp_byte_grid_get(Instance, (nuint)x, (nuint)y);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp2_byte_grid_set(Instance, (nuint)x, (nuint)y, value); NativeMethods.sp_byte_grid_set(Instance, (nuint)x, (nuint)y, value);
} }
} }
} }
@ -85,7 +85,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
NativeMethods.sp2_byte_grid_fill(Instance, value); NativeMethods.sp_byte_grid_fill(Instance, value);
} }
} }
@ -95,7 +95,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp2_byte_grid_width(Instance); return (int)NativeMethods.sp_byte_grid_width(Instance);
} }
} }
} }
@ -106,7 +106,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp2_byte_grid_height(Instance); return (int)NativeMethods.sp_byte_grid_height(Instance);
} }
} }
} }
@ -117,7 +117,7 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp2_byte_grid_unsafe_data_ref(Instance); var slice = NativeMethods.sp_byte_grid_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
@ -129,6 +129,6 @@ public sealed class ByteGrid : Sp2NativeInstance<BindGen.ByteGrid>
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_byte_grid_dealloc(Instance); NativeMethods.sp_byte_grid_dealloc(Instance);
} }
} }

View file

@ -1,15 +1,15 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class Command : Sp2NativeInstance<BindGen.Command> public sealed class Command : SpNativeInstance<BindGen.Command>
{ {
public static bool TryFromPacket(Packet packet, [MaybeNullWhen(false)] out Command command) public static bool TryFromPacket(Packet packet, [MaybeNullWhen(false)] out Command command)
{ {
unsafe unsafe
{ {
var result = NativeMethods.sp2_command_try_from_packet(packet.Into()); var result = NativeMethods.sp_command_try_from_packet(packet.Into());
if (result == null) if (result == null)
{ {
command = null; command = null;
@ -25,7 +25,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_clone(Instance)); return new Command(NativeMethods.sp_command_clone(Instance));
} }
} }
@ -33,7 +33,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_clear()); return new Command(NativeMethods.sp_command_clear());
} }
} }
@ -41,7 +41,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_hard_reset()); return new Command(NativeMethods.sp_command_hard_reset());
} }
} }
@ -49,7 +49,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_fade_out()); return new Command(NativeMethods.sp_command_fade_out());
} }
} }
@ -57,7 +57,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_brightness(brightness)); return new Command(NativeMethods.sp_command_brightness(brightness));
} }
} }
@ -65,7 +65,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_char_brightness((ushort)x, (ushort)y, grid.Into())); return new Command(NativeMethods.sp_command_char_brightness((ushort)x, (ushort)y, grid.Into()));
} }
} }
@ -74,7 +74,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
unsafe unsafe
{ {
return new Command( return new Command(
NativeMethods.sp2_command_bitmap_linear((ushort)offset, bitVec.Into(), compressionCode)); NativeMethods.sp_command_bitmap_linear((ushort)offset, bitVec.Into(), compressionCode));
} }
} }
@ -83,7 +83,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
unsafe unsafe
{ {
return new Command( return new Command(
NativeMethods.sp2_command_bitmap_linear_and((ushort)offset, bitVec.Into(), compressionCode)); NativeMethods.sp_command_bitmap_linear_and((ushort)offset, bitVec.Into(), compressionCode));
} }
} }
@ -92,7 +92,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
unsafe unsafe
{ {
return new Command( return new Command(
NativeMethods.sp2_command_bitmap_linear_or((ushort)offset, bitVec.Into(), compressionCode)); NativeMethods.sp_command_bitmap_linear_or((ushort)offset, bitVec.Into(), compressionCode));
} }
} }
@ -101,7 +101,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
unsafe unsafe
{ {
return new Command( return new Command(
NativeMethods.sp2_command_bitmap_linear_xor((ushort)offset, bitVec.Into(), compressionCode)); NativeMethods.sp_command_bitmap_linear_xor((ushort)offset, bitVec.Into(), compressionCode));
} }
} }
@ -109,7 +109,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_bitmap_linear_win((ushort)x, (ushort)y, pixelGrid.Into(), compression)); return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, pixelGrid.Into(), compression));
} }
} }
@ -117,7 +117,7 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
{ {
unsafe unsafe
{ {
return new Command(NativeMethods.sp2_command_cp437_data((ushort)x, (ushort)y, byteGrid.Into())); return new Command(NativeMethods.sp_command_cp437_data((ushort)x, (ushort)y, byteGrid.Into()));
} }
} }
@ -127,6 +127,6 @@ public sealed class Command : Sp2NativeInstance<BindGen.Command>
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_command_dealloc(Instance); NativeMethods.sp_command_dealloc(Instance);
} }
} }

View file

@ -1,9 +1,9 @@
using System.Text; using System.Text;
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class Connection : Sp2NativeInstance<BindGen.Connection> public sealed class Connection : SpNativeInstance<BindGen.Connection>
{ {
public static Connection Open(string host) public static Connection Open(string host)
{ {
@ -11,7 +11,7 @@ public sealed class Connection : Sp2NativeInstance<BindGen.Connection>
{ {
fixed (byte* bytePtr = Encoding.UTF8.GetBytes(host)) fixed (byte* bytePtr = Encoding.UTF8.GetBytes(host))
{ {
return new Connection(NativeMethods.sp2_connection_open(bytePtr)); return new Connection(NativeMethods.sp_connection_open(bytePtr));
} }
} }
} }
@ -20,13 +20,13 @@ public sealed class Connection : Sp2NativeInstance<BindGen.Connection>
{ {
unsafe unsafe
{ {
return NativeMethods.sp2_connection_send(Instance, packet.Into()); return NativeMethods.sp_connection_send(Instance, packet.Into());
} }
} }
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_connection_dealloc(Instance); NativeMethods.sp_connection_dealloc(Instance);
} }
private unsafe Connection(BindGen.Connection* instance) : base(instance) private unsafe Connection(BindGen.Connection* instance) : base(instance)

View file

@ -1,4 +1,4 @@
namespace ServicePoint2; namespace ServicePoint;
public static class Constants public static class Constants
{ {

View file

@ -1,15 +1,15 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class Packet : Sp2NativeInstance<BindGen.Packet> public sealed class Packet : SpNativeInstance<BindGen.Packet>
{ {
public static Packet FromCommand(Command command) public static Packet FromCommand(Command command)
{ {
unsafe unsafe
{ {
return new Packet(NativeMethods.sp2_packet_from_command(command.Into())); return new Packet(NativeMethods.sp_packet_from_command(command.Into()));
} }
} }
@ -19,7 +19,7 @@ public sealed class Packet : Sp2NativeInstance<BindGen.Packet>
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
var instance = NativeMethods.sp2_packet_try_load(bytesPtr, (nuint)bytes.Length); var instance = NativeMethods.sp_packet_try_load(bytesPtr, (nuint)bytes.Length);
packet = instance == null packet = instance == null
? null ? null
: new Packet(instance); : new Packet(instance);
@ -34,6 +34,6 @@ public sealed class Packet : Sp2NativeInstance<BindGen.Packet>
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_packet_dealloc(Instance); NativeMethods.sp_packet_dealloc(Instance);
} }
} }

View file

@ -1,14 +1,14 @@
using ServicePoint2.BindGen; using ServicePoint.BindGen;
namespace ServicePoint2; namespace ServicePoint;
public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid> public sealed class PixelGrid : SpNativeInstance<BindGen.PixelGrid>
{ {
public static PixelGrid New(int width, int height) public static PixelGrid New(int width, int height)
{ {
unsafe unsafe
{ {
return new PixelGrid(NativeMethods.sp2_pixel_grid_new((nuint)width, (nuint)height)); return new PixelGrid(NativeMethods.sp_pixel_grid_new((nuint)width, (nuint)height));
} }
} }
@ -18,7 +18,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
fixed (byte* bytesPtr = bytes) fixed (byte* bytesPtr = bytes)
{ {
return new PixelGrid(NativeMethods.sp2_pixel_grid_load((nuint)width, (nuint)height, bytesPtr, return new PixelGrid(NativeMethods.sp_pixel_grid_load((nuint)width, (nuint)height, bytesPtr,
(nuint)bytes.Length)); (nuint)bytes.Length));
} }
} }
@ -28,7 +28,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return new PixelGrid(NativeMethods.sp2_pixel_grid_clone(Instance)); return new PixelGrid(NativeMethods.sp_pixel_grid_clone(Instance));
} }
} }
@ -38,14 +38,14 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return NativeMethods.sp2_pixel_grid_get(Instance, (nuint)x, (nuint)y); return NativeMethods.sp_pixel_grid_get(Instance, (nuint)x, (nuint)y);
} }
} }
set set
{ {
unsafe unsafe
{ {
NativeMethods.sp2_pixel_grid_set(Instance, (nuint)x, (nuint)y, value); NativeMethods.sp_pixel_grid_set(Instance, (nuint)x, (nuint)y, value);
} }
} }
} }
@ -54,7 +54,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
NativeMethods.sp2_pixel_grid_fill(Instance, value); NativeMethods.sp_pixel_grid_fill(Instance, value);
} }
} }
@ -64,7 +64,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp2_pixel_grid_width(Instance); return (int)NativeMethods.sp_pixel_grid_width(Instance);
} }
} }
} }
@ -75,7 +75,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
return (int)NativeMethods.sp2_pixel_grid_height(Instance); return (int)NativeMethods.sp_pixel_grid_height(Instance);
} }
} }
} }
@ -86,7 +86,7 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
{ {
unsafe unsafe
{ {
var slice = NativeMethods.sp2_pixel_grid_unsafe_data_ref(Instance); var slice = NativeMethods.sp_pixel_grid_unsafe_data_ref(Instance);
return new Span<byte>(slice.start, (int)slice.length); return new Span<byte>(slice.start, (int)slice.length);
} }
} }
@ -98,6 +98,6 @@ public sealed class PixelGrid : Sp2NativeInstance<BindGen.PixelGrid>
private protected override unsafe void Dealloc() private protected override unsafe void Dealloc()
{ {
NativeMethods.sp2_pixel_grid_dealloc(Instance); NativeMethods.sp_pixel_grid_dealloc(Instance);
} }
} }

View file

@ -11,60 +11,54 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>ServicePoint2</PackageId> <PackageId>ServicePoint</PackageId>
<Version>0.3.0</Version> <Version>0.5.0</Version>
<Authors>Repository Authors</Authors> <Authors>Repository Authors</Authors>
<Company>None</Company> <Company>None</Company>
<Product>ServicePoint2</Product> <Product>ServicePoint</Product>
<PackageTags>CCCB</PackageTags> <PackageTags>CCCB</PackageTags>
<Description> <Description>
C# bindings for the rust crate servicepoint2. You will need a suitable native shared library to use this. C# bindings for the rust crate servicepoint. You will need a suitable native shared library to use this.
For documentation, see the rust documentation: https://docs.rs/servicepoint2/latest/servicepoint2/. For documentation, see the rust documentation: https://docs.rs/servicepoint/latest/servicepoint/.
Note that this library is still in early development. Breaking changes are expected before 1.0 is released. Note that this library is still in early development. Breaking changes are expected before 1.0 is released.
</Description> </Description>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup> </PropertyGroup>
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build"> <!-- generate C# bindings -->
<Exec Command="cargo build --manifest-path ../../servicepoint2/Cargo.toml --all-features --release"/>
</Target>
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build">
<Exec Command="cargo build --manifest-path ../../servicepoint2/Cargo.toml --all-features"/>
</Target>
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build"> <Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build">
<Exec Command="cargo build --release"/> <Exec Command="cargo build --release"/>
</Target> </Target>
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build"> <Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build">
<Exec Command="cargo build"/> <Exec Command="cargo build"/>
</Target> </Target>
<!-- build native library to include in output -->
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build">
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint/Cargo.toml --release"/>
</Target>
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build">
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint/Cargo.toml"/>
</Target>
<!-- include native binary in output -->
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="../../target/debug/libservicepoint2.so" CopyToOutputDirectory="Always"> <Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
<Link>libservicepoint2.so</Link> <Link>libservicepoint_binding_c.so</Link>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Release'"> <ItemGroup Condition="'$(Configuration)'=='Release'">
<Content Include="../../target/release/libservicepoint2.so" CopyToOutputDirectory="Always"> <Content Include="../../../target/release/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
<Link>libservicepoint2.so</Link> <Link>libservicepoint_binding_c.so</Link>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- include link to source code at revision --> <!-- include link to source code at revision -->
<None Include="../../README.md" Pack="true" PackagePath="\"/> <None Include="../README.md" Pack="true" PackagePath="\"/>
<!-- add README.md to package --> <!-- add README.md to package -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="..\build.rs">
<Link>build.rs</Link>
</Content>
</ItemGroup>
</Project> </Project>

View file

@ -1,8 +1,8 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ServicePoint2; namespace ServicePoint;
public static class ServicePoint2Extensions public static class ServicePointExtensions
{ {
public static Packet IntoPacket(this Command command) public static Packet IntoPacket(this Command command)
{ {

View file

@ -1,6 +1,6 @@
namespace ServicePoint2; namespace ServicePoint;
public abstract class Sp2NativeInstance<T> public abstract class SpNativeInstance<T>
: IDisposable : IDisposable
where T : unmanaged where T : unmanaged
{ {
@ -16,7 +16,7 @@ public abstract class Sp2NativeInstance<T>
} }
} }
private protected unsafe Sp2NativeInstance(T* instance) private protected unsafe SpNativeInstance(T* instance)
{ {
ArgumentNullException.ThrowIfNull(instance); ArgumentNullException.ThrowIfNull(instance);
_instance = instance; _instance = instance;
@ -44,7 +44,7 @@ public abstract class Sp2NativeInstance<T>
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
~Sp2NativeInstance() ~SpNativeInstance()
{ {
ReleaseUnmanagedResources(); ReleaseUnmanagedResources();
} }

View file

@ -0,0 +1,28 @@
fn main() {
println!("cargo:rerun-if-changed=../servicepoint_binding_c/src");
println!("cargo:rerun-if-changed=build.rs");
csbindgen::Builder::default()
.input_extern_file("../servicepoint_binding_c/src/bit_vec.rs")
.input_extern_file("../servicepoint_binding_c/src/byte_grid.rs")
.input_extern_file("../servicepoint_binding_c/src/command.rs")
.input_extern_file("../servicepoint_binding_c/src/connection.rs")
.input_extern_file("../servicepoint_binding_c/src/pixel_grid.rs")
.input_extern_file("../servicepoint_binding_c/src/lib.rs")
.input_extern_file("../servicepoint_binding_c/src/c_slice.rs")
.input_extern_file("../servicepoint_binding_c/src/packet.rs")
.input_extern_file("../servicepoint/src/bit_vec.rs")
.input_extern_file("../servicepoint/src/byte_grid.rs")
.input_extern_file("../servicepoint/src/command.rs")
.input_extern_file("../servicepoint/src/connection.rs")
.input_extern_file("../servicepoint/src/pixel_grid.rs")
.input_extern_file("../servicepoint/src/lib.rs")
.input_extern_file("../servicepoint/src/packet.rs")
.input_extern_file("../servicepoint/src/compression_code.rs")
.csharp_dll_name("servicepoint_binding_c")
.csharp_namespace("ServicePoint.BindGen")
.csharp_use_nint_types(true)
.csharp_class_accessibility("public")
.csharp_generate_const_filter(|_| true)
.generate_csharp_file("ServicePoint/BindGen/ServicePoint.g.cs")
.unwrap();
}

View file

@ -1,5 +1,5 @@
using ServicePoint2; using ServicePoint;
using CompressionCode = ServicePoint2.BindGen.CompressionCode; using CompressionCode = ServicePoint.BindGen.CompressionCode;
using var connection = Connection.Open("127.0.0.1:2342"); using var connection = Connection.Open("127.0.0.1:2342");

View file

@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\servicepoint2-binding-cs\src\ServicePoint2.csproj"/> <ProjectReference Include="../../ServicePoint/ServicePoint.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -0,0 +1 @@

View file

@ -1,10 +0,0 @@
[package]
name = "announce"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
servicepoint2 = { path = "../../servicepoint2" }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"

View file

@ -1,12 +0,0 @@
[package]
name = "game_of_life"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
servicepoint2 = { path = "../../servicepoint2" }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"
rand = "0.8.5"

View file

@ -1,22 +0,0 @@
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
OUT_DIR := ${ROOT_DIR}/out
SP2_DIR := ${ROOT_DIR}/../../servicepoint2
SP2_INCLUDE := ${ROOT_DIR}/../../servicepoint2-binding-c
SP2_TARGET_RELEASE := ${SP2_DIR}/target/release
.PHONY: build run clean
all: ${OUT_DIR}/lang_c
run: ${OUT_DIR}/lang_c
out/lang_c
clean:
rm -r ${OUT_DIR} || true
rm -r ${SP2_TARGET_RELEASE} || true
${OUT_DIR}/lang_c: main.c
cd ${SP2_DIR} && cargo build --release --all-features
mkdir -p ${OUT_DIR}
gcc main.c -I ${SP2_INCLUDE} -L ${SP2_TARGET_RELEASE} -Wl,-Bstatic -lservicepoint2 -Wl,-Bdynamic -o ${OUT_DIR}/lang_c

View file

@ -1,19 +0,0 @@
#include <stdio.h>
#include "servicepoint2.h"
int main(void) {
sp2_Connection *connection = sp2_connection_open("localhost:2342");
if (connection == NULL)
return 1;
sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT);
sp2_pixel_grid_fill(pixels, true);
sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
sp2_Packet *packet = sp2_packet_from_command(command);
if (!sp2_connection_send(connection, packet))
return 1;
sp2_connection_dealloc(connection);
return 0;
}

View file

@ -1,10 +0,0 @@
[package]
name = "moving_line"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
servicepoint2 = { path = "../../servicepoint2" }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"

View file

@ -1,12 +0,0 @@
[package]
name = "random_brightness"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
servicepoint2 = { path = "../../servicepoint2" }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"
rand = "0.8"

View file

@ -1,10 +0,0 @@
[package]
name = "wiping_clear"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
servicepoint2 = { path = "../../servicepoint2" }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"

View file

@ -1,4 +0,0 @@
#!/usr/bin/env sh
# if the script crashes here, run `cargo install cbindgen`
cbindgen --config cbindgen.toml --clean --output servicepoint2.h ../servicepoint2

View file

@ -1,264 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "csbindgen"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf70eb656f35e0e6956cbde31c66431c53d8a546823489719099c71525767a9c"
dependencies = [
"regex",
"syn",
]
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lz4"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
dependencies = [
"libc",
"lz4-sys",
]
[[package]]
name = "lz4-sys"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "servicepoint-binding-cs"
version = "0.1.0"
dependencies = [
"csbindgen",
"servicepoint2",
]
[[package]]
name = "servicepoint2"
version = "0.2.0"
dependencies = [
"bzip2",
"flate2",
"log",
"lz4",
"zstd",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "zstd"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.10+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
dependencies = [
"cc",
"pkg-config",
]

View file

@ -1,13 +0,0 @@
[package]
name = "servicepoint-binding-cs"
version = "0.4.2"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
servicepoint2 = { path = "../servicepoint2", features = ["c_api"] }
[build-dependencies]
csbindgen = "1.8.0"

View file

@ -1,19 +0,0 @@
fn main() {
println!("cargo:rerun-if-changed=DOESNOTEXIST"); // rebuild every time
csbindgen::Builder::default()
.input_extern_file("../servicepoint2/src/bit_vec.rs")
.input_extern_file("../servicepoint2/src/byte_grid.rs")
.input_extern_file("../servicepoint2/src/command.rs")
.input_extern_file("../servicepoint2/src/compression_code.rs")
.input_extern_file("../servicepoint2/src/connection.rs")
.input_extern_file("../servicepoint2/src/pixel_grid.rs")
.input_extern_file("../servicepoint2/src/lib.rs")
.input_extern_file("../servicepoint2/src/c_slice.rs")
.input_extern_file("../servicepoint2/src/packet.rs")
.csharp_dll_name("servicepoint2")
.csharp_namespace("ServicePoint2.BindGen")
.csharp_use_nint_types(true)
.csharp_class_accessibility("public")
.generate_csharp_file("src/BindGen/ServicePoint2.g.cs")
.unwrap();
}

167
servicepoint2/Cargo.lock generated
View file

@ -1,167 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lz4"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1"
dependencies = [
"libc",
"lz4-sys",
]
[[package]]
name = "lz4-sys"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "servicepoint2"
version = "0.2.0"
dependencies = [
"bzip2",
"flate2",
"log",
"lz4",
"zstd",
]
[[package]]
name = "zstd"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.10+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
dependencies = [
"cc",
"pkg-config",
]

View file

@ -1,4 +1,11 @@
{pkgs ? import <nixpkgs> {}}: {pkgs ? import <nixpkgs> {}}:
pkgs.mkShell { pkgs.mkShell {
nativeBuildInputs = with pkgs.buildPackages; [ rustup pkg-config xe lzma cargo-tarpaulin ]; nativeBuildInputs = with pkgs.buildPackages; [
rustup
pkg-config
xe
lzma
cargo-tarpaulin
gnumake
];
} }