add command code constants, send header

This commit is contained in:
Vinzenz Schroeter 2025-04-12 21:47:23 +02:00
commit a3e2dcb3c2
7 changed files with 141 additions and 60 deletions

View file

@ -9,7 +9,7 @@ use std::ptr::NonNull;
///
/// # Examples
/// ```C
/// UdpConnection connection = sp_connection_open("127.0.0.1:2342");
/// UdpConnection connection = sp_udp_open("127.0.0.1:2342");
/// if (connection == NULL)
/// return 1;
///
@ -18,7 +18,7 @@ use std::ptr::NonNull;
/// sp_brightness_grid_set(grid, 1, 1, 10);
///
/// TypedCommand command = sp_command_char_brightness(grid);
/// sp_connection_free(connection);
/// sp_udp_free(connection);
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_brightness_grid_new(
@ -167,5 +167,5 @@ pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref(
const _: () = assert!(size_of::<Brightness>() == 1);
let data = unsafe { (*brightness_grid.as_ptr()).data_ref_mut() };
unsafe { ByteSlice::from_slice(transmute(data)) }
unsafe { ByteSlice::from_slice(transmute::<&mut [Brightness], &mut [u8]>(data)) }
}

View file

@ -24,7 +24,7 @@ impl ByteSlice {
unsafe { std::slice::from_raw_parts(self.start.as_ptr(), self.length) }
}
pub(crate) unsafe fn as_slice_mut(&self) -> &mut [u8] {
pub(crate) unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe {
std::slice::from_raw_parts_mut(self.start.as_ptr(), self.length)
}

3
src/commands/mod.rs Normal file
View file

@ -0,0 +1,3 @@
mod typed;
pub use typed::*;

201
src/commands/typed.rs Normal file
View file

@ -0,0 +1,201 @@
use crate::{heap_drop, heap_move, heap_move_nonnull, SPBitVec};
use servicepoint::{
BinaryOperation, Bitmap, Brightness, BrightnessGrid, CharGrid,
CompressionCode, Cp437Grid, GlobalBrightnessCommand, Packet, TypedCommand,
};
use std::ptr::NonNull;
/// Tries to turn a [Packet] into a [TypedCommand].
///
/// The packet is deallocated in the process.
///
/// Returns: pointer to new [TypedCommand] instance or NULL if parsing failed.
#[no_mangle]
pub unsafe extern "C" fn sp_command_try_from_packet(
packet: NonNull<Packet>,
) -> *mut TypedCommand {
let packet = *unsafe { Box::from_raw(packet.as_ptr()) };
match servicepoint::TypedCommand::try_from(packet) {
Err(_) => std::ptr::null_mut(),
Ok(command) => heap_move(command),
}
}
/// Clones a [TypedCommand] instance.
///
/// returns: new [TypedCommand] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_clone(
command: NonNull<TypedCommand>,
) -> NonNull<TypedCommand> {
heap_move_nonnull(unsafe { command.as_ref().clone() })
}
/// Set all pixels to the off state.
///
/// Does not affect brightness.
///
/// Returns: a new [servicepoint::Command::Clear] instance.
///
/// # Examples
///
/// ```C
/// sp_udp_send_command(connection, sp_command_clear());
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_command_clear() -> NonNull<TypedCommand> {
heap_move_nonnull(servicepoint::ClearCommand.into())
}
/// Kills the udp daemon on the display, which usually results in a restart.
///
/// Please do not send this in your normal program flow.
///
/// Returns: a new [servicepoint::Command::HardReset] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_hard_reset() -> NonNull<TypedCommand> {
heap_move_nonnull(servicepoint::HardResetCommand.into())
}
/// A yet-to-be-tested command.
///
/// Returns: a new [servicepoint::Command::FadeOut] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<TypedCommand> {
heap_move_nonnull(servicepoint::FadeOutCommand.into())
}
/// Set the brightness of all tiles to the same value.
///
/// Returns: a new [servicepoint::Command::Brightness] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_global_brightness(
brightness: Brightness,
) -> NonNull<TypedCommand> {
heap_move_nonnull(GlobalBrightnessCommand::from(brightness).into())
}
/// Set the brightness of individual tiles in a rectangular area of the display.
///
/// The passed [BrightnessGrid] gets consumed.
///
/// Returns: a new [servicepoint::Command::CharBrightness] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_brightness_grid(
x: usize,
y: usize,
grid: NonNull<BrightnessGrid>,
) -> NonNull<TypedCommand> {
let grid = unsafe { *Box::from_raw(grid.as_ptr()) };
let result = servicepoint::BrightnessGridCommand {
origin: servicepoint::Origin::new(x, y),
grid,
}
.into();
heap_move_nonnull(result)
}
/// Set pixel data starting at the pixel offset on screen.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The [`BinaryOperation`] will be applied on the display comparing old and sent bit.
///
/// `new_bit = old_bit op sent_bit`
///
/// For example, [`BinaryOperation::Or`] can be used to turn on some pixels without affecting other pixels.
///
/// The contained [`BitVecU8Msb0`] is always uncompressed.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitvec(
offset: usize,
bit_vec: NonNull<SPBitVec>,
compression: CompressionCode,
operation: BinaryOperation,
) -> *mut TypedCommand {
let bit_vec = unsafe { *Box::from_raw(bit_vec.as_ptr()) };
let command = servicepoint::BitVecCommand {
offset,
operation,
bitvec: bit_vec.0,
compression,
}
.into();
heap_move(command)
}
/// Show codepage 437 encoded text on the screen.
///
/// The passed [Cp437Grid] gets consumed.
///
/// Returns: a new [servicepoint::Cp437GridCommand] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_cp437_grid(
x: usize,
y: usize,
grid: NonNull<Cp437Grid>,
) -> NonNull<TypedCommand> {
let grid = *unsafe { Box::from_raw(grid.as_ptr()) };
let result = servicepoint::Cp437GridCommand {
origin: servicepoint::Origin::new(x, y),
grid,
}
.into();
heap_move_nonnull(result)
}
/// Show UTF-8 encoded text on the screen.
///
/// The passed [CharGrid] gets consumed.
///
/// Returns: a new [servicepoint::CharGridCommand] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_char_grid(
x: usize,
y: usize,
grid: NonNull<CharGrid>,
) -> NonNull<TypedCommand> {
let grid = unsafe { *Box::from_raw(grid.as_ptr()) };
let result = servicepoint::CharGridCommand {
origin: servicepoint::Origin::new(x, y),
grid,
}
.into();
heap_move_nonnull(result)
}
/// Sets a window of pixels to the specified values.
///
/// The passed [Bitmap] gets consumed.
///
/// Returns: a new [servicepoint::BitmapCommand] instance.
#[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap(
x: usize,
y: usize,
bitmap: NonNull<Bitmap>,
compression: CompressionCode,
) -> *mut TypedCommand {
let bitmap = unsafe { *Box::from_raw(bitmap.as_ptr()) };
let command = servicepoint::BitmapCommand {
origin: servicepoint::Origin::new(x, y),
bitmap,
compression,
}
.into();
heap_move(command)
}
/// Deallocates a [TypedCommand].
///
/// # Examples
///
/// ```C
/// TypedCommand c = sp_command_clear();
/// sp_command_free(c);
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_command_free(command: NonNull<TypedCommand>) {
unsafe { heap_drop(command) }
}

