diff --git a/crates/servicepoint/src/bit_vec.rs b/crates/servicepoint/src/bit_vec.rs new file mode 100644 index 0000000..2ece813 --- /dev/null +++ b/crates/servicepoint/src/bit_vec.rs @@ -0,0 +1,10 @@ +/// A byte-packed vector of booleans. +/// +/// The implementation is provided by [bitvec]. +/// This is an alias for the specific type of [bitvec::BitVec] used in this crate. +pub type BitVec = bitvec::BitVec; + +pub mod bitvec { + //! Re-export of the used library [mod@bitvec]. + pub use bitvec::prelude::*; +} diff --git a/crates/servicepoint/src/bitmap.rs b/crates/servicepoint/src/bitmap.rs index 85983e5..807e2ad 100644 --- a/crates/servicepoint/src/bitmap.rs +++ b/crates/servicepoint/src/bitmap.rs @@ -5,7 +5,18 @@ use ::bitvec::order::Msb0; use ::bitvec::prelude::BitSlice; use ::bitvec::slice::IterMut; -/// A grid of pixels stored in packed bytes. +/// A fixed-size 2D grid of booleans. +/// +/// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels. +/// This means that no conversion is necessary for sending the data to the display. +/// +/// # Examples +/// +/// ```rust +/// use servicepoint::Bitmap; +/// let mut bitmap = Bitmap::new(4, 2); +/// +/// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct Bitmap { width: usize, @@ -27,7 +38,11 @@ impl Bitmap { /// /// - when the width is not dividable by 8 pub fn new(width: usize, height: usize) -> Self { - assert_eq!(width % 8, 0); + assert_eq!( + width % 8, + 0, + "width must be a multiple of 8, but is {width}" + ); Self { width, height, @@ -182,6 +197,26 @@ impl From for BitVec { } } +impl From<&ValueGrid> for Bitmap { + fn from(value: &ValueGrid) -> Self { + let mut result = Self::new(value.width(), value.height()); + for (mut to, from) in result.iter_mut().zip(value.iter()) { + *to = *from; + } + result + } +} + +impl From<&Bitmap> for ValueGrid { + fn from(value: &Bitmap) -> Self { + let mut result = Self::new(value.width(), value.height()); + for (to, from) in result.iter_mut().zip(value.iter()) { + *to = *from; + } + result + } +} + pub struct IterRows<'t> { bitmap: &'t Bitmap, row: usize, @@ -204,7 +239,7 @@ impl<'t> Iterator for IterRows<'t> { #[cfg(test)] mod tests { - use crate::{BitVec, Bitmap, DataRef, Grid}; + use crate::{BitVec, Bitmap, DataRef, Grid, ValueGrid}; #[test] fn fill() { @@ -304,4 +339,16 @@ mod tests { let bitvec: BitVec = grid.into(); assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]); } + + #[test] + fn from_bool_grid() { + let original = ValueGrid::load( + 8, + 1, + &[true, false, true, false, true, false, true, false], + ); + let converted = Bitmap::from(&original); + let reconverted = ValueGrid::from(&converted); + assert_eq!(original, reconverted); + } } diff --git a/crates/servicepoint/src/bitvec.rs b/crates/servicepoint/src/bitvec.rs deleted file mode 100644 index d512cf6..0000000 --- a/crates/servicepoint/src/bitvec.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use bitvec::prelude::*; - -/// An alias for the specific type of [bitvec::prelude::BitVec] used. -pub type BitVec = bitvec::prelude::BitVec; diff --git a/crates/servicepoint/src/byte_grid.rs b/crates/servicepoint/src/byte_grid.rs index b9a077a..0a7fdae 100644 --- a/crates/servicepoint/src/byte_grid.rs +++ b/crates/servicepoint/src/byte_grid.rs @@ -1,2 +1,4 @@ -/// A 2d grid of bytes - see [crate::ValueGrid]. -pub type ByteGrid = crate::value_grid::ValueGrid; +use crate::ValueGrid; + +/// A 2d grid of bytes - see [ValueGrid]. +pub type ByteGrid = ValueGrid; diff --git a/crates/servicepoint/src/lib.rs b/crates/servicepoint/src/lib.rs index 3b5150d..790751a 100644 --- a/crates/servicepoint/src/lib.rs +++ b/crates/servicepoint/src/lib.rs @@ -2,14 +2,17 @@ //! //! Your starting point is a [Connection] to the display. //! With a connection, you can send [Command]s. -//! When received, the display will update the state of the pixels. +//! When received, the display will update the state of its pixels. //! //! # Examples //! -//! ```rust -//! use servicepoint::{Command, CompressionCode, Grid, Bitmap}; +//! ### Clear display //! -//! let connection = servicepoint::Connection::open("127.0.0.1:2342") +//! ```rust +//! use servicepoint::{Connection, Command}; +//! +//! // establish a connection +//! let connection = Connection::open("127.0.0.1:2342") //! .expect("connection failed"); //! //! // turn off all pixels on display @@ -17,6 +20,8 @@ //! .expect("send failed"); //! ``` //! +//! ### Set all pixels to on +//! //! ```rust //! # use servicepoint::{Command, CompressionCode, Grid, Bitmap}; //! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed"); @@ -34,9 +39,24 @@ //! // send command to display //! connection.send(command).expect("send failed"); //! ``` +//! +//! ### Send text +//! +//! ```rust +//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap, CharGrid}; +//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed"); +//! // create a text grid +//! let mut grid = CharGrid::from("Hello\nCCCB?"); +//! // modify the grid +//! grid.set(grid.width() - 1, 1, '!'); +//! // create the command to send the data +//! let command = Command::Utf8Data(servicepoint::Origin::ZERO, grid); +//! // send command to display +//! connection.send(command).expect("send failed"); +//! ``` +pub use crate::bit_vec::{bitvec, BitVec}; pub use crate::bitmap::Bitmap; -pub use crate::bitvec::BitVec; pub use crate::brightness::Brightness; pub use crate::brightness_grid::BrightnessGrid; pub use crate::byte_grid::ByteGrid; @@ -55,8 +75,8 @@ pub use crate::value_grid::{ IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid, }; +mod bit_vec; mod bitmap; -mod bitvec; mod brightness; mod brightness_grid; mod byte_grid; diff --git a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h index ca57468..d9cbe57 100644 --- a/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h +++ b/crates/servicepoint_binding_c/examples/lang_c/include/servicepoint.h @@ -14,12 +14,12 @@ #define SP_BRIGHTNESS_LEVELS 12 /** - * see [Brightness::MAX] + * see [servicepoint::Brightness::MAX] */ #define SP_BRIGHTNESS_MAX 11 /** - * see [Brightness::MIN] + * see [servicepoint::Brightness::MIN] */ #define SP_BRIGHTNESS_MIN 0 @@ -131,6 +131,25 @@ typedef struct SPBitmap SPBitmap; */ typedef struct SPBrightnessGrid SPBrightnessGrid; +/** + * A C-wrapper for grid containing UTF-8 characters. + * + * As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers. + * + * The encoding is enforced in most cases by the rust standard library + * and will panic when provided with illegal characters. + * + * # Examples + * + * ```C + * CharGrid grid = sp_char_grid_new(4, 3); + * sp_char_grid_fill(grid, '?'); + * sp_char_grid_set(grid, 0, 0, '!'); + * sp_char_grid_free(grid); + * ``` + */ +typedef struct SPCharGrid SPCharGrid; + /** * A low-level display command. * @@ -367,6 +386,20 @@ SPBitmap *sp_bitmap_load(size_t width, SPBitmap *sp_bitmap_new(size_t width, size_t height); +/** + * Creates a new [SPBitmap] with a size matching the screen. + * + * returns: [SPBitmap] initialized to all pixels off. Will never return NULL. + * + * # Safety + * + * The caller has to make sure that: + * + * - the returned instance is freed in some way, either by using a consuming function or + * by explicitly calling [sp_bitmap_free]. + */ +SPBitmap *sp_bitmap_new_screen_sized(void); + /** * Sets the value of the specified position in the [SPBitmap]. * @@ -865,6 +898,196 @@ SPByteSlice sp_brightness_grid_unsafe_data_ref(SPBrightnessGrid *brightness_grid */ size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid); +/** + * Clones a [SPCharGrid]. + * + * Will never return NULL. + * + * # Panics + * + * - when `char_grid` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + * - `char_grid` is not written to concurrently + * - the returned instance is freed in some way, either by using a consuming function or + * by explicitly calling `sp_char_grid_free`. + */ +SPCharGrid *sp_char_grid_clone(const SPCharGrid *char_grid); + +/** + * Sets the value of all cells in the [SPCharGrid]. + * + * # Arguments + * + * - `char_grid`: instance to write to + * - `value`: the value to set all cells to + * + * # Panics + * + * - when `char_grid` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + * - `char_grid` is not written to or read from concurrently + */ +void sp_char_grid_fill(SPCharGrid *char_grid, uint32_t value); + +/** + * Deallocates a [SPCharGrid]. + * + * # Panics + * + * - when `char_grid` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + * - `char_grid` is not used concurrently or after char_grid call + * - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand] + * + * [SPCommand]: [crate::SPCommand] + */ +void sp_char_grid_free(SPCharGrid *char_grid); + +/** + * Gets the current value at the specified position. + * + * # Arguments + * + * - `char_grid`: instance to read from + * - `x` and `y`: position of the cell to read + * + * # Panics + * + * - when `char_grid` is NULL + * - when accessing `x` or `y` out of bounds + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + * - `char_grid` is not written to concurrently + */ +uint32_t sp_char_grid_get(const SPCharGrid *char_grid, size_t x, size_t y); + +/** + * Gets the height of the [SPCharGrid] instance. + * + * # Arguments + * + * - `char_grid`: instance to read from + * + * # Panics + * + * - when `char_grid` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + */ +size_t sp_char_grid_height(const SPCharGrid *char_grid); + +/** + * Loads a [SPCharGrid] with the specified dimensions from the provided data. + * + * Will never return NULL. + * + * # Panics + * + * - when `data` is NULL + * - when the provided `data_length` does not match `height` and `width` + * - when `data` is not valid UTF-8 + * + * # Safety + * + * The caller has to make sure that: + * + * - `data` points to a valid memory location of at least `data_length` + * bytes in size. + * - the returned instance is freed in some way, either by using a consuming function or + * by explicitly calling `sp_char_grid_free`. + */ +SPCharGrid *sp_char_grid_load(size_t width, + size_t height, + const uint8_t *data, + size_t data_length); + +/** + * Creates a new [SPCharGrid] with the specified dimensions. + * + * returns: [SPCharGrid] initialized to 0. Will never return NULL. + * + * # Safety + * + * The caller has to make sure that: + * + * - the returned instance is freed in some way, either by using a consuming function or + * by explicitly calling `sp_char_grid_free`. + */ +SPCharGrid *sp_char_grid_new(size_t width, + size_t height); + +/** + * Sets the value of the specified position in the [SPCharGrid]. + * + * # Arguments + * + * - `char_grid`: instance to write to + * - `x` and `y`: position of the cell + * - `value`: the value to write to the cell + * + * returns: old value of the cell + * + * # Panics + * + * - when `char_grid` is NULL + * - when accessing `x` or `y` out of bounds + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPBitVec] + * - `char_grid` is not written to or read from concurrently + * + * [SPBitVec]: [crate::SPBitVec] + */ +void sp_char_grid_set(SPCharGrid *char_grid, + size_t x, + size_t y, + uint32_t value); + +/** + * Gets the width of the [SPCharGrid] instance. + * + * # Arguments + * + * - `char_grid`: instance to read from + * + * # Panics + * + * - when `char_grid` is NULL + * + * # Safety + * + * The caller has to make sure that: + * + * - `char_grid` points to a valid [SPCharGrid] + */ +size_t sp_char_grid_width(const SPCharGrid *char_grid); + /** * Set pixel data starting at the pixel offset on screen. * @@ -1101,7 +1324,7 @@ SPCommand *sp_command_clear(void); SPCommand *sp_command_clone(const SPCommand *command); /** - * Show text on the screen. + * Show codepage 437 encoded text on the screen. * * The passed [SPCp437Grid] gets consumed. * @@ -1201,6 +1424,30 @@ SPCommand *sp_command_hard_reset(void); */ SPCommand *sp_command_try_from_packet(SPPacket *packet); +/** + * Show UTF-8 encoded text on the screen. + * + * The passed [SPCharGrid] gets consumed. + * + * Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL. + * + * # Panics + * + * - when `grid` is null + * + * # Safety + * + * The caller has to make sure that: + * + * - `grid` points to a valid instance of [SPCharGrid] + * - `grid` is not used concurrently or after this call + * - the returned [SPCommand] instance is freed in some way, either by using a consuming function or + * by explicitly calling `sp_command_free`. + */ +SPCommand *sp_command_utf8_data(size_t x, + size_t y, + SPCharGrid *grid); + /** * Creates a new instance of [SPConnection] for testing that does not actually send anything. * @@ -1528,7 +1775,7 @@ SPPacket *sp_packet_clone(const SPPacket *packet); * * # Panics * - * - when `sp_packet_free` is NULL + * - when `packet` is NULL * * # Safety * @@ -1560,6 +1807,40 @@ void sp_packet_free(SPPacket *packet); */ SPPacket *sp_packet_from_command(SPCommand *command); +/** + * Creates a raw [SPPacket] from parts. + * + * # Arguments + * + * - `command_code` specifies which command this packet contains + * - `a`, `b`, `c` and `d` are command-specific header values + * - `payload` is the optional data that is part of the command + * - `payload_len` is the size of the payload + * + * returns: new instance. Will never return null. + * + * # Panics + * + * - when `payload` is null, but `payload_len` is not zero + * - when `payload_len` is zero, but `payload` is nonnull + * + * # Safety + * + * The caller has to make sure that: + * + * - `payload` points to a valid memory region of at least `payload_len` bytes + * - `payload` is not written to concurrently + * - the returned [SPPacket] instance is freed in some way, either by using a consuming function or + * by explicitly calling [sp_packet_free]. + */ +SPPacket *sp_packet_from_parts(uint16_t command_code, + uint16_t a, + uint16_t b, + uint16_t c, + uint16_t d, + const uint8_t *payload, + size_t payload_len); + /** * Tries to load a [SPPacket] from the passed array with the specified length. * diff --git a/crates/servicepoint_binding_c/src/char_grid.rs b/crates/servicepoint_binding_c/src/char_grid.rs new file mode 100644 index 0000000..dfaf225 --- /dev/null +++ b/crates/servicepoint_binding_c/src/char_grid.rs @@ -0,0 +1,263 @@ +//! C functions for interacting with [SPCharGrid]s +//! +//! prefix `sp_char_grid_` + +use servicepoint::Grid; +use std::ptr::NonNull; + +/// A C-wrapper for grid containing UTF-8 characters. +/// +/// As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers. +/// +/// The encoding is enforced in most cases by the rust standard library +/// and will panic when provided with illegal characters. +/// +/// # Examples +/// +/// ```C +/// CharGrid grid = sp_char_grid_new(4, 3); +/// sp_char_grid_fill(grid, '?'); +/// sp_char_grid_set(grid, 0, 0, '!'); +/// sp_char_grid_free(grid); +/// ``` +pub struct SPCharGrid(pub(crate) servicepoint::CharGrid); + +impl Clone for SPCharGrid { + fn clone(&self) -> Self { + SPCharGrid(self.0.clone()) + } +} + +/// Creates a new [SPCharGrid] with the specified dimensions. +/// +/// returns: [SPCharGrid] initialized to 0. Will never return NULL. +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - the returned instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_char_grid_free`. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_new( + width: usize, + height: usize, +) -> NonNull { + let result = + Box::new(SPCharGrid(servicepoint::CharGrid::new(width, height))); + NonNull::from(Box::leak(result)) +} + +/// Loads a [SPCharGrid] with the specified dimensions from the provided data. +/// +/// Will never return NULL. +/// +/// # Panics +/// +/// - when `data` is NULL +/// - when the provided `data_length` does not match `height` and `width` +/// - when `data` is not valid UTF-8 +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `data` points to a valid memory location of at least `data_length` +/// bytes in size. +/// - the returned instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_char_grid_free`. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_load( + width: usize, + height: usize, + data: *const u8, + data_length: usize, +) -> NonNull { + assert!(data.is_null()); + let data = std::slice::from_raw_parts(data, data_length); + let result = Box::new(SPCharGrid( + servicepoint::CharGrid::load_utf8(width, height, data.to_vec()) + .unwrap(), + )); + NonNull::from(Box::leak(result)) +} + +/// Clones a [SPCharGrid]. +/// +/// Will never return NULL. +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +/// - `char_grid` is not written to concurrently +/// - the returned instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_char_grid_free`. +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_clone( + char_grid: *const SPCharGrid, +) -> NonNull { + assert!(!char_grid.is_null()); + let result = Box::new((*char_grid).clone()); + NonNull::from(Box::leak(result)) +} + +/// Deallocates a [SPCharGrid]. +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +/// - `char_grid` is not used concurrently or after char_grid call +/// - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand] +/// +/// [SPCommand]: [crate::SPCommand] +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_free(char_grid: *mut SPCharGrid) { + assert!(!char_grid.is_null()); + _ = Box::from_raw(char_grid); +} + +/// Gets the current value at the specified position. +/// +/// # Arguments +/// +/// - `char_grid`: instance to read from +/// - `x` and `y`: position of the cell to read +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// - when accessing `x` or `y` out of bounds +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +/// - `char_grid` is not written to concurrently +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_get( + char_grid: *const SPCharGrid, + x: usize, + y: usize, +) -> u32 { + assert!(!char_grid.is_null()); + (*char_grid).0.get(x, y) as u32 +} + +/// Sets the value of the specified position in the [SPCharGrid]. +/// +/// # Arguments +/// +/// - `char_grid`: instance to write to +/// - `x` and `y`: position of the cell +/// - `value`: the value to write to the cell +/// +/// returns: old value of the cell +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// - when accessing `x` or `y` out of bounds +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPBitVec] +/// - `char_grid` is not written to or read from concurrently +/// +/// [SPBitVec]: [crate::SPBitVec] +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_set( + char_grid: *mut SPCharGrid, + x: usize, + y: usize, + value: u32, +) { + assert!(!char_grid.is_null()); + (*char_grid).0.set(x, y, char::from_u32(value).unwrap()); +} + +/// Sets the value of all cells in the [SPCharGrid]. +/// +/// # Arguments +/// +/// - `char_grid`: instance to write to +/// - `value`: the value to set all cells to +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +/// - `char_grid` is not written to or read from concurrently +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_fill( + char_grid: *mut SPCharGrid, + value: u32, +) { + assert!(!char_grid.is_null()); + (*char_grid).0.fill(char::from_u32(value).unwrap()); +} + +/// Gets the width of the [SPCharGrid] instance. +/// +/// # Arguments +/// +/// - `char_grid`: instance to read from +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_width( + char_grid: *const SPCharGrid, +) -> usize { + assert!(!char_grid.is_null()); + (*char_grid).0.width() +} + +/// Gets the height of the [SPCharGrid] instance. +/// +/// # Arguments +/// +/// - `char_grid`: instance to read from +/// +/// # Panics +/// +/// - when `char_grid` is NULL +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `char_grid` points to a valid [SPCharGrid] +#[no_mangle] +pub unsafe extern "C" fn sp_char_grid_height( + char_grid: *const SPCharGrid, +) -> usize { + assert!(!char_grid.is_null()); + (*char_grid).0.height() +} diff --git a/crates/servicepoint_binding_c/src/command.rs b/crates/servicepoint_binding_c/src/command.rs index 3da0cbb..f7e50ea 100644 --- a/crates/servicepoint_binding_c/src/command.rs +++ b/crates/servicepoint_binding_c/src/command.rs @@ -5,8 +5,8 @@ use std::ptr::{null_mut, NonNull}; use crate::{ - SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid, - SPPacket, + SPBitVec, SPBitmap, SPBrightnessGrid, SPCharGrid, SPCompressionCode, + SPCp437Grid, SPPacket, }; /// A low-level display command. @@ -366,7 +366,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor( NonNull::from(Box::leak(result)) } -/// Show text on the screen. +/// Show codepage 437 encoded text on the screen. /// /// The passed [SPCp437Grid] gets consumed. /// @@ -399,6 +399,39 @@ pub unsafe extern "C" fn sp_command_cp437_data( NonNull::from(Box::leak(result)) } +/// Show UTF-8 encoded text on the screen. +/// +/// The passed [SPCharGrid] gets consumed. +/// +/// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL. +/// +/// # Panics +/// +/// - when `grid` is null +/// +/// # Safety +/// +/// The caller has to make sure that: +/// +/// - `grid` points to a valid instance of [SPCharGrid] +/// - `grid` is not used concurrently or after this call +/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or +/// by explicitly calling `sp_command_free`. +#[no_mangle] +pub unsafe extern "C" fn sp_command_utf8_data( + x: usize, + y: usize, + grid: *mut SPCharGrid, +) -> NonNull { + assert!(!grid.is_null()); + let grid = *Box::from_raw(grid); + let result = Box::new(SPCommand(servicepoint::Command::Utf8Data( + servicepoint::Origin::new(x, y), + grid.0, + ))); + NonNull::from(Box::leak(result)) +} + /// Sets a window of pixels to the specified values. /// /// The passed [SPBitmap] gets consumed. diff --git a/crates/servicepoint_binding_c/src/lib.rs b/crates/servicepoint_binding_c/src/lib.rs index 42442f5..887fb40 100644 --- a/crates/servicepoint_binding_c/src/lib.rs +++ b/crates/servicepoint_binding_c/src/lib.rs @@ -29,6 +29,7 @@ pub use crate::bitmap::*; pub use crate::bitvec::*; pub use crate::brightness_grid::*; pub use crate::byte_slice::*; +pub use crate::char_grid::*; pub use crate::command::*; pub use crate::connection::*; pub use crate::constants::*; @@ -39,6 +40,7 @@ mod bitmap; mod bitvec; mod brightness_grid; mod byte_slice; +mod char_grid; mod command; mod connection; mod constants;