From 98e8a6d6392501373fc53250b51ad505cbb2979f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 12 May 2024 17:15:30 +0200 Subject: [PATCH] add c api --- Cargo.lock | 290 +++++++++++++++++++++++++++++- examples/game_of_life/src/main.rs | 5 +- examples/moving_line/src/main.rs | 5 +- examples/wiping_clear/src/main.rs | 5 +- servicepoint2/Cargo.toml | 3 + servicepoint2/bindings.h | 186 +++++++++++++++++++ servicepoint2/build.rs | 16 ++ servicepoint2/src/bit_vec.rs | 51 ++++++ servicepoint2/src/byte_grid.rs | 57 ++++++ servicepoint2/src/command.rs | 126 ++++++++++++- servicepoint2/src/connection.rs | 39 ++++ servicepoint2/src/lib.rs | 2 +- servicepoint2/src/packet.rs | 13 ++ servicepoint2/src/pixel_grid.rs | 57 ++++++ 14 files changed, 832 insertions(+), 23 deletions(-) create mode 100644 servicepoint2/bindings.h create mode 100644 servicepoint2/build.rs diff --git a/Cargo.lock b/Cargo.lock index 97bd3f9..03c62af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ name = "announce" version = "0.1.0" dependencies = [ - "clap", + "clap 4.5.4", "env_logger", "servicepoint2", ] @@ -75,6 +75,35 @@ dependencies = [ "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]] name = "bzip2" version = "0.4.4" @@ -96,6 +125,25 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cbindgen" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.97" @@ -113,6 +161,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "clap" version = "4.5.4" @@ -131,8 +194,8 @@ checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", - "clap_lex", - "strsim", + "clap_lex 0.7.0", + "strsim 0.11.1", ] [[package]] @@ -141,10 +204,19 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.63", +] + +[[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]] @@ -191,6 +263,22 @@ dependencies = [ "log", ] +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "flate2" version = "1.0.30" @@ -205,7 +293,7 @@ dependencies = [ name = "game_of_life" version = "0.1.0" dependencies = [ - "clap", + "clap 4.5.4", "env_logger", "rand", "servicepoint2", @@ -222,24 +310,61 @@ dependencies = [ "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]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "jobserver" version = "0.1.31" @@ -255,6 +380,12 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "log" version = "0.4.21" @@ -300,7 +431,7 @@ dependencies = [ name = "moving_line" version = "0.1.0" dependencies = [ - "clap", + "clap 4.5.4", "env_logger", "servicepoint2", ] @@ -311,6 +442,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "pkg-config" version = "0.3.30" @@ -375,7 +512,7 @@ dependencies = [ name = "random_brightness" version = "0.1.0" dependencies = [ - "clap", + "clap 4.5.4", "env_logger", "rand", "servicepoint2", @@ -410,23 +547,91 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[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 = "servicepoint2" version = "0.1.3" dependencies = [ "bzip2", + "cbindgen", "flate2", "log", "lz4", "zstd", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[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 = "syn" version = "2.0.63" @@ -438,6 +643,42 @@ dependencies = [ "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]] name = "unicode-ident" version = "1.0.12" @@ -456,6 +697,37 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.52.0" @@ -533,7 +805,7 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" name = "wiping_clear" version = "0.1.0" dependencies = [ - "clap", + "clap 4.5.4", "env_logger", "servicepoint2", ] diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 1d99298..ad2b56a 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -4,8 +4,7 @@ use std::time::Duration; use clap::Parser; use rand::{distributions, Rng}; -use servicepoint2::{Connection, Origin, PixelGrid}; -use servicepoint2::Command::BitmapLinearWin; +use servicepoint2::{Command, Connection, Origin, PixelGrid}; #[derive(Parser, Debug)] struct Cli { @@ -24,7 +23,7 @@ fn main() { loop { connection - .send(BitmapLinearWin(Origin::top_left(), field.clone())) + .send(Command::BitmapLinearWin(Origin::top_left(), field.clone())) .expect("could not send"); thread::sleep(Duration::from_millis(14)); field = iteration(field); diff --git a/examples/moving_line/src/main.rs b/examples/moving_line/src/main.rs index bada1a6..023f5da 100644 --- a/examples/moving_line/src/main.rs +++ b/examples/moving_line/src/main.rs @@ -3,8 +3,7 @@ use std::time::Duration; use clap::Parser; -use servicepoint2::Command::BitmapLinearWin; -use servicepoint2::{Connection, Origin, PixelGrid, PIXEL_HEIGHT, PIXEL_WIDTH}; +use servicepoint2::{Command, Connection, Origin, PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid}; #[derive(Parser, Debug)] struct Cli { @@ -25,7 +24,7 @@ fn main() { pixels.set((y + x_offset) % PIXEL_WIDTH as usize, y, true); } connection - .send(BitmapLinearWin(Origin::top_left(), pixels.clone())) + .send(Command::BitmapLinearWin(Origin::top_left(), pixels.clone())) .unwrap(); thread::sleep(Duration::from_millis(14)); } diff --git a/examples/wiping_clear/src/main.rs b/examples/wiping_clear/src/main.rs index 5837541..14ed9a4 100644 --- a/examples/wiping_clear/src/main.rs +++ b/examples/wiping_clear/src/main.rs @@ -3,9 +3,8 @@ use std::time::Duration; use clap::Parser; -use servicepoint2::Command::BitmapLinearAnd; use servicepoint2::{ - BitVec, CompressionCode, Connection, PixelGrid, PIXEL_HEIGHT, PIXEL_WIDTH, + BitVec, Command, CompressionCode, Connection, PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid }; #[derive(Parser, Debug)] @@ -37,7 +36,7 @@ fn main() { let bit_vec = BitVec::from(&*pixel_data); connection - .send(BitmapLinearAnd(0, bit_vec, CompressionCode::Gz)) + .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Gz)) .unwrap(); thread::sleep(sleep_duration); } diff --git a/servicepoint2/Cargo.toml b/servicepoint2/Cargo.toml index f77704c..a966e40 100644 --- a/servicepoint2/Cargo.toml +++ b/servicepoint2/Cargo.toml @@ -23,3 +23,6 @@ compression-bz = ["dep:bzip2", "compression"] compression-lz = ["dep:lz4", "compression"] compression-zs = ["dep:zstd", "compression"] compression = [] + +[build-dependencies] +cbindgen = "0.26.0" diff --git a/servicepoint2/bindings.h b/servicepoint2/bindings.h new file mode 100644 index 0000000..0c4cb8c --- /dev/null +++ b/servicepoint2/bindings.h @@ -0,0 +1,186 @@ +#include +#include +#include +#include + +/** + * size of a single tile in one dimension + */ +#define sp2_TILE_SIZE 8 + +/** + * tile count in the x-direction + */ +#define sp2_TILE_WIDTH 56 + +/** + * tile count in the y-direction + */ +#define sp2_TILE_HEIGHT 20 + +/** + * screen width in pixels + */ +#define sp2_PIXEL_WIDTH (sp2_TILE_WIDTH * sp2_TILE_SIZE) + +/** + * screen height in pixels + */ +#define sp2_PIXEL_HEIGHT (sp2_TILE_HEIGHT * sp2_TILE_SIZE) + +/** + * pixel count on whole screen + */ +#define sp2_PIXEL_COUNT ((uintptr_t)sp2_PIXEL_WIDTH * (uintptr_t)sp2_PIXEL_HEIGHT) + +/** + * Specifies the kind of compression to use. Availability depends on features. + */ +enum sp2_CompressionCode { + Uncompressed = 0, + Gz = 26490, + Bz = 25210, + Lz = 27770, + Zs = 31347, +}; +typedef uint16_t sp2_CompressionCode; + +/** + * A vector of bits + */ +typedef struct sp2_BitVec sp2_BitVec; + +/** + * A grid of bytes + */ +typedef struct sp2_ByteGrid sp2_ByteGrid; + +/** + * A command to send to the display. + */ +typedef struct sp2_Command sp2_Command; + +/** + * A connection to the display. + */ +typedef struct sp2_Connection sp2_Connection; + +/** + * A grid of pixels stored in packed bytes. + */ +typedef struct sp2_PixelGrid sp2_PixelGrid; + +typedef uint8_t sp2_Brightness; + +typedef uint16_t sp2_Offset; + +/** + * Tries to load a command from the passed array with the specified length. + * + * returns: NULL in case of an error, pointer to the allocated command otherwise + */ +struct sp2_Command *command_try_load(const uint8_t *data, uintptr_t length); + +/** + * Clones a `Command` instance + */ +struct sp2_Command *command_clone(const struct sp2_Command *original); + +/** + * Allocates a new `Command::Clear` instance + */ +struct sp2_Command *command_clear(void); + +/** + * Allocates a new `Command::HardReset` instance + */ +struct sp2_Command *command_hard_reset(void); + +/** + * Allocates a new `Command::FadeOut` instance + */ +struct sp2_Command *command_fade_out(void); + +/** + * Allocates a new `Command::Brightness` instance + */ +struct sp2_Command *command_brightness(sp2_Brightness brightness); + +/** + * Allocates a new `Command::CharBrightness` instance. + * The passed `ByteGrid` gets deallocated in the process. + */ +struct sp2_Command *command_char_brightness(uint16_t x, uint16_t y, struct sp2_ByteGrid *byte_grid); + +/** + * Allocates a new `Command::BitmapLinear` instance. + * The passed `BitVec` gets deallocated in the process. + */ +struct sp2_Command *command_bitmap_linear(sp2_Offset offset, + struct sp2_BitVec *bit_vec, + sp2_CompressionCode compression); + +/** + * Allocates a new `Command::BitmapLinearAnd` instance. + * The passed `BitVec` gets deallocated in the process. + */ +struct sp2_Command *command_bitmap_linear_and(sp2_Offset offset, + struct sp2_BitVec *bit_vec, + sp2_CompressionCode compression); + +/** + * Allocates a new `Command::BitmapLinearOr` instance. + * The passed `BitVec` gets deallocated in the process. + */ +struct sp2_Command *command_bitmap_linear_or(sp2_Offset offset, + struct sp2_BitVec *bit_vec, + sp2_CompressionCode compression); + +/** + * Allocates a new `Command::BitmapLinearXor` instance. + * The passed `BitVec` gets deallocated in the process. + */ +struct sp2_Command *command_bitmap_linear_xor(sp2_Offset offset, + struct sp2_BitVec *bit_vec, + sp2_CompressionCode compression); + +/** + * Allocates a new `Command::Cp437Data` instance. + * The passed `ByteGrid` gets deallocated in the process. + */ +struct sp2_Command *command_cp437_data(uint16_t x, uint16_t y, struct sp2_ByteGrid *byte_grid); + +/** + * Allocates a new `Command::BitmapLinearWin` instance. + * The passed `PixelGrid` gets deallocated in the process. + */ +struct sp2_Command *command_bitmap_linear_win(uint16_t x, + uint16_t y, + struct sp2_PixelGrid *byte_grid); + +/** + * 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. + */ +void command_dealloc(struct sp2_Command *ptr); + +/** + * 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 + */ +struct sp2_Connection *connection_open(const char *host); + +/** + * Sends the command instance. The instance is consumed / destroyed and cannot be used after this call. + */ +bool connection_send(const struct sp2_Connection *connection, + struct sp2_Command *command_ptr); + +/** + * Closes and deallocates a connection instance + */ +void connection_dealloc(struct sp2_Connection *ptr); diff --git a/servicepoint2/build.rs b/servicepoint2/build.rs new file mode 100644 index 0000000..5088a95 --- /dev/null +++ b/servicepoint2/build.rs @@ -0,0 +1,16 @@ +extern crate cbindgen; + +use std::env; +use cbindgen::Language; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_item_prefix("sp2_") + .with_language(Language::C) + .generate() + .expect("Unable to generate bindings") + .write_to_file("bindings.h"); +} \ No newline at end of file diff --git a/servicepoint2/src/bit_vec.rs b/servicepoint2/src/bit_vec.rs index 2c01fea..8451591 100644 --- a/servicepoint2/src/bit_vec.rs +++ b/servicepoint2/src/bit_vec.rs @@ -105,3 +105,54 @@ impl std::fmt::Debug for BitVec { .finish() } } + +#[allow(unused)] +pub mod c_api { + use crate::BitVec; + + /// Creates a new `BitVec` instance. + /// The returned instance has to be freed with `bit_vec_dealloc`. + pub unsafe extern "C" fn 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`. + pub unsafe extern "C" fn byte_grid_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`. + pub unsafe extern "C" fn byte_grid_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. + pub unsafe extern "C" fn byte_grid_dealloc(this: *mut BitVec) { + _ = Box::from_raw(this); + } + + /// Gets the value of a bit from the `BitVec`. + pub unsafe extern "C" fn byte_grid_get(this: *const BitVec, index: usize) -> bool { + (*this).get(index) + } + + /// Sets the value of a bit in the `BitVec`. + pub unsafe extern "C" fn byte_grid_set(this: *mut BitVec, index: usize, value: bool) -> bool { + (*this).set(index, value) + } + + /// Sets the value of all bits in the `BitVec`. + pub unsafe extern "C" fn byte_grid_fill(this: *mut BitVec, value: bool) { + (*this).fill(value) + } + + /// Gets the length of the `BitVec` in bits. + pub unsafe extern "C" fn byte_grid_len(this: *const BitVec) -> usize{ + (*this).len() + } +} diff --git a/servicepoint2/src/byte_grid.rs b/servicepoint2/src/byte_grid.rs index 38c118c..c5f10cf 100644 --- a/servicepoint2/src/byte_grid.rs +++ b/servicepoint2/src/byte_grid.rs @@ -57,3 +57,60 @@ impl Into> for ByteGrid { self.data } } + +#[allow(unused)] +pub mod c_api +{ + use crate::{ByteGrid, PixelGrid}; + + /// Creates a new `ByteGrid` instance. + /// The returned instance has to be freed with `byte_grid_dealloc`. + pub unsafe extern "C" fn 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`. + pub unsafe extern "C" fn 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`. + pub unsafe extern "C" fn 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. + pub unsafe extern "C" fn byte_grid_dealloc(this: *mut ByteGrid) { + _ = Box::from_raw(this); + } + + /// Get the current value at the specified position + pub unsafe extern "C" fn byte_grid_get(this: *const ByteGrid, x: usize, y: usize) -> u8 { + (*this).get(x, y) + } + + /// Sets the current value at the specified position + pub unsafe extern "C" fn 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 + pub unsafe extern "C" fn byte_grid_fill(this: *mut ByteGrid, value: u8) { + (*this).fill(value); + } + + /// Gets the width in pixels of the `ByteGrid` instance. + pub unsafe extern "C" fn pixel_grid_width(this: *const PixelGrid) -> usize { + (*this).width + } + + /// Gets the height in pixels of the `ByteGrid` instance. + pub unsafe extern "C" fn pixel_grid_height(this: *const PixelGrid) -> usize { + (*this).height + } +} \ No newline at end of file diff --git a/servicepoint2/src/command.rs b/servicepoint2/src/command.rs index aec6c36..85c5437 100644 --- a/servicepoint2/src/command.rs +++ b/servicepoint2/src/command.rs @@ -1,8 +1,8 @@ -use crate::compression::{into_compressed, into_decompressed}; use crate::{ BitVec, ByteGrid, CommandCode, CompressionCode, Header, Packet, PixelGrid, TILE_SIZE, }; +use crate::compression::{into_compressed, into_decompressed}; /// An origin marks the top left position of a window sent to the display. #[derive(Debug, Clone, Copy)] @@ -18,12 +18,12 @@ impl Origin { #[derive(Debug, Clone, Copy)] pub struct Size(pub u16, pub u16); -type Offset = u16; +pub type Offset = u16; -type Brightness = u8; +pub type Brightness = u8; /// A command to send to the display. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Command { /// Set all pixels to the off state Clear, @@ -315,3 +315,121 @@ fn packet_into_linear_bitmap( }; Ok((BitVec::from(&*payload), sub)) } + +#[allow(unused)] +pub mod c_api +{ + use std::ptr::null_mut; + + use crate::{BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet, PixelGrid}; + + /// Tries to load a command from the passed array with the specified length. + /// + /// returns: NULL in case of an error, pointer to the allocated command otherwise + #[no_mangle] + pub unsafe extern "C" fn command_try_load(data: *const u8, length: usize) -> *mut Command { + let data = std::slice::from_raw_parts(data, length); + let packet = match Packet::try_from(data) { + Err(_) => return null_mut(), + Ok(packet) => packet + }; + let command = match Command::try_from(packet) { + Err(_) => return null_mut(), + Ok(command) => command, + }; + Box::into_raw(Box::new(command)) + } + + /// Clones a `Command` instance + #[no_mangle] + pub unsafe extern "C" fn 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 command_clear() -> *mut Command { + Box::into_raw(Box::new(Command::Clear)) + } + + /// Allocates a new `Command::HardReset` instance + #[no_mangle] + pub unsafe extern "C" fn 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 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 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 command_char_brightness(x: u16, y: u16, 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 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 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 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 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 command_cp437_data(x: u16, y: u16, 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 command_bitmap_linear_win(x: u16, y: u16, byte_grid: *mut PixelGrid) -> *mut Command { + let byte_grid = *Box::from_raw(byte_grid); + Box::into_raw(Box::new(Command::BitmapLinearWin(Origin(x, y), byte_grid))) + } + + /// 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 command_dealloc(ptr: *mut Command) { + _ = Box::from_raw(ptr); + } +} \ No newline at end of file diff --git a/servicepoint2/src/connection.rs b/servicepoint2/src/connection.rs index e296939..183e519 100644 --- a/servicepoint2/src/connection.rs +++ b/servicepoint2/src/connection.rs @@ -64,3 +64,42 @@ impl Connection { Ok(()) } } + +pub mod c_api +{ + use std::ffi::{c_char, CStr}; + use std::ptr::null_mut; + + use crate::{Command, Connection}; + + /// 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 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 + }; + + let boxed = Box::new(connection); + Box::into_raw(boxed) + } + + /// Sends the command instance. The instance is consumed / destroyed and cannot be used after this call. + #[no_mangle] + pub unsafe extern "C" fn connection_send(connection: *const Connection, command_ptr: *mut Command) -> bool{ + let command = Box::from_raw(command_ptr); + (*connection).send(*command).is_ok() + } + + /// Closes and deallocates a connection instance + #[no_mangle] + pub unsafe extern "C" fn connection_dealloc(ptr: *mut Connection) { + _ = Box::from_raw(ptr); + } +} \ No newline at end of file diff --git a/servicepoint2/src/lib.rs b/servicepoint2/src/lib.rs index a342a35..0a72250 100644 --- a/servicepoint2/src/lib.rs +++ b/servicepoint2/src/lib.rs @@ -1,6 +1,6 @@ pub use crate::bit_vec::BitVec; pub use crate::byte_grid::ByteGrid; -pub use crate::command::{Command, Origin, Size}; +pub use crate::command::{Brightness, Command, Offset, Origin, Size}; pub use crate::command_code::CommandCode; pub use crate::compression_code::CompressionCode; pub use crate::connection::Connection; diff --git a/servicepoint2/src/packet.rs b/servicepoint2/src/packet.rs index f54e601..4bba32b 100644 --- a/servicepoint2/src/packet.rs +++ b/servicepoint2/src/packet.rs @@ -45,3 +45,16 @@ impl From for Packet { Packet(Header(mode, a, b, c, d), payload) } } + +impl From<&[u8]> for Packet { + fn from(value: &[u8]) -> Self { + let mode = u16_from_be_slice(&value[0..=1]); + let a = u16_from_be_slice(&value[2..=3]); + let b = u16_from_be_slice(&value[4..=5]); + let c = u16_from_be_slice(&value[6..=7]); + let d = u16_from_be_slice(&value[8..=9]); + let payload = value[10..].to_vec(); + + Packet(Header(mode, a, b, c, d), payload) + } +} diff --git a/servicepoint2/src/pixel_grid.rs b/servicepoint2/src/pixel_grid.rs index 68e7af1..4092cd2 100644 --- a/servicepoint2/src/pixel_grid.rs +++ b/servicepoint2/src/pixel_grid.rs @@ -83,3 +83,60 @@ impl Into> for PixelGrid { self.bit_vec.into() } } + +#[allow(unused)] +pub mod c_api +{ + use crate::PixelGrid; + + /// Creates a new `PixelGrid` instance. + /// The returned instance has to be freed with `pixel_grid_dealloc`. + pub unsafe extern "C" fn 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`. + pub unsafe extern "C" fn 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`. + pub unsafe extern "C" fn 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. + pub unsafe extern "C" fn pixel_grid_dealloc(this: *mut PixelGrid) { + _ = Box::from_raw(this); + } + + /// Get the current value at the specified position + pub unsafe extern "C" fn pixel_grid_get(this: *const PixelGrid, x: usize, y: usize) -> bool { + (*this).get(x, y) + } + + /// Sets the current value at the specified position + pub unsafe extern "C" fn 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 + pub unsafe extern "C" fn pixel_grid_fill(this: *mut PixelGrid, value: bool) { + (*this).fill(value); + } + + /// Gets the width in pixels of the `PixelGrid` instance. + pub unsafe extern "C" fn pixel_grid_width(this: *const PixelGrid) -> usize { + (*this).width + } + + /// Gets the height in pixels of the `PixelGrid` instance. + pub unsafe extern "C" fn pixel_grid_height(this: *const PixelGrid) -> usize { + (*this).height + } +}