View file

@ -1,5 +1,5 @@
use crate::{heap_drop, heap_move};
use servicepoint::{Connection, Packet, TypedCommand, UdpConnection};
use servicepoint::{Connection, Header, Packet, TypedCommand, UdpConnection};
use std::ffi::{c_char, CStr};
use std::net::{Ipv4Addr, SocketAddrV4};
use std::ptr::NonNull;
@ -11,9 +11,9 @@ use std::ptr::NonNull;
/// # Examples
///
/// ```C
/// UdpConnection connection = sp_connection_open("172.23.42.29:2342");
/// UdpConnection connection = sp_udp_open("172.23.42.29:2342");
/// if (connection != NULL)
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_udp_send_command(connection, sp_command_clear());
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_udp_open(
@ -37,9 +37,9 @@ pub unsafe extern "C" fn sp_udp_open(
/// # Examples
///
/// ```C
/// UdpConnection connection = sp_connection_open_ipv4(172, 23, 42, 29, 2342);
/// UdpConnection connection = sp_udp_open_ipv4(172, 23, 42, 29, 2342);
/// if (connection != NULL)
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_udp_send_command(connection, sp_command_clear());
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_udp_open_ipv4(
@ -80,8 +80,7 @@ pub unsafe extern "C" fn sp_udp_send_packet(
/// # Examples
///
/// ```C
/// sp_connection_send_command(connection, sp_command_clear());
/// sp_connection_send_command(connection, sp_command_brightness(5));
/// sp_udp_send_command(connection, sp_command_brightness(5));
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_udp_send_command(
@ -92,6 +91,27 @@ pub unsafe extern "C" fn sp_udp_send_command(
unsafe { connection.as_ref().send(command) }.is_ok()
}
/// Sends a [Header] to the display using the [UdpConnection].
///
/// returns: true in case of success
///
/// # Examples
///
/// ```C
/// sp_udp_send_header(connection, sp_command_brightness(5));
/// ```
#[no_mangle]
pub unsafe extern "C" fn sp_udp_send_header(
udp_connection: NonNull<UdpConnection>,
header: Header,
) -> bool {
let packet = Packet {
header,
payload: vec![],
};
unsafe { udp_connection.as_ref() }.send(packet).is_ok()
}
/// Closes and deallocates a [UdpConnection].
#[no_mangle]
pub unsafe extern "C" fn sp_udp_free(connection: NonNull<UdpConnection>) {

View file

@ -9,7 +9,7 @@
//! #include "servicepoint.h"
//!
//! int main(void) {
//! UdpConnection *connection = sp_connection_open("172.23.42.29:2342");
//! UdpConnection *connection = sp_udp_open("172.23.42.29:2342");
//! if (connection == NULL)
//! return 1;
//!
@ -17,10 +17,10 @@
//! sp_bitmap_fill(pixels, true);
//!
//! TypedCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
//! while (sp_connection_send_command(connection, sp_command_clone(command)));
//! while (sp_udp_send_command(connection, sp_command_clone(command)));
//!
//! sp_command_free(command);
//! sp_connection_free(connection);
//! sp_udp_free(connection);
//! return 0;
//! }
//! ```
@ -30,10 +30,11 @@ 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::commands::*;
pub use crate::connection::*;
pub use crate::cp437_grid::*;
pub use crate::packet::*;
pub use servicepoint::CommandCode;
use std::ptr::NonNull;
mod bitmap;
@ -41,7 +42,7 @@ mod bitvec;
mod brightness_grid;
mod byte_slice;
mod char_grid;
mod command;
mod commands;
mod connection;
mod cp437_grid;
mod packet;

View file

@ -1,5 +1,5 @@
use crate::{heap_drop, heap_move, heap_move_nonnull, ByteSlice};
use servicepoint::{Header, Packet, TypedCommand};
use servicepoint::{CommandCode, Header, Packet, TypedCommand};
use std::ptr::NonNull;
/// Turns a [TypedCommand] into a [Packet].
@ -65,7 +65,7 @@ pub unsafe extern "C" fn sp_packet_get_header(
pub unsafe extern "C" fn sp_packet_get_payload(
packet: NonNull<Packet>,
) -> ByteSlice {
unsafe { ByteSlice::from_slice(&mut *(*packet.as_ptr()).payload) }
unsafe { ByteSlice::from_slice(&mut (*packet.as_ptr()).payload) }
}
/// Sets the payload of the provided packet to the provided data.
@ -87,7 +87,7 @@ pub unsafe extern "C" fn sp_packet_set_payload(
#[no_mangle]
pub unsafe extern "C" fn sp_packet_serialize_to(
packet: NonNull<Packet>,
buffer: ByteSlice,
mut buffer: ByteSlice,
) {
unsafe {
packet.as_ref().serialize_to(buffer.as_slice_mut());
@ -107,3 +107,19 @@ pub unsafe extern "C" fn sp_packet_clone(
pub unsafe extern "C" fn sp_packet_free(packet: NonNull<Packet>) {
unsafe { heap_drop(packet) }
}
#[no_mangle]
pub unsafe extern "C" fn sp_u16_to_command_code(
code: u16,
result: *mut CommandCode,
) -> bool {
match CommandCode::try_from(code) {
Ok(code) => {
unsafe {
*result = code;
}
true
}
Err(_) => false,
}
}