named fields instead of tuple for Packet, doc adjustments

This commit is contained in:
Vinzenz Schroeter 2024-09-07 11:44:42 +02:00
parent e54891e662
commit e97418b51b
5 changed files with 529 additions and 135 deletions

View file

@ -190,10 +190,17 @@ impl TryFrom<Packet> for Command {
/// Try to interpret the `Packet` as one containing a `Command` /// Try to interpret the `Packet` as one containing a `Command`
fn try_from(packet: Packet) -> Result<Self, Self::Error> { fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let Packet(Header(command_u16, a, _, _, _), _) = packet; let Packet {
let command_code = match CommandCode::try_from(command_u16) { header: Header {
command_code,
a,
..
},
..
} = packet;
let command_code = match CommandCode::try_from(command_code) {
Err(()) => { Err(()) => {
return Err(TryFromPacketError::InvalidCommand(command_u16)); return Err(TryFromPacketError::InvalidCommand(command_code));
} }
Ok(value) => value, Ok(value) => value,
}; };
@ -266,8 +273,16 @@ impl Command {
packet: Packet, packet: Packet,
compression: CompressionCode, compression: CompressionCode,
) -> Result<Command, TryFromPacketError> { ) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, tiles_x, pixels_y, tile_w, pixel_h), payload) = let Packet {
packet; header: Header {
command_code: _,
a: tiles_x,
b: pixels_y,
c: tile_w,
d: pixel_h,
},
payload,
} = packet;
let payload = match into_decompressed(compression, payload) { let payload = match into_decompressed(compression, payload) {
None => return Err(TryFromPacketError::DecompressionFailed), None => return Err(TryFromPacketError::DecompressionFailed),
@ -290,7 +305,16 @@ impl Command {
packet: Packet, packet: Packet,
command: Command, command: Command,
) -> Result<Command, TryFromPacketError> { ) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet; let Packet {
header: Header {
command_code: _,
a,
b,
c,
d,
},
payload,
} = packet;
if !payload.is_empty() { if !payload.is_empty() {
Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len())) Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len()))
} else if a != 0 || b != 0 || c != 0 || d != 0 { } else if a != 0 || b != 0 || c != 0 || d != 0 {
@ -304,7 +328,15 @@ impl Command {
fn packet_into_linear_bitmap( fn packet_into_linear_bitmap(
packet: Packet, packet: Packet,
) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> { ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> {
let Packet(Header(_, _, length, sub, reserved), payload) = packet; let Packet {
header: Header {
b: length,
c: sub,
d: reserved,
..
},
payload,
} = packet;
if reserved != 0 { if reserved != 0 {
return Err(TryFromPacketError::ExtraneousHeaderValues); return Err(TryFromPacketError::ExtraneousHeaderValues);
} }
@ -330,7 +362,16 @@ impl Command {
fn packet_into_char_brightness( fn packet_into_char_brightness(
packet: &Packet, packet: &Packet,
) -> Result<Command, TryFromPacketError> { ) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, x, y, width, height), payload) = packet; let Packet {
header: Header {
command_code: _,
a: x,
b: y,
c: width,
d: height,
},
payload,
} = packet;
let grid = let grid =
PrimitiveGrid::load(*width as usize, *height as usize, payload); PrimitiveGrid::load(*width as usize, *height as usize, payload);
@ -348,7 +389,16 @@ impl Command {
fn packet_into_brightness( fn packet_into_brightness(
packet: &Packet, packet: &Packet,
) -> Result<Command, TryFromPacketError> { ) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet; let Packet {
header: Header {
command_code: _,
a,
b,
c,
d,
},
payload,
} = packet;
if payload.len() != 1 { if payload.len() != 1 {
return Err(TryFromPacketError::UnexpectedPayloadSize( return Err(TryFromPacketError::UnexpectedPayloadSize(
1, 1,
@ -369,7 +419,16 @@ impl Command {
fn packet_into_cp437( fn packet_into_cp437(
packet: &Packet, packet: &Packet,
) -> Result<Command, TryFromPacketError> { ) -> Result<Command, TryFromPacketError> {
let Packet(Header(_, a, b, c, d), payload) = packet; let Packet {
header: Header {
command_code: _,
a,
b,
c,
d,
},
payload,
} = packet;
Ok(Command::Cp437Data( Ok(Command::Cp437Data(
Origin::new(*a as usize, *b as usize), Origin::new(*a as usize, *b as usize),
Cp437Grid::load(*c as usize, *d as usize, payload), Cp437Grid::load(*c as usize, *d as usize, payload),
@ -483,7 +542,16 @@ mod tests {
#[test] #[test]
fn error_invalid_command() { fn error_invalid_command() {
let p = Packet(Header(0xFF, 0x00, 0x00, 0x00, 0x00), vec![]); let p = Packet {
header: Header {
command_code: 0xFF,
a: 0x00,
b: 0x00,
c: 0x00,
d: 0x00,
},
payload: vec![],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -493,10 +561,16 @@ mod tests {
#[test] #[test]
fn error_extraneous_header_values_clear() { fn error_extraneous_header_values_clear() {
let p = Packet( let p = Packet {
Header(CommandCode::Clear.into(), 0x05, 0x00, 0x00, 0x00), header: Header {
vec![], command_code: CommandCode::Clear.into(),
); a: 0x05,
b: 0x00,
c: 0x00,
d: 0x00,
},
payload: vec![],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -506,10 +580,16 @@ mod tests {
#[test] #[test]
fn error_extraneous_header_values_brightness() { fn error_extraneous_header_values_brightness() {
let p = Packet( let p = Packet {
Header(CommandCode::Brightness.into(), 0x00, 0x13, 0x37, 0x00), header: Header {
vec![5], command_code: CommandCode::Brightness.into(),
); a: 0x00,
b: 0x13,
c: 0x37,
d: 0x00,
},
payload: vec![5],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -519,10 +599,16 @@ mod tests {
#[test] #[test]
fn error_extraneous_header_hard_reset() { fn error_extraneous_header_hard_reset() {
let p = Packet( let p = Packet {
Header(CommandCode::HardReset.into(), 0x00, 0x00, 0x00, 0x01), header: Header {
vec![], command_code: CommandCode::HardReset.into(),
); a: 0x00,
b: 0x00,
c: 0x00,
d: 0x01,
},
payload: vec![],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -532,10 +618,16 @@ mod tests {
#[test] #[test]
fn error_extraneous_header_fade_out() { fn error_extraneous_header_fade_out() {
let p = Packet( let p = Packet {
Header(CommandCode::FadeOut.into(), 0x10, 0x00, 0x00, 0x01), header: Header {
vec![], command_code: CommandCode::FadeOut.into(),
); a: 0x10,
b: 0x00,
c: 0x00,
d: 0x01,
},
payload: vec![],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -545,10 +637,16 @@ mod tests {
#[test] #[test]
fn error_unexpected_payload() { fn error_unexpected_payload() {
let p = Packet( let p = Packet {
Header(CommandCode::FadeOut.into(), 0x00, 0x00, 0x00, 0x00), header: Header {
vec![5, 7], command_code: CommandCode::FadeOut.into(),
); a: 0x00,
b: 0x00,
c: 0x00,
d: 0x00,
},
payload: vec![5, 7],
};
let result = Command::try_from(p); let result = Command::try_from(p);
assert!(matches!( assert!(matches!(
result, result,
@ -564,15 +662,19 @@ mod tests {
PixelGrid::new(8, 8), PixelGrid::new(8, 8),
compression, compression,
) )
.into(); .into();
let Packet(header, mut payload) = p;
let Packet {
header,
mut payload,
} = p;
// mangle it // mangle it
for byte in payload.iter_mut() { for byte in payload.iter_mut() {
*byte -= *byte / 2; *byte -= *byte / 2;
} }
let p = Packet(header, payload); let p = Packet { header, payload };
let result = Command::try_from(p); let result = Command::try_from(p);
if compression != CompressionCode::Uncompressed { if compression != CompressionCode::Uncompressed {
assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) assert_eq!(result, Err(TryFromPacketError::DecompressionFailed))
@ -590,15 +692,18 @@ mod tests {
BitVec::repeat(false, 8), BitVec::repeat(false, 8),
compression, compression,
) )
.into(); .into();
let Packet(header, mut payload) = p; let Packet {
header,
mut payload,
} = p;
// mangle it // mangle it
for byte in payload.iter_mut() { for byte in payload.iter_mut() {
*byte -= *byte / 2; *byte -= *byte / 2;
} }
let p = Packet(header, payload); let p = Packet { header, payload };
let result = Command::try_from(p); let result = Command::try_from(p);
if compression != CompressionCode::Uncompressed { if compression != CompressionCode::Uncompressed {
assert_eq!(result, Err(TryFromPacketError::DecompressionFailed)) assert_eq!(result, Err(TryFromPacketError::DecompressionFailed))
@ -612,32 +717,59 @@ mod tests {
#[test] #[test]
fn unexpected_payload_size_brightness() { fn unexpected_payload_size_brightness() {
assert_eq!( assert_eq!(
Command::try_from(Packet( Command::try_from(Packet {
Header(CommandCode::Brightness.into(), 0, 0, 0, 0), header: Header {
vec!(), command_code: CommandCode::Brightness.into(),
)), a: 0,
b: 0,
c: 0,
d: 0,
},
payload: vec!()
}),
Err(TryFromPacketError::UnexpectedPayloadSize(1, 0)) Err(TryFromPacketError::UnexpectedPayloadSize(1, 0))
); );
assert_eq!( assert_eq!(
Command::try_from(Packet( Command::try_from(Packet {
Header(CommandCode::Brightness.into(), 0, 0, 0, 0), header: Header {
vec!(0, 0), command_code: CommandCode::Brightness.into(),
)), a: 0,
b: 0,
c: 0,
d: 0,
},
payload: vec!(0, 0)
}),
Err(TryFromPacketError::UnexpectedPayloadSize(1, 2)) Err(TryFromPacketError::UnexpectedPayloadSize(1, 2))
); );
} }
#[test] #[test]
fn error_reserved_used() { fn error_reserved_used() {
let Packet(header, payload) = Command::BitmapLinear( let Packet { header, payload } = Command::BitmapLinear(
0, 0,
BitVec::repeat(false, 8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();
let Header(command, offset, length, sub, _reserved) = header; let Header {
let p = Packet(Header(command, offset, length, sub, 69), payload); command_code: command,
a: offset,
b: length,
c: sub,
d: _reserved,
} = header;
let p = Packet {
header: Header {
command_code: command,
a: offset,
b: length,
c: sub,
d: 69,
},
payload,
};
assert_eq!( assert_eq!(
Command::try_from(p), Command::try_from(p),
Err(TryFromPacketError::ExtraneousHeaderValues) Err(TryFromPacketError::ExtraneousHeaderValues)
@ -646,14 +778,29 @@ mod tests {
#[test] #[test]
fn error_invalid_compression() { fn error_invalid_compression() {
let Packet(header, payload) = Command::BitmapLinear( let Packet { header, payload } = Command::BitmapLinear(
0, 0,
BitVec::repeat(false, 8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();
let Header(command, offset, length, _sub, reserved) = header; let Header {
let p = Packet(Header(command, offset, length, 42, reserved), payload); command_code: command,
a: offset,
b: length,
c: _sub,
d: reserved,
} = header;
let p = Packet {
header: Header {
command_code: command,
a: offset,
b: length,
c: 42,
d: reserved,
},
payload,
};
assert_eq!( assert_eq!(
Command::try_from(p), Command::try_from(p),
Err(TryFromPacketError::InvalidCompressionCode(42)) Err(TryFromPacketError::InvalidCompressionCode(42))
@ -662,17 +809,29 @@ mod tests {
#[test] #[test]
fn error_unexpected_size() { fn error_unexpected_size() {
let Packet(header, payload) = Command::BitmapLinear( let Packet { header, payload } = Command::BitmapLinear(
0, 0,
BitVec::repeat(false, 8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();
let Header(command, offset, length, compression, reserved) = header; let Header {
let p = Packet( command_code: command,
Header(command, offset, 420, compression, reserved), a: offset,
b: length,
c: compression,
d: reserved,
} = header;
let p = Packet {
header: Header {
command_code: command,
a: offset,
b: 420,
c: compression,
d: reserved,
},
payload, payload,
); };
assert_eq!( assert_eq!(
Command::try_from(p), Command::try_from(p),
Err(TryFromPacketError::UnexpectedPayloadSize( Err(TryFromPacketError::UnexpectedPayloadSize(

View file

@ -71,7 +71,7 @@ impl Connection {
Connection::Udp(socket) => { Connection::Udp(socket) => {
socket socket
.send(&data) .send(&data)
.map_err(move |io_err| SendError::IoError(io_err)) .map_err(SendError::IoError)
.map(move |_| ()) // ignore Ok value .map(move |_| ()) // ignore Ok value
} }
Connection::Fake => Ok(()), Connection::Fake => Ok(()),

View file

@ -3,25 +3,84 @@ use std::mem::size_of;
use crate::command_code::CommandCode; use crate::command_code::CommandCode;
use crate::compression::into_compressed; use crate::compression::into_compressed;
use crate::{ use crate::{
Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Tiles,
TILE_SIZE, TILE_SIZE,
}; };
/// A raw header. Should probably not be used directly. /// A raw header.
///
/// The header specifies the kind of command, the size of the payload and where to display the
/// payload, where applicable.
///
/// Because the meaning of most fields depend on the command, there are no speaking names for them.
///
/// Should probably only be used directly to use features not exposed by the library.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Header(pub u16, pub u16, pub u16, pub u16, pub u16); pub struct Header {
/// The first two bytes specify which command this packet represents.
pub command_code: u16,
/// First command-specific value
pub a: u16,
/// Second command-specific value
pub b: u16,
/// Third command-specific value
pub c: u16,
/// Fourth command-specific value
pub d: u16,
}
/// The raw payload. Should probably not be used directly. /// The raw payload.
///
/// Should probably only be used directly to use features not exposed by the library.
pub type Payload = Vec<u8>; pub type Payload = Vec<u8>;
/// The raw packet. Should probably not be used directly. /// The raw packet.
///
/// Contents should probably only be used directly to use features not exposed by the library.
///
/// # Examples
///
/// Converting a packet to a command and back:
///
/// ```rust
/// # use servicepoint::{Command, Packet};
/// # let command = Command::Clear;
/// let packet: Packet = command.into();
/// let command: Command = Command::try_from(packet).expect("could not read packet");
/// ```
///
/// Converting a packet to bytes and back:
///
/// ```rust
/// # use servicepoint::{Command, Packet};
/// # let command = Command::Clear;
/// # let packet: Packet = command.into();
/// let bytes: Vec<u8> = packet.into();
/// let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
/// ```
///
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Packet(pub Header, pub Payload); pub struct Packet {
/// Meta-information for the packed command
pub header: Header,
/// The data for the packed command
pub payload: Payload,
}
impl From<Packet> for Vec<u8> { impl From<Packet> for Vec<u8> {
/// Turn the packet into raw bytes ready to send /// Turn the packet into raw bytes ready to send
fn from(value: Packet) -> Self { fn from(value: Packet) -> Self {
let Packet(Header(mode, a, b, c, d), payload) = value; let Packet {
header:
Header {
command_code: mode,
a,
b,
c,
d,
},
payload,
} = value;
let mut packet = vec![0u8; 10 + payload.len()]; let mut packet = vec![0u8; 10 + payload.len()];
packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode)); packet[0..=1].copy_from_slice(&u16::to_be_bytes(mode));
@ -36,13 +95,6 @@ impl From<Packet> for Vec<u8> {
} }
} }
fn u16_from_be_slice(slice: &[u8]) -> u16 {
let mut bytes = [0u8; 2];
bytes[0] = slice[0];
bytes[1] = slice[1];
u16::from_be_bytes(bytes)
}
impl TryFrom<&[u8]> for Packet { impl TryFrom<&[u8]> for Packet {
type Error = (); type Error = ();
@ -54,14 +106,31 @@ impl TryFrom<&[u8]> for Packet {
return Err(()); return Err(());
} }
let mode = u16_from_be_slice(&value[0..=1]); let header = {
let a = u16_from_be_slice(&value[2..=3]); let command_code = Self::u16_from_be_slice(&value[0..=1]);
let b = u16_from_be_slice(&value[4..=5]); let a = Self::u16_from_be_slice(&value[2..=3]);
let c = u16_from_be_slice(&value[6..=7]); let b = Self::u16_from_be_slice(&value[4..=5]);
let d = u16_from_be_slice(&value[8..=9]); let c = Self::u16_from_be_slice(&value[6..=7]);
let d = Self::u16_from_be_slice(&value[8..=9]);
Header {
command_code,
a,
b,
c,
d,
}
};
let payload = value[10..].to_vec(); let payload = value[10..].to_vec();
Ok(Packet(Header(mode, a, b, c, d), payload)) Ok(Packet { header, payload })
}
}
impl TryFrom<Vec<u8>> for Packet {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
} }
} }
@ -79,26 +148,23 @@ impl From<Command> for Packet {
Command::BitmapLegacy => { Command::BitmapLegacy => {
Self::command_code_only(CommandCode::BitmapLegacy) Self::command_code_only(CommandCode::BitmapLegacy)
} }
Command::CharBrightness(origin, grid) => Packet( Command::CharBrightness(origin, grid) => {
Header( Self::origin_grid_to_packet(
CommandCode::CharBrightness.into(), origin,
origin.x as u16, grid,
origin.y as u16, CommandCode::CharBrightness,
grid.width() as u16, )
grid.height() as u16, }
), Command::Brightness(brightness) => Packet {
grid.into(), header: Header {
), command_code: CommandCode::Brightness.into(),
Command::Brightness(brightness) => Packet( a: 0x00000,
Header( b: 0x0000,
CommandCode::Brightness.into(), c: 0x0000,
0x00000, d: 0x0000,
0x0000, },
0x0000, payload: vec![brightness.into()],
0x0000, },
),
vec![brightness.into()],
),
Command::BitmapLinearWin(origin, pixels, compression) => { Command::BitmapLinearWin(origin, pixels, compression) => {
Self::bitmap_win_into_packet(origin, pixels, compression) Self::bitmap_win_into_packet(origin, pixels, compression)
} }
@ -134,15 +200,10 @@ impl From<Command> for Packet {
bits.into(), bits.into(),
) )
} }
Command::Cp437Data(origin, grid) => Packet( Command::Cp437Data(origin, grid) => Self::origin_grid_to_packet(
Header( origin,
CommandCode::Cp437Data.into(), grid,
origin.x as u16, CommandCode::Cp437Data,
origin.y as u16,
grid.width() as u16,
grid.height() as u16,
),
grid.into(),
), ),
} }
} }
@ -159,16 +220,16 @@ impl Packet {
) -> Packet { ) -> Packet {
let length = payload.len() as u16; let length = payload.len() as u16;
let payload = into_compressed(compression, payload); let payload = into_compressed(compression, payload);
Packet( Packet {
Header( header: Header {
command.into(), command_code: command.into(),
offset as u16, a: offset as u16,
length, b: length,
compression.into(), c: compression.into(),
0, d: 0,
), },
payload, payload,
) }
} }
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
@ -198,15 +259,54 @@ impl Packet {
CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd, CompressionCode::Zstd => CommandCode::BitmapLinearWinZstd,
}; };
Packet( Packet {
Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h), header: Header {
command_code: command.into(),
a: tile_x,
b: origin.y as u16,
c: tile_w,
d: pixel_h,
},
payload, payload,
) }
} }
/// Helper method for creating empty packets only containing the command code /// Helper method for creating empty packets only containing the command code
fn command_code_only(code: CommandCode) -> Packet { fn command_code_only(code: CommandCode) -> Packet {
Packet(Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000), vec![]) Packet {
header: Header {
command_code: code.into(),
a: 0x0000,
b: 0x0000,
c: 0x0000,
d: 0x0000,
},
payload: vec![],
}
}
fn u16_from_be_slice(slice: &[u8]) -> u16 {
let mut bytes = [0u8; 2];
bytes[0] = slice[0];
bytes[1] = slice[1];
u16::from_be_bytes(bytes)
}
fn origin_grid_to_packet<T>(
origin: Origin<Tiles>,
grid: impl Grid<T> + Into<Payload>,
command_code: CommandCode,
) -> Packet {
Packet {
header: Header {
command_code: command_code.into(),
a: origin.x as u16,
b: origin.y as u16,
c: grid.width() as u16,
d: grid.height() as u16,
},
payload: grid.into(),
}
} }
} }
@ -216,10 +316,31 @@ mod tests {
#[test] #[test]
fn round_trip() { fn round_trip() {
let p = Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23]); let p = Packet {
header: Header {
command_code: 0,
a: 1,
b: 2,
c: 3,
d: 4,
},
payload: vec![42u8; 23],
};
let data: Vec<u8> = p.into(); let data: Vec<u8> = p.into();
let p = Packet::try_from(&*data).unwrap(); let p = Packet::try_from(&*data).unwrap();
assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23])); assert_eq!(
p,
Packet {
header: Header {
command_code: 0,
a: 1,
b: 2,
c: 3,
d: 4
},
payload: vec![42u8; 23]
}
);
} }
#[test] #[test]

View file

@ -105,7 +105,7 @@ typedef struct SPBrightnessGrid SPBrightnessGrid;
* *
* This struct and associated functions implement the UDP protocol for the display. * This struct and associated functions implement the UDP protocol for the display.
* *
* To send a `CCommand`, use a `CConnection`. * To send a `SPCommand`, use a `SPConnection`.
* *
* # Examples * # Examples
* *
@ -539,6 +539,13 @@ size_t sp_brightness_grid_width(const struct SPBrightnessGrid *this_);
* Allocates a new `Command::BitmapLinear` instance. * Allocates a new `Command::BitmapLinear` instance.
* The passed `BitVec` gets consumed. * The passed `BitVec` gets consumed.
* *
* 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 contained `BitVec` is always uncompressed.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -557,6 +564,13 @@ struct SPCommand *sp_command_bitmap_linear(SPOffset offset,
* Allocates a new `Command::BitmapLinearAnd` instance. * Allocates a new `Command::BitmapLinearAnd` instance.
* The passed `BitVec` gets consumed. * The passed `BitVec` gets consumed.
* *
* Set pixel data according to an and-mask starting at the offset.
*
* 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 contained `BitVec` is always uncompressed.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -575,6 +589,13 @@ struct SPCommand *sp_command_bitmap_linear_and(SPOffset offset,
* Allocates a new `Command::BitmapLinearOr` instance. * Allocates a new `Command::BitmapLinearOr` instance.
* The passed `BitVec` gets consumed. * The passed `BitVec` gets consumed.
* *
* Set pixel data according to an or-mask starting at the offset.
*
* 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 contained `BitVec` is always uncompressed.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -593,6 +614,8 @@ struct SPCommand *sp_command_bitmap_linear_or(SPOffset offset,
* Allocates a new `Command::BitmapLinearWin` instance. * Allocates a new `Command::BitmapLinearWin` instance.
* The passed `PixelGrid` gets consumed. * The passed `PixelGrid` gets consumed.
* *
* Sets a window of pixels to the specified values
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -612,6 +635,13 @@ struct SPCommand *sp_command_bitmap_linear_win(size_t x,
* Allocates a new `Command::BitmapLinearXor` instance. * Allocates a new `Command::BitmapLinearXor` instance.
* The passed `BitVec` gets consumed. * The passed `BitVec` gets consumed.
* *
* Set pixel data according to a xor-mask starting at the offset.
*
* 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 contained `BitVec` is always uncompressed.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -647,6 +677,8 @@ struct SPCommand *sp_command_brightness(uint8_t brightness);
* Allocates a new `Command::CharBrightness` instance. * Allocates a new `Command::CharBrightness` instance.
* The passed `SPBrightnessGrid` gets consumed. * The passed `SPBrightnessGrid` gets consumed.
* *
* Set the brightness of individual tiles in a rectangular area of the display.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -663,6 +695,14 @@ struct SPCommand *sp_command_char_brightness(size_t x,
/** /**
* Allocates a new `Command::Clear` instance. * Allocates a new `Command::Clear` instance.
* *
* Set all pixels to the off state. Does not affect brightness.
*
* # Examples
*
* ```C
* sp_connection_send(connection, sp_command_clear());
* ```
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -673,7 +713,7 @@ struct SPCommand *sp_command_char_brightness(size_t x,
struct SPCommand *sp_command_clear(void); struct SPCommand *sp_command_clear(void);
/** /**
* Clones a `Command` instance. * Clones a `SPCommand` instance.
* *
* # Safety * # Safety
* *
@ -690,6 +730,13 @@ struct SPCommand *sp_command_clone(const struct SPCommand *original);
* Allocates a new `Command::Cp437Data` instance. * Allocates a new `Command::Cp437Data` instance.
* The passed `ByteGrid` gets consumed. * The passed `ByteGrid` gets consumed.
* *
* Show text on the screen.
*
* <div class="warning">
* The library does not currently convert between UTF-8 and CP-437.
* Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
* </div>
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -706,6 +753,13 @@ struct SPCommand *sp_command_cp437_data(size_t x,
/** /**
* Deallocates a `Command`. * Deallocates a `Command`.
* *
* # Examples
*
* ```C
* SPCommand c = sp_command_clear();
* sp_command_dealloc(c);
* ```
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -731,6 +785,9 @@ struct SPCommand *sp_command_fade_out(void);
/** /**
* Allocates a new `Command::HardReset` instance. * Allocates a new `Command::HardReset` instance.
* *
* Kills the udp daemon on the display, which usually results in a restart.
* Please do not send this in your normal program flow.
*
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
@ -741,18 +798,18 @@ struct SPCommand *sp_command_fade_out(void);
struct SPCommand *sp_command_hard_reset(void); struct SPCommand *sp_command_hard_reset(void);
/** /**
* Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. * Tries to turn a `SPPacket` into a `SPCommand`. The packet is deallocated in the process.
* *
* Returns: pointer to new `Command` instance or NULL * Returns: pointer to new `SPCommand` instance or NULL
* *
* # Safety * # Safety
* *
* The caller has to make sure that: * The caller has to make sure that:
* *
* - `packet` points to a valid instance of `Packet` * - `packet` points to a valid instance of `SPPacket`
* - `packet` is not used concurrently or after this call * - `packet` is not used concurrently or after this call
* - the result is checked for NULL * - the result is checked for NULL
* - the returned `Command` instance is freed in some way, either by using a consuming function or * - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
* by explicitly calling `sp_command_dealloc`. * by explicitly calling `sp_command_dealloc`.
*/ */
struct SPCommand *sp_command_try_from_packet(struct SPPacket *packet); struct SPCommand *sp_command_try_from_packet(struct SPPacket *packet);

View file

@ -546,25 +546,25 @@ namespace ServicePoint.BindGen
public static extern ByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid* @this); public static extern ByteSlice sp_cp437_grid_unsafe_data_ref(Cp437Grid* @this);
/// <summary> /// <summary>
/// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. /// Tries to turn a `SPPacket` into a `SPCommand`. The packet is deallocated in the process.
/// ///
/// Returns: pointer to new `Command` instance or NULL /// Returns: pointer to new `SPCommand` instance or NULL
/// ///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
/// ///
/// - `packet` points to a valid instance of `Packet` /// - `packet` points to a valid instance of `SPPacket`
/// - `packet` is not used concurrently or after this call /// - `packet` is not used concurrently or after this call
/// - the result is checked for NULL /// - the result is checked for NULL
/// - the returned `Command` instance is freed in some way, either by using a consuming function or /// - the returned `SPCommand` instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_dealloc`. /// by explicitly calling `sp_command_dealloc`.
/// </summary> /// </summary>
[DllImport(__DllName, EntryPoint = "sp_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* sp_command_try_from_packet(Packet* packet); public static extern Command* sp_command_try_from_packet(Packet* packet);
/// <summary> /// <summary>
/// Clones a `Command` instance. /// Clones a `SPCommand` instance.
/// ///
/// # Safety /// # Safety
/// ///
@ -581,6 +581,14 @@ namespace ServicePoint.BindGen
/// <summary> /// <summary>
/// Allocates a new `Command::Clear` instance. /// Allocates a new `Command::Clear` instance.
/// ///
/// Set all pixels to the off state. Does not affect brightness.
///
/// # Examples
///
/// ```C
/// sp_connection_send(connection, sp_command_clear());
/// ```
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -594,6 +602,9 @@ namespace ServicePoint.BindGen
/// <summary> /// <summary>
/// Allocates a new `Command::HardReset` instance. /// Allocates a new `Command::HardReset` instance.
/// ///
/// Kills the udp daemon on the display, which usually results in a restart.
/// Please do not send this in your normal program flow.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -639,6 +650,8 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::CharBrightness` instance. /// Allocates a new `Command::CharBrightness` instance.
/// The passed `SPBrightnessGrid` gets consumed. /// The passed `SPBrightnessGrid` gets consumed.
/// ///
/// Set the brightness of individual tiles in a rectangular area of the display.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -655,6 +668,13 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::BitmapLinear` instance. /// Allocates a new `Command::BitmapLinear` instance.
/// The passed `BitVec` gets consumed. /// The passed `BitVec` gets consumed.
/// ///
/// 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 contained `BitVec` is always uncompressed.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -672,6 +692,13 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::BitmapLinearAnd` instance. /// Allocates a new `Command::BitmapLinearAnd` instance.
/// The passed `BitVec` gets consumed. /// The passed `BitVec` gets consumed.
/// ///
/// Set pixel data according to an and-mask starting at the offset.
///
/// 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 contained `BitVec` is always uncompressed.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -689,6 +716,13 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::BitmapLinearOr` instance. /// Allocates a new `Command::BitmapLinearOr` instance.
/// The passed `BitVec` gets consumed. /// The passed `BitVec` gets consumed.
/// ///
/// Set pixel data according to an or-mask starting at the offset.
///
/// 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 contained `BitVec` is always uncompressed.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -706,6 +740,13 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::BitmapLinearXor` instance. /// Allocates a new `Command::BitmapLinearXor` instance.
/// The passed `BitVec` gets consumed. /// The passed `BitVec` gets consumed.
/// ///
/// Set pixel data according to a xor-mask starting at the offset.
///
/// 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 contained `BitVec` is always uncompressed.
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -723,6 +764,13 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::Cp437Data` instance. /// Allocates a new `Command::Cp437Data` instance.
/// The passed `ByteGrid` gets consumed. /// The passed `ByteGrid` gets consumed.
/// ///
/// Show text on the screen.
///
/// &lt;div class="warning"&gt;
/// The library does not currently convert between UTF-8 and CP-437.
/// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
/// &lt;/div&gt;
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -739,6 +787,8 @@ namespace ServicePoint.BindGen
/// Allocates a new `Command::BitmapLinearWin` instance. /// Allocates a new `Command::BitmapLinearWin` instance.
/// The passed `PixelGrid` gets consumed. /// The passed `PixelGrid` gets consumed.
/// ///
/// Sets a window of pixels to the specified values
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that:
@ -755,6 +805,13 @@ namespace ServicePoint.BindGen
/// <summary> /// <summary>
/// Deallocates a `Command`. /// Deallocates a `Command`.
/// ///
/// # Examples
///
/// ```C
/// SPCommand c = sp_command_clear();
/// sp_command_dealloc(c);
/// ```
///
/// # Safety /// # Safety
/// ///
/// The caller has to make sure that: /// The caller has to make sure that: