Merge branch 'next'

This commit is contained in:
Vinzenz Schroeter 2025-02-15 12:19:00 +01:00
commit 2f7a2dfd62
21 changed files with 278 additions and 69 deletions

View file

@ -4,6 +4,9 @@ Contributions are accepted in any form (issues, documentation, feature requests,
All creatures welcome.
If you have access, please contribute on the [CCCB Forgejo](https://git.berlin.ccc.de/servicepoint/servicepoint).
Contributions on GitHub will be copied over and merged there.
## Pull requests
Feel free to create a PR, even if your change is not done yet.

6
Cargo.lock generated
View file

@ -931,7 +931,7 @@ dependencies = [
[[package]]
name = "servicepoint"
version = "0.13.0"
version = "0.13.1"
dependencies = [
"bitvec",
"bzip2",
@ -948,7 +948,7 @@ dependencies = [
[[package]]
name = "servicepoint_binding_c"
version = "0.13.0"
version = "0.13.1"
dependencies = [
"cbindgen",
"servicepoint",
@ -956,7 +956,7 @@ dependencies = [
[[package]]
name = "servicepoint_binding_uniffi"
version = "0.13.0"
version = "0.13.1"
dependencies = [
"servicepoint",
"thiserror 2.0.11",

View file

@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "0.13.0"
version = "0.13.1"
[workspace.lints.rust]
missing-docs = "warn"

View file

@ -10,7 +10,8 @@ Display" or "Airport Display".
This repository contains a library for parsing, encoding and sending packets to this display via UDP in multiple
programming languages.
This repository will move to [git.berlin.ccc.de/servicepoint/servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint) soon.
This project moved to [git.berlin.ccc.de/servicepoint/servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint).
The [GitHub repository](https://github.com/cccb/servicepoint) remains available as a mirror.
Take a look at the contained crates for language specific information:
@ -22,7 +23,7 @@ Take a look at the contained crates for language specific information:
## Projects using the library
- screen simulator (rust): [servicepoint-simulator](https://github.com/kaesaecracker/servicepoint-simulator)
- screen simulator (rust): [servicepoint-simulator](https://git.berlin.ccc.de/servicepoint/servicepoint-simulator)
- A bunch of projects (C): [arfst23/ServicePoint](https://github.com/arfst23/ServicePoint), including
- a CLI tool to display image files on the display or use the display as a TTY
- a BSD games robots clone
@ -31,9 +32,14 @@ Take a look at the contained crates for language specific information:
- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
- partial typescript implementation inspired by this library and browser stream: [cccb-servicepoint-browser](https://github.com/SamuelScheit/cccb-servicepoint-browser)
- a CLI: [servicepoint-cli](https://git.berlin.ccc.de/servicepoint/servicepoint-cli)
To add yourself to the list, open a pull request.
You can also check out [awesome-servicepoint](https://github.com/stars/kaesaecracker/lists/awesome-servicepoint) for a bigger collection of projects, including some not related to this library.
If you have access, there is even more software linked in [the wiki](https://wiki.berlin.ccc.de/LED-Riesendisplay).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).

View file

@ -6,8 +6,9 @@ edition = "2021"
license = "GPL-3.0-or-later"
description = "A rust library for the CCCB Service Point Display."
homepage = "https://docs.rs/crate/servicepoint"
repository = "https://github.com/cccb/servicepoint"
repository = "https://git.berlin.ccc.de/servicepoint/servicepoint"
readme = "README.md"
keywords = ["cccb", "cccb-servicepoint"]
[lib]
crate-type = ["rlib"]

View file

@ -17,7 +17,7 @@ cargo add servicepoint
or
```toml
[dependencies]
servicepoint = "0.13.0"
servicepoint = "0.13.1"
```
## Examples
@ -64,4 +64,4 @@ You can choose to (not) include them by toggling the related features.
## Everything else
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.
Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for further information.

View file

@ -72,8 +72,8 @@ impl Bitmap {
/// - when the width is not dividable by 8
#[must_use]
pub fn load(width: usize, height: usize, data: &[u8]) -> Self {
assert_eq!(width % 8, 0);
assert_eq!(data.len(), height * width / 8);
assert_eq!(width % 8, 0, "width must be a multiple of 8, but is {width}");
assert_eq!(data.len(), height * width / 8, "data length must match dimensions, with 8 pixels per byte.");
Self {
width,
height,
@ -81,6 +81,23 @@ impl Bitmap {
}
}
/// Creates a [Bitmap] with the specified width from the provided [BitVec] without copying it.
///
/// returns: [Bitmap] that contains the provided data.
///
/// # Panics
///
/// - when the bitvec size is not dividable by the provided width
/// - when the width is not dividable by 8
#[must_use]
pub fn from_bitvec(width: usize, bit_vec: BitVec) -> Self {
assert_eq!(width % 8, 0, "width must be a multiple of 8, but is {width}");
let len = bit_vec.len();
let height = len / width;
assert_eq!(0, len % width, "dimension mismatch - len {len} is not dividable by {width}");
Self { width, height, bit_vec }
}
/// Iterate over all cells in [Bitmap].
///
/// Order is equivalent to the following loop:

View file

@ -99,4 +99,11 @@ mod tests {
assert_eq!(Brightness::MAX, Brightness::saturating_from(100));
assert_eq!(Brightness(5), Brightness::saturating_from(5));
}
#[test]
#[cfg(feature = "rand")]
fn test() {
let mut rng = rand::thread_rng();
assert_ne!(rng.gen::<Brightness>(), rng.gen());
}
}

View file

@ -26,7 +26,7 @@ impl BrightnessGrid {
}
impl From<BrightnessGrid> for Vec<u8> {
fn from(value: ValueGrid<Brightness>) -> Self {
fn from(value: BrightnessGrid) -> Self {
value
.iter()
.map(|brightness| (*brightness).into())
@ -35,7 +35,7 @@ impl From<BrightnessGrid> for Vec<u8> {
}
impl From<&BrightnessGrid> for ByteGrid {
fn from(value: &ValueGrid<Brightness>) -> Self {
fn from(value: &BrightnessGrid) -> Self {
let u8s = value
.iter()
.map(|brightness| (*brightness).into())

View file

@ -1,6 +1,5 @@
use crate::command_code::CommandCode;
use crate::compression::into_decompressed;
use crate::value_grid::ValueGrid;
use crate::*;
/// Type alias for documenting the meaning of the u16 in enum values
@ -441,7 +440,7 @@ impl Command {
payload,
} = packet;
let grid = ValueGrid::load(*width as usize, *height as usize, payload);
let grid = ByteGrid::load(*width as usize, *height as usize, payload);
let grid = match BrightnessGrid::try_from(grid) {
Ok(grid) => grid,
Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)),

View file

@ -1,6 +1,6 @@
/// The u16 command codes used for the [Command]s.
#[repr(u16)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum CommandCode {
Clear = 0x0002,
Cp437Data = 0x0003,
@ -101,3 +101,114 @@ impl TryFrom<u16> for CommandCode {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clear() {
assert_eq!(CommandCode::try_from(0x0002), Ok(CommandCode::Clear));
assert_eq!(u16::from(CommandCode::Clear), 0x0002);
}
#[test]
fn cp437_data() {
assert_eq!(CommandCode::try_from(0x0003), Ok(CommandCode::Cp437Data));
assert_eq!(u16::from(CommandCode::Cp437Data), 0x0003);
}
#[test]
fn char_brightness() {
assert_eq!(CommandCode::try_from(0x0005), Ok(CommandCode::CharBrightness));
assert_eq!(u16::from(CommandCode::CharBrightness), 0x0005);
}
#[test]
fn brightness() {
assert_eq!(CommandCode::try_from(0x0007), Ok(CommandCode::Brightness));
assert_eq!(u16::from(CommandCode::Brightness), 0x0007);
}
#[test]
fn hard_reset() {
assert_eq!(CommandCode::try_from(0x000b), Ok(CommandCode::HardReset));
assert_eq!(u16::from(CommandCode::HardReset), 0x000b);
}
#[test]
fn fade_out() {
assert_eq!(CommandCode::try_from(0x000d), Ok(CommandCode::FadeOut));
assert_eq!(u16::from(CommandCode::FadeOut), 0x000d);
}
#[test]
#[allow(deprecated)]
fn bitmap_legacy() {
assert_eq!(CommandCode::try_from(0x0010), Ok(CommandCode::BitmapLegacy));
assert_eq!(u16::from(CommandCode::BitmapLegacy), 0x0010);
}
#[test]
fn linear() {
assert_eq!(CommandCode::try_from(0x0012), Ok(CommandCode::BitmapLinear));
assert_eq!(u16::from(CommandCode::BitmapLinear), 0x0012);
}
#[test]
fn linear_and() {
assert_eq!(CommandCode::try_from(0x0014), Ok(CommandCode::BitmapLinearAnd));
assert_eq!(u16::from(CommandCode::BitmapLinearAnd), 0x0014);
}
#[test]
fn linear_xor() {
assert_eq!(CommandCode::try_from(0x0016), Ok(CommandCode::BitmapLinearXor));
assert_eq!(u16::from(CommandCode::BitmapLinearXor), 0x0016);
}
#[test]
#[cfg(feature = "compression_zlib")]
fn bitmap_win_zlib() {
assert_eq!(CommandCode::try_from(0x0017), Ok(CommandCode::BitmapLinearWinZlib));
assert_eq!(u16::from(CommandCode::BitmapLinearWinZlib), 0x0017);
}
#[test]
#[cfg(feature = "compression_bzip2")]
fn bitmap_win_bzip2() {
assert_eq!(CommandCode::try_from(0x0018), Ok(CommandCode::BitmapLinearWinBzip2));
assert_eq!(u16::from(CommandCode::BitmapLinearWinBzip2), 0x0018);
}
#[test]
#[cfg(feature = "compression_lzma")]
fn bitmap_win_lzma() {
assert_eq!(CommandCode::try_from(0x0019), Ok(CommandCode::BitmapLinearWinLzma));
assert_eq!(u16::from(CommandCode::BitmapLinearWinLzma), 0x0019);
}
#[test]
#[cfg(feature = "compression_zstd")]
fn bitmap_win_zstd() {
assert_eq!(CommandCode::try_from(0x001A), Ok(CommandCode::BitmapLinearWinZstd));
assert_eq!(u16::from(CommandCode::BitmapLinearWinZstd), 0x001A);
}
#[test]
fn bitmap_win_uncompressed() {
assert_eq!(CommandCode::try_from(0x0013), Ok(CommandCode::BitmapLinearWinUncompressed));
assert_eq!(u16::from(CommandCode::BitmapLinearWinUncompressed), 0x0013);
}
#[test]
fn utf8_data() {
assert_eq!(CommandCode::try_from(0x0020), Ok(CommandCode::Utf8Data));
assert_eq!(u16::from(CommandCode::Utf8Data), 0x0020);
}
#[test]
fn linear_or() {
assert_eq!(CommandCode::try_from(0x0015), Ok(CommandCode::BitmapLinearOr));
assert_eq!(u16::from(CommandCode::BitmapLinearOr), 0x0015);
}
}

View file

@ -65,3 +65,56 @@ impl TryFrom<u16> for CompressionCode {
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn uncompressed() {
assert_eq!(
CompressionCode::try_from(0x0000),
Ok(CompressionCode::Uncompressed)
);
assert_eq!(u16::from(CompressionCode::Uncompressed), 0x0000);
}
#[test]
#[cfg(feature = "compression_zlib")]
fn zlib() {
assert_eq!(
CompressionCode::try_from(0x677a),
Ok(CompressionCode::Zlib)
);
assert_eq!(u16::from(CompressionCode::Zlib), 0x677a);
}
#[test]
#[cfg(feature = "compression_bzip2")]
fn bzip2() {
assert_eq!(
CompressionCode::try_from(0x627a),
Ok(CompressionCode::Bzip2)
);
assert_eq!(u16::from(CompressionCode::Bzip2), 0x627a);
}
#[test]
#[cfg(feature = "compression_lzma")]
fn lzma() {
assert_eq!(
CompressionCode::try_from(0x6c7a),
Ok(CompressionCode::Lzma)
);
assert_eq!(u16::from(CompressionCode::Lzma), 0x6c7a);
}
#[test]
#[cfg(feature = "compression_zstd")]
fn zstd() {
assert_eq!(
CompressionCode::try_from(0x7a73),
Ok(CompressionCode::Zstd)
);
assert_eq!(u16::from(CompressionCode::Zstd), 0x7a73);
}
}

View file

@ -79,6 +79,21 @@ impl<T: Value> ValueGrid<T> {
}
}
/// Creates a [ValueGrid] with the specified width from the provided data without copying it.
///
/// returns: [ValueGrid] that contains the provided data.
///
/// # Panics
///
/// - when the data size is not dividable by the width.
#[must_use]
pub fn from_vec(width: usize, data: Vec<T>) -> Self {
let len = data.len();
let height = len / width;
assert_eq!(0, len % width, "dimension mismatch - len {len} is not dividable by {width}");
Self { data, width, height }
}
/// Loads a [ValueGrid] with the specified width from the provided data, wrapping to as many rows as needed.
///
/// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
@ -496,13 +511,18 @@ mod tests {
#[test]
fn ref_mut() {
let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]);
let mut vec = ValueGrid::from_vec(3, vec![0, 1, 2, 3,4,5,6,7,8]);
let top_left = vec.get_ref_mut(0, 0);
*top_left += 5;
let somewhere = vec.get_ref_mut(2, 1);
*somewhere = 42;
assert_eq!(None, vec.get_ref_mut_optional(2, 2));
assert_eq!(None, vec.get_ref_mut_optional(3, 2));
assert_eq!(None, vec.get_ref_mut_optional(2, 3));
assert_eq!(Some(&mut 5), vec.get_ref_mut_optional(0, 0));
assert_eq!(Some(&mut 42), vec.get_ref_mut_optional(2, 1));
assert_eq!(Some(&mut 8), vec.get_ref_mut_optional(2, 2));
}
#[test]

View file

@ -6,9 +6,10 @@ edition = "2021"
license = "GPL-3.0-or-later"
description = "C bindings for the servicepoint crate."
homepage = "https://docs.rs/crate/servicepoint_binding_c"
repository = "https://github.com/cccb/servicepoint"
repository = "https://git.berlin.ccc.de/servicepoint/servicepoint"
readme = "README.md"
links = "servicepoint"
keywords = ["cccb", "cccb-servicepoint", "cbindgen"]
[lib]
crate-type = ["staticlib", "cdylib", "rlib"]
@ -17,7 +18,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
cbindgen = "0.27.0"
[dependencies.servicepoint]
version = "0.13.0"
version = "0.13.1"
path = "../servicepoint"
features = ["all_compressions"]

View file

@ -60,4 +60,4 @@ You have the choice of linking statically (recommended) or dynamically.
## Everything else
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.
Look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for further information.

View file

@ -6,8 +6,9 @@ edition = "2021"
license = "GPL-3.0-or-later"
description = "C bindings for the servicepoint crate."
homepage = "https://docs.rs/crate/servicepoint_binding_c"
repository = "https://github.com/cccb/servicepoint"
repository = "https://git.berlin.ccc.de/servicepoint/servicepoint"
#readme = "README.md"
keywords = ["cccb", "cccb-servicepoint", "uniffi"]
[lib]
crate-type = ["cdylib"]
@ -20,7 +21,7 @@ uniffi = { version = "0.25.3" }
thiserror.workspace = true
[dependencies.servicepoint]
version = "0.13.0"
version = "0.13.1"
path = "../servicepoint"
features = ["all_compressions"]

View file

@ -5,7 +5,7 @@ Display" or "Airport Display".
This crate contains bindings for multiple programming languages, enabling non-rust-developers to use the library.
Also take a look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for more
Also take a look at the main project [README](https://git.berlin.ccc.de/servicepoint/servicepoint/src/branch/main/README.md) for more
information.
## Note on stability
@ -37,7 +37,7 @@ You can absolutely use it, and it works, but expect minor breaking changes with
Including this repository as a submodule and building from source is the recommended way of using the library.
```bash
git submodule add https://github.com/cccb/servicepoint.git
git submodule add https://git.berlin.ccc.de/servicepoint/servicepoint.git
git commit -m "add servicepoint submodule"
```

View file

@ -9,7 +9,7 @@
<PropertyGroup>
<PackageId>ServicePoint</PackageId>
<Version>0.13.0</Version>
<Version>0.13.1</Version>
<Authors>Repository Authors</Authors>
<Company>None</Company>
<Product>ServicePoint</Product>

View file

@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "servicepoint"
s.version = "0.13.0"
s.version = "0.13.1"
s.summary = ""
s.description = ""
s.authors = ["kaesaecracker"]

View file

@ -22,11 +22,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1736549401,
"narHash": "sha256-ibkQrMHxF/7TqAYcQE+tOnIsSEzXmMegzyBWza6uHKM=",
"lastModified": 1739357830,
"narHash": "sha256-9xim3nJJUFbVbJCz48UP4fGRStVW5nv4VdbimbKxJ3I=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "1dab772dd4a68a7bba5d9460685547ff8e17d899",
"rev": "0ff09db9d034a04acd4e8908820ba0b410d7a33a",
"type": "github"
},
"original": {

View file

@ -1,5 +1,5 @@
{
description = "Flake for servicepoint-simulator";
description = "Flake for the servicepoint library.";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
@ -23,28 +23,21 @@
"aarch64-darwin"
"x86_64-darwin"
];
forAllSystems = lib.genAttrs supported-systems;
make-rust-toolchain-core =
pkgs:
pkgs.symlinkJoin {
name = "rust-toolchain-core";
paths = with pkgs; [
rustc
cargo
rustPlatform.rustcSrc
];
};
forAllSystems =
f:
lib.genAttrs supported-systems (
system:
f rec {
pkgs = nixpkgs.legacyPackages.${system};
inherit system;
}
);
in
rec {
packages = forAllSystems (
system:
{ pkgs, ... }:
let
pkgs = nixpkgs.legacyPackages."${system}";
rust-toolchain-core = make-rust-toolchain-core pkgs;
naersk' = pkgs.callPackage naersk {
cargo = rust-toolchain-core;
rustc = rust-toolchain-core;
};
naersk' = pkgs.callPackage naersk { };
nativeBuildInputs = with pkgs; [
pkg-config
makeWrapper
@ -69,9 +62,8 @@
package
];
src = ./.;
nativeBuildInputs = nativeBuildInputs;
inherit nativeBuildInputs buildInputs;
strictDeps = true;
buildInputs = buildInputs;
gitSubmodules = true;
overrideMain = old: {
preConfigure = ''
@ -95,9 +87,8 @@
cargoTestOptions = x: x ++ package-param;
src = ./.;
doCheck = true;
nativeBuildInputs = nativeBuildInputs;
strictDeps = true;
buildInputs = buildInputs;
inherit nativeBuildInputs buildInputs;
};
in
rec {
@ -130,25 +121,24 @@
legacyPackages = packages;
devShells = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages."${system}";
rust-toolchain = pkgs.symlinkJoin {
{ pkgs, system }:
{
default = pkgs.mkShell rec {
inputsFrom = [ self.packages.${system}.servicepoint ];
packages = with pkgs; [
(pkgs.symlinkJoin
{
name = "rust-toolchain";
paths = with pkgs; [
(make-rust-toolchain-core pkgs)
rustc
cargo
rustPlatform.rustcSrc
rustfmt
clippy
cargo-expand
cargo-tarpaulin
];
};
in
{
default = pkgs.mkShell rec {
inputsFrom = [ self.packages.${system}.servicepoint ];
packages = with pkgs; [
rust-toolchain
})
ruby
dotnet-sdk_8
gcc
@ -160,6 +150,6 @@
}
);
formatter = forAllSystems (system: nixpkgs.legacyPackages."${system}".nixfmt-rfc-style);
formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style);
};
}