mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 02:00:12 +01:00
Merge branch 'uniffi2'
This commit is contained in:
commit
93657c9f85
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,8 +1,6 @@
|
|||
target
|
||||
.idea
|
||||
out
|
||||
bin
|
||||
obj
|
||||
.direnv
|
||||
.envrc
|
||||
result
|
||||
|
|
902
Cargo.lock
generated
902
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,15 +3,15 @@ resolver = "2"
|
|||
members = [
|
||||
"crates/servicepoint",
|
||||
"crates/servicepoint_binding_c",
|
||||
"crates/servicepoint_binding_cs",
|
||||
"crates/servicepoint_binding_c/examples/lang_c"
|
||||
"crates/servicepoint_binding_c/examples/lang_c",
|
||||
"crates/servicepoint_binding_uniffi"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
|
||||
[workspace.lints.rust]
|
||||
missing-docs = "warn"
|
||||
|
||||
[workspace.dependencies]
|
||||
thiserror = "1.0.69"
|
||||
thiserror = "1.0.69"
|
||||
|
|
23
README.md
23
README.md
|
@ -1,5 +1,10 @@
|
|||
# servicepoint
|
||||
|
||||
[![crates.io](https://img.shields.io/crates/v/servicepoint.svg)](https://crates.io/crates/servicepoint)
|
||||
[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint)](https://crates.io/crates/servicepoint)
|
||||
[![docs.rs](https://img.shields.io/docsrs/servicepoint)](https://docs.rs/servicepoint/latest/servicepoint/)
|
||||
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint)](../../LICENSE)
|
||||
|
||||
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
|
||||
Display" or "Airport Display".
|
||||
This repository contains a library for parsing, encoding and sending packets to this display via UDP in multiple
|
||||
|
@ -7,20 +12,20 @@ programming languages.
|
|||
|
||||
Take a look at the contained crates for language specific information:
|
||||
|
||||
| Language | Readme |
|
||||
|-----------|---------------------------------------------------------------------|
|
||||
| Rust | [servicepoint](crates/servicepoint/README.md) |
|
||||
| C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) |
|
||||
| .NET (C#) | [servicepoint_binding_cs](crates/servicepoint_binding_cs/README.md) |
|
||||
| Crate | Languages | Readme |
|
||||
|-----------------------------|-----------------------------------|-------------------------------------------------------------------------|
|
||||
| servicepoint | Rust | [servicepoint](crates/servicepoint/README.md) |
|
||||
| servicepoint_binding_c | C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) |
|
||||
| servicepoint_binding_uniffi | C# / Python / Go / Kotlin / Swift | [servicepoint_binding_cs](crates/servicepoint_binding_uniffi/README.md) |
|
||||
|
||||
## Projects using the library
|
||||
|
||||
- screen simulator (rust): [servicepoint-simulator](https://github.com/kaesaecracker/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
|
||||
- a split-flap-display simulator
|
||||
- animations that play on the display
|
||||
- a CLI tool to display image files on the display or use the display as a TTY
|
||||
- a BSD games robots clone
|
||||
- a split-flap-display simulator
|
||||
- animations that play on the display
|
||||
- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
|
||||
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ cargo add servicepoint
|
|||
or
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = "0.11.0"
|
||||
servicepoint = "0.12.0"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
|
|
@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||
cbindgen = "0.27.0"
|
||||
|
||||
[dependencies.servicepoint]
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
path = "../servicepoint"
|
||||
features = ["all_compressions"]
|
||||
|
||||
|
|
|
@ -1201,6 +1201,20 @@ SPCommand *sp_command_hard_reset(void);
|
|||
*/
|
||||
SPCommand *sp_command_try_from_packet(SPPacket *packet);
|
||||
|
||||
/**
|
||||
* Creates a new instance of [SPConnection] for testing that does not actually send anything.
|
||||
*
|
||||
* returns: a new instance. 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_connection_free`.
|
||||
*/
|
||||
SPConnection *sp_connection_fake(void);
|
||||
|
||||
/**
|
||||
* Closes and deallocates a [SPConnection].
|
||||
*
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! prefix `sp_connection_`
|
||||
|
||||
use std::ffi::{c_char, CStr};
|
||||
use std::ptr::null_mut;
|
||||
use std::ptr::{null_mut, NonNull};
|
||||
|
||||
use crate::{SPCommand, SPPacket};
|
||||
|
||||
|
@ -46,6 +46,22 @@ pub unsafe extern "C" fn sp_connection_open(
|
|||
Box::into_raw(Box::new(SPConnection(connection)))
|
||||
}
|
||||
|
||||
/// Creates a new instance of [SPConnection] for testing that does not actually send anything.
|
||||
///
|
||||
/// returns: a new instance. 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_connection_free`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_connection_fake() -> NonNull<SPConnection> {
|
||||
let result = Box::new(SPConnection(servicepoint::Connection::Fake));
|
||||
NonNull::from(Box::leak(result))
|
||||
}
|
||||
|
||||
/// Sends a [SPPacket] to the display using the [SPConnection].
|
||||
///
|
||||
/// The passed `packet` gets consumed.
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "servicepoint_binding_cs"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
publish = false
|
||||
readme = "README.md"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
test = false
|
||||
|
||||
[build-dependencies]
|
||||
csbindgen = "1.9.3"
|
||||
|
||||
[dependencies]
|
||||
servicepoint_binding_c = { version = "0.11.0", path = "../servicepoint_binding_c" }
|
||||
servicepoint = { version = "0.11.0", path = "../servicepoint" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
|
@ -1,65 +0,0 @@
|
|||
# ServicePoint
|
||||
|
||||
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
|
||||
Display" or "Airport Display".
|
||||
This crate contains C# bindings for the `servicepoint` library, enabling users to parse, encode and send packets to this display via UDP.
|
||||
|
||||
## Examples
|
||||
|
||||
```csharp
|
||||
using ServicePoint;
|
||||
|
||||
// using statement calls Dispose() on scope exit, which frees unmanaged instances
|
||||
using var connection = Connection.Open("127.0.0.1:2342");
|
||||
using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight);
|
||||
|
||||
while (true)
|
||||
{
|
||||
pixels.Fill(true);
|
||||
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
|
||||
Thread.Sleep(5000);
|
||||
|
||||
pixels.Fill(false);
|
||||
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone()));
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
```
|
||||
|
||||
A full example including project files is available as part of this crate.
|
||||
|
||||
## Note on stability
|
||||
|
||||
This library is still in early development.
|
||||
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
|
||||
|
||||
## Installation
|
||||
|
||||
NuGet packages are not a good way to distribute native projects ([relevant issue](https://github.com/dotnet/sdk/issues/33845)).
|
||||
Because of that, there is no NuGet package you can use directly.
|
||||
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 commit -m "add servicepoint submodule"
|
||||
```
|
||||
|
||||
You can now reference `servicepoint-bindings-cs/src/ServicePoint.csproj` in your project.
|
||||
The rust library will automatically be built.
|
||||
|
||||
Please provide more information in the form of an issue if you need the build to copy a different library file for your platform.
|
||||
|
||||
## Notes on differences to rust library
|
||||
|
||||
Uses C bindings internally to provide a similar API to rust. Things to keep in mind:
|
||||
|
||||
- You will get a `NullPointerException` when trying to call a method where the native instance has been consumed already (e.g. when `Send`ing a command instance twice). Send a clone instead of the original if you want to keep using it.
|
||||
- Some lower-level APIs _will_ panic in native code when used improperly.
|
||||
Example: manipulating the `Span<byte>` of an object after freeing the instance.
|
||||
- C# specifics are documented in the library. Use the rust documentation for everything else. Naming and semantics are the same apart from CamelCase instead of kebab_case.
|
||||
- You will only get rust backtraces in debug builds of the native code.
|
||||
- F# is not explicitly tested. If there are usability or functionality problems, please open an issue.
|
||||
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
|
||||
|
||||
## Everything else
|
||||
|
||||
Look at the main project [README](https://github.com/cccb/servicepoint/blob/main/README.md) for further information.
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoint/ServicePoint.csproj", "{70EFFA3F-012A-4518-9627-466BEAE4252E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{C2F8EC4A-2426-4DC3-990F-C43810B183F5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\..\shell.nix = ..\..\shell.nix
|
||||
..\..\.envrc = ..\..\.envrc
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{70EFFA3F-012A-4518-9627-466BEAE4252E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70EFFA3F-012A-4518-9627-466BEAE4252E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70EFFA3F-012A-4518-9627-466BEAE4252E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70EFFA3F-012A-4518-9627-466BEAE4252E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA3B8B6E-993A-47DA-844B-F92AF520FF59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA3B8B6E-993A-47DA-844B-F92AF520FF59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA3B8B6E-993A-47DA-844B-F92AF520FF59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA3B8B6E-993A-47DA-844B-F92AF520FF59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
File diff suppressed because it is too large
Load diff
|
@ -1,88 +0,0 @@
|
|||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
|
||||
{
|
||||
public static BitVec New(int size)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new BitVec(NativeMethods.sp_bitvec_new((nuint)size));
|
||||
}
|
||||
}
|
||||
|
||||
public static BitVec Load(Span<byte> bytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
return new BitVec(NativeMethods.sp_bitvec_load(bytesPtr, (nuint)bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BitVec Clone()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new BitVec(NativeMethods.sp_bitvec_clone(Instance));
|
||||
}
|
||||
}
|
||||
|
||||
public bool this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_bitvec_get(Instance, (nuint)index);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_bitvec_set(Instance, (nuint)index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fill(bool value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_bitvec_fill(Instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_bitvec_len(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> Data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var slice = NativeMethods.sp_bitvec_unsafe_data_ref(Instance);
|
||||
return new Span<byte>(slice.start, (int)slice.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe BitVec(BindGen.BitVec* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_bitvec_free(Instance);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class Bitmap : SpNativeInstance<BindGen.Bitmap>
|
||||
{
|
||||
public static Bitmap New(int width, int height)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Bitmap(NativeMethods.sp_bitmap_new((nuint)width, (nuint)height));
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap Load(int width, int height, Span<byte> bytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
return new Bitmap(NativeMethods.sp_bitmap_load((nuint)width, (nuint)height, bytesPtr,
|
||||
(nuint)bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap Clone()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Bitmap(NativeMethods.sp_bitmap_clone(Instance));
|
||||
}
|
||||
}
|
||||
|
||||
public bool this[int x, int y]
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_bitmap_get(Instance, (nuint)x, (nuint)y);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_bitmap_set(Instance, (nuint)x, (nuint)y, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fill(bool value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_bitmap_fill(Instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_bitmap_width(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_bitmap_height(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> Data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var slice = NativeMethods.sp_bitmap_unsafe_data_ref(Instance);
|
||||
return new Span<byte>(slice.start, (int)slice.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe Bitmap(BindGen.Bitmap* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_bitmap_free(Instance);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class BrightnessGrid : SpNativeInstance<BindGen.BrightnessGrid>
|
||||
{
|
||||
public static BrightnessGrid New(int width, int height)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new BrightnessGrid(NativeMethods.sp_brightness_grid_new((nuint)width, (nuint)height));
|
||||
}
|
||||
}
|
||||
|
||||
public static BrightnessGrid Load(int width, int height, Span<byte> bytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
return new BrightnessGrid(NativeMethods.sp_brightness_grid_load((nuint)width, (nuint)height, bytesPtr,
|
||||
(nuint)bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BrightnessGrid Clone()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new BrightnessGrid(NativeMethods.sp_brightness_grid_clone(Instance));
|
||||
}
|
||||
}
|
||||
|
||||
public byte this[int x, int y]
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_brightness_grid_get(Instance, (nuint)x, (nuint)y);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_brightness_grid_set(Instance, (nuint)x, (nuint)y, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fill(byte value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_brightness_grid_fill(Instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_brightness_grid_width(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_brightness_grid_height(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> Data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var slice = NativeMethods.sp_brightness_grid_unsafe_data_ref(Instance);
|
||||
return new Span<byte>(slice.start, (int)slice.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe BrightnessGrid(BindGen.BrightnessGrid* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_brightness_grid_free(Instance);
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class Command : SpNativeInstance<BindGen.Command>
|
||||
{
|
||||
public static bool TryFromPacket(Packet packet, [MaybeNullWhen(false)] out Command command)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var result = NativeMethods.sp_command_try_from_packet(packet.Into());
|
||||
if (result == null)
|
||||
{
|
||||
command = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
command = new Command(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Command Clone()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_clone(Instance));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command Clear()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_clear());
|
||||
}
|
||||
}
|
||||
|
||||
public static Command HardReset()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_hard_reset());
|
||||
}
|
||||
}
|
||||
|
||||
public static Command FadeOut()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_fade_out());
|
||||
}
|
||||
}
|
||||
|
||||
public static Command Brightness(byte brightness)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_brightness(brightness));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command CharBrightness(int x, int y, BrightnessGrid grid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_char_brightness((ushort)x, (ushort)y, grid.Into()));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command BitmapLinear(int offset, BitVec bitVec, CompressionCode compressionCode)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(
|
||||
NativeMethods.sp_command_bitmap_linear((ushort)offset, bitVec.Into(), compressionCode));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command BitmapLinearAnd(int offset, BitVec bitVec, CompressionCode compressionCode)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(
|
||||
NativeMethods.sp_command_bitmap_linear_and((ushort)offset, bitVec.Into(), compressionCode));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command BitmapLinearOr(int offset, BitVec bitVec, CompressionCode compressionCode)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(
|
||||
NativeMethods.sp_command_bitmap_linear_or((ushort)offset, bitVec.Into(), compressionCode));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command BitmapLinearXor(int offset, BitVec bitVec, CompressionCode compressionCode)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(
|
||||
NativeMethods.sp_command_bitmap_linear_xor((ushort)offset, bitVec.Into(), compressionCode));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command BitmapLinearWin(int x, int y, Bitmap bitmap, CompressionCode compression)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_bitmap_linear_win((ushort)x, (ushort)y, bitmap.Into(), compression));
|
||||
}
|
||||
}
|
||||
|
||||
public static Command Cp437Data(int x, int y, Cp437Grid byteGrid)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Command(NativeMethods.sp_command_cp437_data((ushort)x, (ushort)y, byteGrid.Into()));
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe Command(BindGen.Command* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_command_free(Instance);
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
using System.Text;
|
||||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class Connection : SpNativeInstance<BindGen.Connection>
|
||||
{
|
||||
public static Connection Open(string host)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytePtr = Encoding.UTF8.GetBytes(host))
|
||||
{
|
||||
return new Connection(NativeMethods.sp_connection_open(bytePtr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Send(Packet packet)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_connection_send_packet(Instance, packet.Into());
|
||||
}
|
||||
}
|
||||
|
||||
public bool Send(Command command)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_connection_send_command(Instance, command.Into());
|
||||
}
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_connection_free(Instance);
|
||||
|
||||
private unsafe Connection(BindGen.Connection* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
/// size of a single tile in one dimension
|
||||
public const nuint TileSize = NativeMethods.SP_TILE_SIZE;
|
||||
|
||||
/// tile count in the x-direction
|
||||
public const nuint TileWidth = NativeMethods.SP_TILE_WIDTH;
|
||||
|
||||
/// tile count in the y-direction
|
||||
public const nuint TileHeight = NativeMethods.SP_TILE_SIZE;
|
||||
|
||||
/// screen width in pixels
|
||||
public const nuint PixelWidth = TileWidth * TileSize;
|
||||
|
||||
/// screen height in pixels
|
||||
public const nuint PixelHeight = TileHeight * TileSize;
|
||||
|
||||
/// pixel count on whole screen
|
||||
public const nuint PixelCount = PixelWidth * PixelHeight;
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
using System.Text;
|
||||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class Cp437Grid : SpNativeInstance<BindGen.Cp437Grid>
|
||||
{
|
||||
public static Cp437Grid New(int width, int height)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Cp437Grid(NativeMethods.sp_cp437_grid_new((nuint)width, (nuint)height));
|
||||
}
|
||||
}
|
||||
|
||||
public static Cp437Grid Load(int width, int height, Span<byte> bytes)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
return new Cp437Grid(NativeMethods.sp_cp437_grid_load((nuint)width, (nuint)height, bytesPtr,
|
||||
(nuint)bytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Cp437Grid Clone()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Cp437Grid(NativeMethods.sp_cp437_grid_clone(Instance));
|
||||
}
|
||||
}
|
||||
|
||||
public byte this[int x, int y]
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return NativeMethods.sp_cp437_grid_get(Instance, (nuint)x, (nuint)y);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_cp437_grid_set(Instance, (nuint)x, (nuint)y, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string this[int y]
|
||||
{
|
||||
set
|
||||
{
|
||||
var width = Width;
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, width);
|
||||
|
||||
var x = 0;
|
||||
for (; x < value.Length; x++)
|
||||
this[x, y] = (byte)value[x];
|
||||
|
||||
for (; x < width; x++)
|
||||
this[x, y] = 0;
|
||||
}
|
||||
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
var val = this[x, y];
|
||||
if (val == 0)
|
||||
break;
|
||||
sb.Append((char)val);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void Fill(byte value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeMethods.sp_cp437_grid_fill(Instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_cp437_grid_width(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return (int)NativeMethods.sp_cp437_grid_height(Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Span<byte> Data
|
||||
{
|
||||
get
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var slice = NativeMethods.sp_cp437_grid_unsafe_data_ref(Instance);
|
||||
return new Span<byte>(slice.start, (int)slice.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe Cp437Grid(BindGen.Cp437Grid* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_cp437_grid_free(Instance);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
global using System;
|
|
@ -1,36 +0,0 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using ServicePoint.BindGen;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class Packet : SpNativeInstance<BindGen.Packet>
|
||||
{
|
||||
public static Packet FromCommand(Command command)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return new Packet(NativeMethods.sp_packet_from_command(command.Into()));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryFromBytes(Span<byte> bytes, [MaybeNullWhen(false)] out Packet packet)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
{
|
||||
var instance = NativeMethods.sp_packet_try_load(bytesPtr, (nuint)bytes.Length);
|
||||
packet = instance == null
|
||||
? null
|
||||
: new Packet(instance);
|
||||
return packet != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe Packet(BindGen.Packet* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override unsafe void Free() => NativeMethods.sp_packet_free(Instance);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ServicePoint;
|
||||
|
||||
public static class ServicePointExtensions
|
||||
{
|
||||
public static Packet IntoPacket(this Command command)
|
||||
{
|
||||
return Packet.FromCommand(command);
|
||||
}
|
||||
|
||||
public static bool TryIntoCommand(this Packet packet, [MaybeNullWhen(false)] out Command command)
|
||||
{
|
||||
return Command.TryFromPacket(packet, out command);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
namespace ServicePoint;
|
||||
|
||||
public abstract class SpNativeInstance<T>
|
||||
: IDisposable
|
||||
where T : unmanaged
|
||||
{
|
||||
private unsafe T* _instance;
|
||||
|
||||
internal unsafe T* Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
throw new NullReferenceException("instance is null");
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private protected unsafe SpNativeInstance(T* instance)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(instance);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
private protected abstract void Free();
|
||||
|
||||
internal unsafe T* Into()
|
||||
{
|
||||
var instance = _instance;
|
||||
_instance = null;
|
||||
return instance;
|
||||
}
|
||||
|
||||
private unsafe void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (_instance != null)
|
||||
Free();
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~SpNativeInstance()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
//! Build script generating the C# code needed to call methods from the `servicepoint` C library.
|
||||
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rerun-if-changed=../servicepoint_binding_c/src");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
|
||||
let mut builder = csbindgen::Builder::default();
|
||||
|
||||
let mut paths = fs::read_dir("../servicepoint_binding_c/src").unwrap()
|
||||
.map(|x| x.unwrap().path())
|
||||
.collect::<Vec<_>>();
|
||||
paths.sort();
|
||||
|
||||
for path in paths {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
builder = builder.input_extern_file(path);
|
||||
}
|
||||
|
||||
builder
|
||||
.csharp_dll_name("servicepoint_binding_c")
|
||||
.csharp_namespace("ServicePoint.BindGen")
|
||||
.csharp_use_nint_types(true)
|
||||
.csharp_class_accessibility("public")
|
||||
.csharp_generate_const_filter(|_| true)
|
||||
.csharp_type_rename(move |name| {
|
||||
if name.len() > 2
|
||||
&& name.starts_with("SP")
|
||||
&& name.chars().nth(2).unwrap().is_uppercase()
|
||||
{
|
||||
name[2..].to_string()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
})
|
||||
.generate_csharp_file("ServicePoint/BindGen/ServicePoint.g.cs")
|
||||
.unwrap();
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using ServicePoint;
|
||||
using CompressionCode = ServicePoint.BindGen.CompressionCode;
|
||||
|
||||
using var connection = Connection.Open("127.0.0.1:2342");
|
||||
|
||||
connection.Send(Command.Clear().IntoPacket());
|
||||
connection.Send(Command.Brightness(128).IntoPacket());
|
||||
|
||||
using var pixels = Bitmap.New(Constants.PixelWidth, Constants.PixelHeight);
|
||||
|
||||
for (var offset = 0; offset < int.MaxValue; offset++)
|
||||
{
|
||||
pixels.Fill(false);
|
||||
|
||||
for (var y = 0; y < pixels.Height; y++)
|
||||
pixels[(y + offset) % Constants.PixelWidth, y] = true;
|
||||
|
||||
connection.Send(Command.BitmapLinearWin(0, 0, pixels.Clone(), CompressionCode.Lzma).IntoPacket());
|
||||
Thread.Sleep(14);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
//! This crate is intentionally left empty. Only the build script is relevant here.
|
60
crates/servicepoint_binding_uniffi/Cargo.toml
Normal file
60
crates/servicepoint_binding_uniffi/Cargo.toml
Normal file
|
@ -0,0 +1,60 @@
|
|||
[package]
|
||||
name = "servicepoint_binding_uniffi"
|
||||
version.workspace = true
|
||||
publish = false
|
||||
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"
|
||||
#readme = "README.md"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.25.3", features = ["build"] }
|
||||
|
||||
[dependencies]
|
||||
uniffi = { version = "0.25.3" }
|
||||
thiserror.workspace = true
|
||||
|
||||
[dependencies.servicepoint]
|
||||
version = "0.12.0"
|
||||
path = "../servicepoint"
|
||||
features = ["all_compressions"]
|
||||
|
||||
[dependencies.uniffi-bindgen-cs]
|
||||
git = "https://github.com/NordSecurity/uniffi-bindgen-cs"
|
||||
# tag="v0.8.3+v0.25.0"
|
||||
rev = "f68639fbc720b50ebe561ba75c66c84dc456bdce"
|
||||
optional = true
|
||||
|
||||
[dependencies.uniffi-bindgen-go]
|
||||
git = "https://github.com/NordSecurity/uniffi-bindgen-go.git"
|
||||
# tag = "0.2.1+v0.25.0"
|
||||
rev = "a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
||||
optional = true
|
||||
|
||||
[lints]
|
||||
#workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen"
|
||||
required-features = ["uniffi/cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen-cs"
|
||||
required-features = ["cs"]
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen-go"
|
||||
required-features = ["go"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cs = ["dep:uniffi-bindgen-cs"]
|
||||
go = ["dep:uniffi-bindgen-go"]
|
90
crates/servicepoint_binding_uniffi/README.md
Normal file
90
crates/servicepoint_binding_uniffi/README.md
Normal file
|
@ -0,0 +1,90 @@
|
|||
# ServicePoint
|
||||
|
||||
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
|
||||
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
|
||||
information.
|
||||
|
||||
## Note on stability
|
||||
|
||||
This library is still in early development.
|
||||
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
|
||||
|
||||
## Notes on differences to rust library
|
||||
|
||||
- Performance will not be as good as the rust version:
|
||||
- most objects are reference counted.
|
||||
- objects with mutating methods will also have a MRSW lock
|
||||
- You will not get rust backtraces in release builds of the native code
|
||||
- Panic messages will work (PanicException)
|
||||
|
||||
## Supported languages
|
||||
|
||||
| Language | Support level | Notes |
|
||||
|-----------|---------------|-------------------------------------------------------------------------------------------------|
|
||||
| .NET (C#) | Full | see dedicated section |
|
||||
| Ruby | Working | LD_LIBRARY_PATH has to be set, see example project |
|
||||
| Python | Tested once | Required project file not included. The shared library will be loaded from the script location. |
|
||||
| Go | untested | |
|
||||
| Kotlin | untested | |
|
||||
| Swift | untested | |
|
||||
|
||||
## Installation
|
||||
|
||||
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 commit -m "add servicepoint submodule"
|
||||
```
|
||||
|
||||
Run `generate-bindings.sh` to regenerate all bindings. This will also build `libservicepoint.so` (or equivalent on your
|
||||
platform).
|
||||
|
||||
For languages not fully supported, there will be no project file for the library, just the naked source file(s).
|
||||
If you successfully use a language, please open an issue or PR to add the missing ones.
|
||||
|
||||
## .NET (C#)
|
||||
|
||||
This is the best supported language.
|
||||
|
||||
F# is not tested. If there are usability or functionality problems, please open an issue.
|
||||
|
||||
Currently, the project file is hard-coded for Linux and will need tweaks for other platforms (e.g. `.dylib` instead of `.so`).
|
||||
|
||||
You do not have to compile or copy the rust crate manually, as building `ServicePoint.csproj` also builds it.
|
||||
|
||||
### Example
|
||||
|
||||
```csharp
|
||||
using System.Threading;
|
||||
using ServicePoint;
|
||||
|
||||
var connection = new Connection("127.0.0.1:2342");
|
||||
connection.Send(Command.Clear());
|
||||
|
||||
connection.Send(Command.Brightness(5));
|
||||
|
||||
var pixels = Bitmap.NewMaxSized();
|
||||
for (ulong offset = 0; offset < ulong.MaxValue; offset++)
|
||||
{
|
||||
pixels.Fill(false);
|
||||
|
||||
for (ulong y = 0; y < pixels.Height(); y++)
|
||||
pixels.Set((y + offset) % pixels.Width(), y, true);
|
||||
|
||||
connection.Send(Command.BitmapLinearWin(0, 0, pixels));
|
||||
Thread.Sleep(14);
|
||||
}
|
||||
```
|
||||
|
||||
A full example including project files is available as part of this crate.
|
||||
|
||||
### Why is there no NuGet-Package?
|
||||
|
||||
NuGet packages are not a good way to distribute native
|
||||
binaries ([relevant issue](https://github.com/dotnet/sdk/issues/33845)).
|
||||
Because of that, there is no NuGet package you can use directly.
|
24
crates/servicepoint_binding_uniffi/generate-bindings.sh
Executable file
24
crates/servicepoint_binding_uniffi/generate-bindings.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cargo build --release
|
||||
|
||||
SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
TARGET_PATH="$(realpath "$SCRIPT_PATH"/../../target/release)"
|
||||
SERVICEPOINT_SO="$TARGET_PATH/libservicepoint_binding_uniffi.so"
|
||||
LIBRARIES_PATH="$SCRIPT_PATH/libraries"
|
||||
|
||||
echo "Source: $SERVICEPOINT_SO"
|
||||
echo "Output: $LIBRARIES_PATH"
|
||||
|
||||
BINDGEN="cargo run --features=uniffi/cli --bin uniffi-bindgen -- "
|
||||
BINDGEN_CS="cargo run --features=cs --bin uniffi-bindgen-cs -- "
|
||||
BINDGEN_GO="cargo run --features=go --bin uniffi-bindgen-go -- "
|
||||
COMMON_ARGS="--library $SERVICEPOINT_SO"
|
||||
|
||||
${BINDGEN} generate $COMMON_ARGS --language python --out-dir "$LIBRARIES_PATH/python"
|
||||
${BINDGEN} generate $COMMON_ARGS --language kotlin --out-dir "$LIBRARIES_PATH/kotlin"
|
||||
${BINDGEN} generate $COMMON_ARGS --language swift --out-dir "$LIBRARIES_PATH/swift"
|
||||
${BINDGEN} generate $COMMON_ARGS --language ruby --out-dir "$LIBRARIES_PATH/ruby/lib"
|
||||
${BINDGEN_CS} $COMMON_ARGS --out-dir "$LIBRARIES_PATH/csharp/ServicePoint"
|
||||
${BINDGEN_GO} $COMMON_ARGS --out-dir "$LIBRARIES_PATH/go/"
|
4
crates/servicepoint_binding_uniffi/libraries/.gitignore
vendored
Normal file
4
crates/servicepoint_binding_uniffi/libraries/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
go
|
||||
kotlin
|
||||
python
|
||||
swift
|
2
crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore
vendored
Normal file
2
crates/servicepoint_binding_uniffi/libraries/csharp/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
bin
|
||||
obj
|
2
crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore
vendored
Normal file
2
crates/servicepoint_binding_uniffi/libraries/csharp/ServicePoint.Example/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
bin
|
||||
obj
|
|
@ -0,0 +1,19 @@
|
|||
using System.Threading;
|
||||
using ServicePoint;
|
||||
|
||||
var connection = new Connection("127.0.0.1:2342");
|
||||
connection.Send(Command.Clear());
|
||||
|
||||
connection.Send(Command.Brightness(5));
|
||||
|
||||
var pixels = Bitmap.NewMaxSized();
|
||||
for (ulong offset = 0; offset < ulong.MaxValue; offset++)
|
||||
{
|
||||
pixels.Fill(false);
|
||||
|
||||
for (ulong y = 0; y < pixels.Height(); y++)
|
||||
pixels.Set((y + offset) % pixels.Width(), y, true);
|
||||
|
||||
connection.Send(Command.BitmapLinearWin(0, 0, pixels));
|
||||
Thread.Sleep(14);
|
||||
}
|
|
@ -3,13 +3,13 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>lang_cs</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>ServicePoint.Example</RootNamespace>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../ServicePoint/ServicePoint.csproj"/>
|
||||
<ProjectReference Include="../ServicePoint/ServicePoint.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,16 @@
|
|||
namespace ServicePoint.Tests;
|
||||
|
||||
public class BitmapTests
|
||||
{
|
||||
[Fact]
|
||||
public void BasicFunctions()
|
||||
{
|
||||
var bitmap = new Bitmap(8, 2);
|
||||
Assert.False(bitmap.Get(0, 0));
|
||||
Assert.False(bitmap.Get(bitmap.Width() - 1, bitmap.Height() - 1));
|
||||
bitmap.Fill(true);
|
||||
Assert.True(bitmap.Get(1, 1));
|
||||
bitmap.Set(1, 1, false);
|
||||
Assert.False(bitmap.Get(1, 1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
namespace ServicePoint.Tests;
|
||||
|
||||
public class CharGridTests
|
||||
{
|
||||
[Fact]
|
||||
public void BasicFunctions()
|
||||
{
|
||||
var grid = new CharGrid(8, 2);
|
||||
Assert.Equal("\0", grid.Get(0, 0));
|
||||
Assert.Equal("\0", grid.Get(grid.Width() - 1, grid.Height() - 1));
|
||||
grid.Fill(" ");
|
||||
Assert.Equal(" ", grid.Get(1, 1));
|
||||
grid.Set(1, 1, "-");
|
||||
Assert.Equal("-", grid.Get(1, 1));
|
||||
Assert.Throws<PanicException>(() => grid.Get(8, 2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RowAndCol()
|
||||
{
|
||||
var grid = new CharGrid(3, 2);
|
||||
Assert.Equal("\0\0\0", grid.GetRow(0));
|
||||
grid.Fill(" ");
|
||||
Assert.Equal(" ", grid.GetCol(1));
|
||||
Assert.Throws<CharGridException.OutOfBounds>(() => grid.GetCol(3));
|
||||
Assert.Throws<CharGridException.InvalidSeriesLength>(() => grid.SetRow(1, "Text"));
|
||||
grid.SetRow(1, "Foo");
|
||||
Assert.Equal("Foo", grid.GetRow(1));
|
||||
Assert.Equal(" o", grid.GetCol(2));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
namespace ServicePoint.Tests;
|
||||
|
||||
public class CommandTests
|
||||
{
|
||||
private Connection _connection = Connection.NewFake();
|
||||
|
||||
[Fact]
|
||||
public void ClearSendable()
|
||||
{
|
||||
_connection.Send(Command.Clear());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BrightnessSendable()
|
||||
{
|
||||
_connection.Send(Command.Brightness(5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidBrightnessThrows()
|
||||
{
|
||||
Assert.Throws<ServicePointException.InvalidBrightness>(() => Command.Brightness(42));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FadeOutSendable()
|
||||
{
|
||||
_connection.Send(Command.FadeOut());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HardResetSendable()
|
||||
{
|
||||
_connection.Send(Command.HardReset());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitmapLinearWinSendable()
|
||||
{
|
||||
_connection.Send(Command.BitmapLinearWin(0, 0, Bitmap.NewMaxSized(), CompressionCode.Uncompressed));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace ServicePoint.Tests;
|
||||
|
||||
public class ConnectionTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidHostnameThrows()
|
||||
{
|
||||
Assert.Throws<ServicePointException.IoException>(() => new Connection(""));
|
||||
Assert.Throws<ServicePointException.IoException>(() => new Connection("-%6$§"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
global using Xunit;
|
||||
global using ServicePoint;
|
|
@ -0,0 +1,27 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../ServicePoint/ServicePoint.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
using ServicePoint;
|
||||
|
||||
public static class ServicePointConstants
|
||||
{
|
||||
private static readonly Constants _instance = ServicepointBindingUniffiMethods.GetConstants();
|
||||
|
||||
public static readonly ulong PixelWidth = _instance.pixelWidth;
|
||||
public static readonly ulong PixelHeight = _instance.pixelHeight;
|
||||
public static readonly ulong PixelCount = _instance.pixelCount;
|
||||
public static readonly ulong TileWidth = _instance.tileWidth;
|
||||
public static readonly ulong TileHeight = _instance.tileHeight;
|
||||
public static readonly ulong TileSize = _instance.tileSize;
|
||||
}
|
|
@ -5,13 +5,11 @@
|
|||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>ServicePoint</PackageId>
|
||||
<Version>0.11.0</Version>
|
||||
<Version>0.12.0</Version>
|
||||
<Authors>Repository Authors</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>ServicePoint</Product>
|
||||
|
@ -27,30 +25,28 @@
|
|||
|
||||
<!-- generate C# bindings -->
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="cargo build --release"/>
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml --release"/>
|
||||
<Exec Command="cargo build -p servicepoint_binding_uniffi --release"/>
|
||||
</Target>
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="cargo build"/>
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/>
|
||||
<Exec Command="cargo build -p servicepoint_binding_uniffi"/>
|
||||
</Target>
|
||||
|
||||
<!-- include native binary in output -->
|
||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
|
||||
<Link>libservicepoint_binding_c.so</Link>
|
||||
<Content Include="../../../../../target/debug/libservicepoint_binding_uniffi.so" CopyToOutputDirectory="Always">
|
||||
<Link>libservicepoint_binding_uniffi.so</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
||||
<Content Include="../../../target/release/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
|
||||
<Link>libservicepoint_binding_c.so</Link>
|
||||
<Content Include="../../../../../target/release/libservicepoint_binding_uniffi.so" CopyToOutputDirectory="Always">
|
||||
<Link>libservicepoint_binding_uniffi.so</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- include link to source code at revision -->
|
||||
<None Include="../README.md" Pack="true" PackagePath="\"/>
|
||||
<!-- add README.md to package -->
|
||||
<None Include="../README.md" Pack="true" PackagePath="\"/>
|
||||
<!-- include link to source code at revision -->
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
|
||||
</ItemGroup>
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,34 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoint\ServicePoint.csproj", "{53576D3C-E32E-49BF-BF10-2DB504E50CE1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint.Example", "ServicePoint.Example\ServicePoint.Example.csproj", "{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint.Tests", "ServicePoint.Tests\ServicePoint.Tests.csproj", "{9DC15508-A980-4135-9FC6-659FF54B4E5C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{53576D3C-E32E-49BF-BF10-2DB504E50CE1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FEF24227-090E-46C2-B8F6-ACB5AA1A4309}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9DC15508-A980-4135-9FC6-659FF54B4E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9DC15508-A980-4135-9FC6-659FF54B4E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9DC15508-A980-4135-9FC6-659FF54B4E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9DC15508-A980-4135-9FC6-659FF54B4E5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,3 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'servicepoint', path: '..'
|
|
@ -0,0 +1,19 @@
|
|||
PATH
|
||||
remote: ..
|
||||
specs:
|
||||
servicepoint (0.0.0)
|
||||
ffi
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ffi (1.17.0-x86_64-linux-gnu)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
servicepoint!
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.27
|
|
@ -0,0 +1,25 @@
|
|||
require_relative "../lib/servicepoint_binding_uniffi"
|
||||
|
||||
include ServicepointBindingUniffi
|
||||
|
||||
connection = Connection.new("172.23.42.29:2342")
|
||||
|
||||
pixels = Bitmap.new_max_sized
|
||||
x_offset = 0
|
||||
loop do
|
||||
|
||||
pixels.fill(false)
|
||||
|
||||
(0..((pixels.height) -1)).each do |y|
|
||||
pixels.set((y + x_offset) % pixels.width, y, true);
|
||||
end
|
||||
|
||||
command = Command.bitmap_linear_win(0, 0, pixels, CompressionCode::UNCOMPRESSED)
|
||||
|
||||
connection.send(command)
|
||||
sleep 0.0005
|
||||
|
||||
x_offset += 1
|
||||
end
|
||||
|
||||
|
3
crates/servicepoint_binding_uniffi/libraries/ruby/example/example.sh
Executable file
3
crates/servicepoint_binding_uniffi/libraries/ruby/example/example.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
LD_LIBRARY_PATH="../../../../../target/release:$LD_LIBRARY_PATH" ruby example.rb
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,13 @@
|
|||
Gem::Specification.new do |s|
|
||||
s.name = "servicepoint"
|
||||
s.version = "0.12.0"
|
||||
s.summary = ""
|
||||
s.description = ""
|
||||
s.authors = ["kaesaecracker"]
|
||||
s.email = ""
|
||||
s.files = ["lib/servicepoint_binding_uniffi.rb"]
|
||||
s.homepage =
|
||||
"https://rubygems.org/gems/hola"
|
||||
s.license = "MIT"
|
||||
s.add_dependency 'ffi'
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
uniffi_bindgen_cs::main().unwrap();
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
uniffi_bindgen_go::main().unwrap();
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
77
crates/servicepoint_binding_uniffi/src/bitmap.rs
Normal file
77
crates/servicepoint_binding_uniffi/src/bitmap.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use servicepoint::{DataRef, Grid};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct Bitmap {
|
||||
pub(crate) actual: RwLock<servicepoint::Bitmap>,
|
||||
}
|
||||
|
||||
impl Bitmap {
|
||||
fn internal_new(actual: servicepoint::Bitmap) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: RwLock::new(actual),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl Bitmap {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(width: u64, height: u64) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Bitmap::new(
|
||||
width as usize,
|
||||
height as usize,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn new_max_sized() -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Bitmap::max_sized())
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn load(width: u64, height: u64, data: Vec<u8>) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Bitmap::load(
|
||||
width as usize,
|
||||
height as usize,
|
||||
&data,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.read().unwrap().clone())
|
||||
}
|
||||
|
||||
pub fn set(&self, x: u64, y: u64, value: bool) {
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.set(x as usize, y as usize, value)
|
||||
}
|
||||
|
||||
pub fn get(&self, x: u64, y: u64) -> bool {
|
||||
self.actual.read().unwrap().get(x as usize, y as usize)
|
||||
}
|
||||
|
||||
pub fn fill(&self, value: bool) {
|
||||
self.actual.write().unwrap().fill(value)
|
||||
}
|
||||
pub fn width(&self) -> u64 {
|
||||
self.actual.read().unwrap().width() as u64
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u64 {
|
||||
self.actual.read().unwrap().height() as u64
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &Bitmap) -> bool {
|
||||
let a = self.actual.read().unwrap();
|
||||
let b = other.actual.read().unwrap();
|
||||
*a == *b
|
||||
}
|
||||
|
||||
pub fn copy_raw(&self) -> Vec<u8> {
|
||||
self.actual.read().unwrap().data_ref().to_vec()
|
||||
}
|
||||
}
|
61
crates/servicepoint_binding_uniffi/src/bitvec.rs
Normal file
61
crates/servicepoint_binding_uniffi/src/bitvec.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct BitVec {
|
||||
pub(crate) actual: RwLock<servicepoint::BitVec>,
|
||||
}
|
||||
|
||||
impl BitVec {
|
||||
fn internal_new(actual: servicepoint::BitVec) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: RwLock::new(actual),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl BitVec {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(size: u64) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::BitVec::repeat(false, size as usize))
|
||||
}
|
||||
#[uniffi::constructor]
|
||||
pub fn load(data: Vec<u8>) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::BitVec::from_slice(&data))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.read().unwrap().clone())
|
||||
}
|
||||
|
||||
pub fn set(&self, index: u64, value: bool) {
|
||||
self.actual.write().unwrap().set(index as usize, value)
|
||||
}
|
||||
|
||||
pub fn get(&self, index: u64) -> bool {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(index as usize)
|
||||
.is_some_and(move |bit| *bit)
|
||||
}
|
||||
|
||||
pub fn fill(&self, value: bool) {
|
||||
self.actual.write().unwrap().fill(value)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> u64 {
|
||||
self.actual.read().unwrap().len() as u64
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &BitVec) -> bool {
|
||||
let a = self.actual.read().unwrap();
|
||||
let b = other.actual.read().unwrap();
|
||||
*a == *b
|
||||
}
|
||||
|
||||
pub fn copy_raw(&self) -> Vec<u8> {
|
||||
self.actual.read().unwrap().clone().into_vec()
|
||||
}
|
||||
}
|
86
crates/servicepoint_binding_uniffi/src/brightness_grid.rs
Normal file
86
crates/servicepoint_binding_uniffi/src/brightness_grid.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use servicepoint::{Brightness, DataRef, Grid};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct BrightnessGrid {
|
||||
pub(crate) actual: RwLock<servicepoint::BrightnessGrid>,
|
||||
}
|
||||
|
||||
impl BrightnessGrid {
|
||||
fn internal_new(actual: servicepoint::BrightnessGrid) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: RwLock::new(actual),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl BrightnessGrid {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(width: u64, height: u64) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::BrightnessGrid::new(
|
||||
width as usize,
|
||||
height as usize,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn load(width: u64, height: u64, data: Vec<u8>) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::BrightnessGrid::saturating_load(
|
||||
width as usize,
|
||||
height as usize,
|
||||
&data,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.read().unwrap().clone())
|
||||
}
|
||||
|
||||
pub fn set(&self, x: u64, y: u64, value: u8) {
|
||||
self.actual.write().unwrap().set(
|
||||
x as usize,
|
||||
y as usize,
|
||||
Brightness::saturating_from(value),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get(&self, x: u64, y: u64) -> u8 {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(x as usize, y as usize)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn fill(&self, value: u8) {
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.fill(Brightness::saturating_from(value))
|
||||
}
|
||||
pub fn width(&self) -> u64 {
|
||||
self.actual.read().unwrap().width() as u64
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u64 {
|
||||
self.actual.read().unwrap().height() as u64
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &BrightnessGrid) -> bool {
|
||||
let a = self.actual.read().unwrap();
|
||||
let b = other.actual.read().unwrap();
|
||||
*a == *b
|
||||
}
|
||||
|
||||
pub fn copy_raw(&self) -> Vec<u8> {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.data_ref()
|
||||
.iter()
|
||||
.map(u8::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
163
crates/servicepoint_binding_uniffi/src/char_grid.rs
Normal file
163
crates/servicepoint_binding_uniffi/src/char_grid.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
use servicepoint::{Grid, SeriesError};
|
||||
use std::convert::Into;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use crate::cp437_grid::Cp437Grid;
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct CharGrid {
|
||||
pub(crate) actual: RwLock<servicepoint::CharGrid>,
|
||||
}
|
||||
|
||||
#[derive(uniffi::Error, thiserror::Error, Debug)]
|
||||
pub enum CharGridError {
|
||||
#[error("Exactly one character was expected, but {value:?} was provided")]
|
||||
StringNotOneChar { value: String },
|
||||
#[error("The provided series was expected to have a length of {expected}, but was {actual}")]
|
||||
InvalidSeriesLength { actual: u64, expected: u64 },
|
||||
#[error("The index {index} was out of bounds for size {size}")]
|
||||
OutOfBounds { index: u64, size: u64 },
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl CharGrid {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(width: u64, height: u64) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::CharGrid::new(
|
||||
width as usize,
|
||||
height as usize,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn load(data: String) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::CharGrid::from(&*data))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.read().unwrap().clone())
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&self,
|
||||
x: u64,
|
||||
y: u64,
|
||||
value: String,
|
||||
) -> Result<(), CharGridError> {
|
||||
let value = Self::str_to_char(value)?;
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.set(x as usize, y as usize, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, x: u64, y: u64) -> String {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(x as usize, y as usize)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn fill(&self, value: String) -> Result<(), CharGridError> {
|
||||
let value = Self::str_to_char(value)?;
|
||||
self.actual.write().unwrap().fill(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u64 {
|
||||
self.actual.read().unwrap().width() as u64
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u64 {
|
||||
self.actual.read().unwrap().height() as u64
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &CharGrid) -> bool {
|
||||
let a = self.actual.read().unwrap();
|
||||
let b = other.actual.read().unwrap();
|
||||
*a == *b
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
let grid = self.actual.read().unwrap();
|
||||
String::from(&*grid)
|
||||
}
|
||||
|
||||
pub fn set_row(&self, y: u64, row: String) -> Result<(), CharGridError> {
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_row(y as usize, &row.chars().collect::<Vec<_>>())
|
||||
.map_err(CharGridError::from)
|
||||
}
|
||||
|
||||
pub fn set_col(&self, x: u64, col: String) -> Result<(), CharGridError> {
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_row(x as usize, &col.chars().collect::<Vec<_>>())
|
||||
.map_err(CharGridError::from)
|
||||
}
|
||||
|
||||
pub fn get_row(&self, y: u64) -> Result<String, CharGridError> {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_row(y as usize)
|
||||
.map(String::from_iter)
|
||||
.ok_or(CharGridError::OutOfBounds {index: y, size: self.height()})
|
||||
}
|
||||
|
||||
pub fn get_col(&self, x: u64) -> Result<String, CharGridError> {
|
||||
self.actual
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_col(x as usize)
|
||||
.map(String::from_iter)
|
||||
.ok_or(CharGridError::OutOfBounds {index: x, size: self.width()})
|
||||
}
|
||||
|
||||
pub fn to_cp437(&self) -> Arc<Cp437Grid> {
|
||||
Cp437Grid::internal_new(servicepoint::Cp437Grid::from(&*self.actual.read().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CharGrid {
|
||||
pub(crate) fn internal_new(actual: servicepoint::CharGrid) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: RwLock::new(actual),
|
||||
})
|
||||
}
|
||||
|
||||
fn str_to_char(value: String) -> Result<char, CharGridError> {
|
||||
if value.len() != 1 {
|
||||
return Err(CharGridError::StringNotOneChar {
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
let value = value.chars().nth(0).unwrap();
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SeriesError> for CharGridError {
|
||||
fn from(e: SeriesError) -> Self {
|
||||
match e {
|
||||
SeriesError::OutOfBounds { index, size } => {
|
||||
CharGridError::OutOfBounds {
|
||||
index: index as u64,
|
||||
size: size as u64,
|
||||
}
|
||||
}
|
||||
SeriesError::InvalidLength { actual, expected } => {
|
||||
CharGridError::InvalidSeriesLength {
|
||||
actual: actual as u64,
|
||||
expected: expected as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
162
crates/servicepoint_binding_uniffi/src/command.rs
Normal file
162
crates/servicepoint_binding_uniffi/src/command.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
use crate::bitmap::Bitmap;
|
||||
use crate::bitvec::BitVec;
|
||||
use crate::brightness_grid::BrightnessGrid;
|
||||
use crate::compression_code::CompressionCode;
|
||||
use crate::cp437_grid::Cp437Grid;
|
||||
use crate::errors::ServicePointError;
|
||||
use servicepoint::Origin;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct Command {
|
||||
pub(crate) actual: servicepoint::Command,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn internal_new(actual: servicepoint::Command) -> Arc<Command> {
|
||||
Arc::new(Command { actual })
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl Command {
|
||||
#[uniffi::constructor]
|
||||
pub fn clear() -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Command::Clear)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn brightness(brightness: u8) -> Result<Arc<Self>, ServicePointError> {
|
||||
servicepoint::Brightness::try_from(brightness)
|
||||
.map_err(move |value| ServicePointError::InvalidBrightness {
|
||||
value,
|
||||
})
|
||||
.map(servicepoint::Command::Brightness)
|
||||
.map(Self::internal_new)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn fade_out() -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Command::FadeOut)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn hard_reset() -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Command::HardReset)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn bitmap_linear_win(
|
||||
offset_x: u64,
|
||||
offset_y: u64,
|
||||
bitmap: &Arc<Bitmap>,
|
||||
compression: CompressionCode,
|
||||
) -> Arc<Self> {
|
||||
let origin = Origin::new(offset_x as usize, offset_y as usize);
|
||||
let bitmap = bitmap.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::BitmapLinearWin(
|
||||
origin,
|
||||
bitmap,
|
||||
servicepoint::CompressionCode::try_from(compression as u16)
|
||||
.unwrap(),
|
||||
);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn char_brightness(
|
||||
offset_x: u64,
|
||||
offset_y: u64,
|
||||
grid: &Arc<BrightnessGrid>,
|
||||
) -> Arc<Self> {
|
||||
let origin = Origin::new(offset_x as usize, offset_y as usize);
|
||||
let grid = grid.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::CharBrightness(origin, grid);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn bitmap_linear(
|
||||
offset: u64,
|
||||
bitmap: &Arc<BitVec>,
|
||||
compression: CompressionCode,
|
||||
) -> Arc<Self> {
|
||||
let bitmap = bitmap.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::BitmapLinear(
|
||||
offset as usize,
|
||||
bitmap,
|
||||
servicepoint::CompressionCode::try_from(compression as u16)
|
||||
.unwrap(),
|
||||
);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn bitmap_linear_and(
|
||||
offset: u64,
|
||||
bitmap: &Arc<BitVec>,
|
||||
compression: CompressionCode,
|
||||
) -> Arc<Self> {
|
||||
let bitmap = bitmap.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::BitmapLinearAnd(
|
||||
offset as usize,
|
||||
bitmap,
|
||||
servicepoint::CompressionCode::try_from(compression as u16)
|
||||
.unwrap(),
|
||||
);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn bitmap_linear_or(
|
||||
offset: u64,
|
||||
bitmap: &Arc<BitVec>,
|
||||
compression: CompressionCode,
|
||||
) -> Arc<Self> {
|
||||
let bitmap = bitmap.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::BitmapLinearOr(
|
||||
offset as usize,
|
||||
bitmap,
|
||||
servicepoint::CompressionCode::try_from(compression as u16)
|
||||
.unwrap(),
|
||||
);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn bitmap_linear_xor(
|
||||
offset: u64,
|
||||
bitmap: &Arc<BitVec>,
|
||||
compression: CompressionCode,
|
||||
) -> Arc<Self> {
|
||||
let bitmap = bitmap.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::BitmapLinearXor(
|
||||
offset as usize,
|
||||
bitmap,
|
||||
servicepoint::CompressionCode::try_from(compression as u16)
|
||||
.unwrap(),
|
||||
);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn cp437_data(
|
||||
offset_x: u64,
|
||||
offset_y: u64,
|
||||
grid: &Arc<Cp437Grid>,
|
||||
) -> Arc<Self> {
|
||||
let origin = Origin::new(offset_x as usize, offset_y as usize);
|
||||
let grid = grid.actual.read().unwrap().clone();
|
||||
let actual = servicepoint::Command::Cp437Data(origin, grid);
|
||||
Self::internal_new(actual)
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.clone())
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &Command) -> bool {
|
||||
self.actual == other.actual
|
||||
}
|
||||
}
|
14
crates/servicepoint_binding_uniffi/src/compression_code.rs
Normal file
14
crates/servicepoint_binding_uniffi/src/compression_code.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#[repr(u16)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, uniffi::Enum)]
|
||||
pub enum CompressionCode {
|
||||
/// no compression
|
||||
Uncompressed = 0x0,
|
||||
/// compress using flate2 with zlib header
|
||||
Zlib = 0x677a,
|
||||
/// compress using bzip2
|
||||
Bzip2 = 0x627a,
|
||||
/// compress using lzma
|
||||
Lzma = 0x6c7a,
|
||||
/// compress using Zstandard
|
||||
Zstd = 0x7a73,
|
||||
}
|
36
crates/servicepoint_binding_uniffi/src/connection.rs
Normal file
36
crates/servicepoint_binding_uniffi/src/connection.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::command::Command;
|
||||
use crate::errors::ServicePointError;
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct Connection {
|
||||
actual: servicepoint::Connection,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl Connection {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(host: String) -> Result<Arc<Self>, ServicePointError> {
|
||||
servicepoint::Connection::open(host)
|
||||
.map(|actual| Arc::new(Connection { actual }))
|
||||
.map_err(|err| ServicePointError::IoError {
|
||||
error: err.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn new_fake() -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: servicepoint::Connection::Fake,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(&self, command: Arc<Command>) -> Result<(), ServicePointError> {
|
||||
self.actual.send(command.actual.clone()).map_err(|err| {
|
||||
ServicePointError::IoError {
|
||||
error: format!("{err:?}"),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
21
crates/servicepoint_binding_uniffi/src/constants.rs
Normal file
21
crates/servicepoint_binding_uniffi/src/constants.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record)]
|
||||
pub struct Constants {
|
||||
pub tile_size: u64,
|
||||
pub tile_width: u64,
|
||||
pub tile_height: u64,
|
||||
pub pixel_width: u64,
|
||||
pub pixel_height: u64,
|
||||
pub pixel_count: u64,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn get_constants() -> Constants {
|
||||
Constants {
|
||||
tile_size: servicepoint::TILE_SIZE as u64,
|
||||
tile_width: servicepoint::TILE_WIDTH as u64,
|
||||
tile_height: servicepoint::TILE_HEIGHT as u64,
|
||||
pixel_width: servicepoint::PIXEL_WIDTH as u64,
|
||||
pixel_height: servicepoint::PIXEL_HEIGHT as u64,
|
||||
pixel_count: servicepoint::PIXEL_COUNT as u64,
|
||||
}
|
||||
}
|
77
crates/servicepoint_binding_uniffi/src/cp437_grid.rs
Normal file
77
crates/servicepoint_binding_uniffi/src/cp437_grid.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use servicepoint::{DataRef, Grid};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use crate::char_grid::CharGrid;
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct Cp437Grid {
|
||||
pub(crate) actual: RwLock<servicepoint::Cp437Grid>,
|
||||
}
|
||||
|
||||
impl Cp437Grid {
|
||||
pub(crate) fn internal_new(actual: servicepoint::Cp437Grid) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
actual: RwLock::new(actual),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl Cp437Grid {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(width: u64, height: u64) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Cp437Grid::new(
|
||||
width as usize,
|
||||
height as usize,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn load(width: u64, height: u64, data: Vec<u8>) -> Arc<Self> {
|
||||
Self::internal_new(servicepoint::Cp437Grid::load(
|
||||
width as usize,
|
||||
height as usize,
|
||||
&data,
|
||||
))
|
||||
}
|
||||
|
||||
#[uniffi::constructor]
|
||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||
Self::internal_new(other.actual.read().unwrap().clone())
|
||||
}
|
||||
|
||||
pub fn set(&self, x: u64, y: u64, value: u8) {
|
||||
self.actual
|
||||
.write()
|
||||
.unwrap()
|
||||
.set(x as usize, y as usize, value)
|
||||
}
|
||||
|
||||
pub fn get(&self, x: u64, y: u64) -> u8 {
|
||||
self.actual.read().unwrap().get(x as usize, y as usize)
|
||||
}
|
||||
|
||||
pub fn fill(&self, value: u8) {
|
||||
self.actual.write().unwrap().fill(value)
|
||||
}
|
||||
pub fn width(&self) -> u64 {
|
||||
self.actual.read().unwrap().width() as u64
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u64 {
|
||||
self.actual.read().unwrap().height() as u64
|
||||
}
|
||||
|
||||
pub fn equals(&self, other: &Cp437Grid) -> bool {
|
||||
let a = self.actual.read().unwrap();
|
||||
let b = other.actual.read().unwrap();
|
||||
*a == *b
|
||||
}
|
||||
|
||||
pub fn copy_raw(&self) -> Vec<u8> {
|
||||
self.actual.read().unwrap().data_ref().to_vec()
|
||||
}
|
||||
|
||||
pub fn to_utf8(&self) -> Arc<CharGrid> {
|
||||
CharGrid::internal_new(servicepoint::CharGrid::from(&*self.actual.read().unwrap()))
|
||||
}
|
||||
}
|
7
crates/servicepoint_binding_uniffi/src/errors.rs
Normal file
7
crates/servicepoint_binding_uniffi/src/errors.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#[derive(uniffi::Error, thiserror::Error, Debug)]
|
||||
pub enum ServicePointError {
|
||||
#[error("An IO error occurred: {error}")]
|
||||
IoError { error: String },
|
||||
#[error("The specified brightness value {value} is out of range")]
|
||||
InvalidBrightness { value: u8 },
|
||||
}
|
12
crates/servicepoint_binding_uniffi/src/lib.rs
Normal file
12
crates/servicepoint_binding_uniffi/src/lib.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
uniffi::setup_scaffolding!();
|
||||
|
||||
mod bitmap;
|
||||
mod bitvec;
|
||||
mod brightness_grid;
|
||||
mod char_grid;
|
||||
mod command;
|
||||
mod compression_code;
|
||||
mod connection;
|
||||
mod cp437_grid;
|
||||
mod errors;
|
||||
mod constants;
|
3
crates/servicepoint_binding_uniffi/uniffi.toml
Normal file
3
crates/servicepoint_binding_uniffi/uniffi.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[bindings.csharp]
|
||||
namespace = "ServicePoint"
|
||||
access_modifier = "public"
|
55
flake.nix
55
flake.nix
|
@ -54,7 +54,11 @@
|
|||
lzma
|
||||
];
|
||||
makeExample =
|
||||
package: example:
|
||||
{
|
||||
package,
|
||||
example,
|
||||
features ? "",
|
||||
}:
|
||||
naersk'.buildPackage {
|
||||
pname = example;
|
||||
cargoBuildOptions =
|
||||
|
@ -68,11 +72,14 @@
|
|||
nativeBuildInputs = nativeBuildInputs;
|
||||
strictDeps = true;
|
||||
buildInputs = buildInputs;
|
||||
gitSubmodules = true;
|
||||
overrideMain = old: {
|
||||
preConfigure = ''
|
||||
cargo_build_options="$cargo_build_options --example ${example}"
|
||||
'';
|
||||
};
|
||||
preConfigure = ''
|
||||
cargo_build_options="$cargo_build_options --example ${example} ${
|
||||
if features == "" then "" else "--features " + features
|
||||
}"
|
||||
'';
|
||||
};
|
||||
};
|
||||
makePackage =
|
||||
package:
|
||||
|
@ -95,11 +102,28 @@
|
|||
in
|
||||
rec {
|
||||
servicepoint = makePackage "servicepoint";
|
||||
announce = makeExample "servicepoint" "announce";
|
||||
game-of-life = makeExample "servicepoint" "game_of_life";
|
||||
moving-line = makeExample "servicepoint" "moving_line";
|
||||
random-brightness = makeExample "servicepoint" "random_brightness";
|
||||
wiping-clear = makeExample "servicepoint" "wiping_clear";
|
||||
announce = makeExample {
|
||||
package = "servicepoint";
|
||||
example = "announce";
|
||||
};
|
||||
game-of-life = makeExample {
|
||||
package = "servicepoint";
|
||||
example = "game_of_life";
|
||||
features = "rand";
|
||||
};
|
||||
moving-line = makeExample {
|
||||
package = "servicepoint";
|
||||
example = "moving_line";
|
||||
};
|
||||
random-brightness = makeExample {
|
||||
package = "servicepoint";
|
||||
example = "random_brightness";
|
||||
features = "rand";
|
||||
};
|
||||
wiping-clear = makeExample {
|
||||
package = "servicepoint";
|
||||
example = "wiping_clear";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -117,16 +141,19 @@
|
|||
clippy
|
||||
cargo-expand
|
||||
cargo-tarpaulin
|
||||
gcc
|
||||
gnumake
|
||||
dotnet-sdk_8
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell rec {
|
||||
inputsFrom = [ self.packages.${system}.servicepoint ];
|
||||
packages = [ rust-toolchain ];
|
||||
packages = with pkgs; [
|
||||
rust-toolchain
|
||||
ruby
|
||||
dotnet-sdk_8
|
||||
gcc
|
||||
gnumake
|
||||
];
|
||||
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}";
|
||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue