mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 18:10:14 +01:00
Merge branch 'uniffi2'
This commit is contained in:
commit
93657c9f85
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,8 +1,6 @@
|
||||||
target
|
target
|
||||||
.idea
|
.idea
|
||||||
out
|
out
|
||||||
bin
|
|
||||||
obj
|
|
||||||
.direnv
|
.direnv
|
||||||
.envrc
|
.envrc
|
||||||
result
|
result
|
||||||
|
|
902
Cargo.lock
generated
902
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,12 +3,12 @@ resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"crates/servicepoint",
|
"crates/servicepoint",
|
||||||
"crates/servicepoint_binding_c",
|
"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]
|
[workspace.package]
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing-docs = "warn"
|
missing-docs = "warn"
|
||||||
|
|
23
README.md
23
README.md
|
@ -1,5 +1,10 @@
|
||||||
# servicepoint
|
# 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
|
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".
|
Display" or "Airport Display".
|
||||||
This repository contains a library for parsing, encoding and sending packets to this display via UDP in multiple
|
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:
|
Take a look at the contained crates for language specific information:
|
||||||
|
|
||||||
| Language | Readme |
|
| Crate | Languages | Readme |
|
||||||
|-----------|---------------------------------------------------------------------|
|
|-----------------------------|-----------------------------------|-------------------------------------------------------------------------|
|
||||||
| Rust | [servicepoint](crates/servicepoint/README.md) |
|
| servicepoint | Rust | [servicepoint](crates/servicepoint/README.md) |
|
||||||
| C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) |
|
| servicepoint_binding_c | C / C++ | [servicepoint_binding_c](crates/servicepoint_binding_c/README.md) |
|
||||||
| .NET (C#) | [servicepoint_binding_cs](crates/servicepoint_binding_cs/README.md) |
|
| servicepoint_binding_uniffi | C# / Python / Go / Kotlin / Swift | [servicepoint_binding_cs](crates/servicepoint_binding_uniffi/README.md) |
|
||||||
|
|
||||||
## Projects using the library
|
## Projects using the library
|
||||||
|
|
||||||
- screen simulator (rust): [servicepoint-simulator](https://github.com/kaesaecracker/servicepoint-simulator)
|
- 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 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 CLI tool to display image files on the display or use the display as a TTY
|
||||||
- a BSD games robots clone
|
- a BSD games robots clone
|
||||||
- a split-flap-display simulator
|
- a split-flap-display simulator
|
||||||
- animations that play on the display
|
- animations that play on the display
|
||||||
- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
|
- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
|
||||||
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
|
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ cargo add servicepoint
|
||||||
or
|
or
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
servicepoint = "0.11.0"
|
servicepoint = "0.12.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
|
@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
cbindgen = "0.27.0"
|
cbindgen = "0.27.0"
|
||||||
|
|
||||||
[dependencies.servicepoint]
|
[dependencies.servicepoint]
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
path = "../servicepoint"
|
path = "../servicepoint"
|
||||||
features = ["all_compressions"]
|
features = ["all_compressions"]
|
||||||
|
|
||||||
|
|
|
@ -1201,6 +1201,20 @@ SPCommand *sp_command_hard_reset(void);
|
||||||
*/
|
*/
|
||||||
SPCommand *sp_command_try_from_packet(SPPacket *packet);
|
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].
|
* Closes and deallocates a [SPConnection].
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//! prefix `sp_connection_`
|
//! prefix `sp_connection_`
|
||||||
|
|
||||||
use std::ffi::{c_char, CStr};
|
use std::ffi::{c_char, CStr};
|
||||||
use std::ptr::null_mut;
|
use std::ptr::{null_mut, NonNull};
|
||||||
|
|
||||||
use crate::{SPCommand, SPPacket};
|
use crate::{SPCommand, SPPacket};
|
||||||
|
|
||||||
|
@ -46,6 +46,22 @@ pub unsafe extern "C" fn sp_connection_open(
|
||||||
Box::into_raw(Box::new(SPConnection(connection)))
|
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].
|
/// Sends a [SPPacket] to the display using the [SPConnection].
|
||||||
///
|
///
|
||||||
/// The passed `packet` gets consumed.
|
/// 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>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>lang_cs</RootNamespace>
|
<RootNamespace>ServicePoint.Example</RootNamespace>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../ServicePoint/ServicePoint.csproj"/>
|
<ProjectReference Include="../ServicePoint/ServicePoint.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
|
||||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>ServicePoint</PackageId>
|
<PackageId>ServicePoint</PackageId>
|
||||||
<Version>0.11.0</Version>
|
<Version>0.12.0</Version>
|
||||||
<Authors>Repository Authors</Authors>
|
<Authors>Repository Authors</Authors>
|
||||||
<Company>None</Company>
|
<Company>None</Company>
|
||||||
<Product>ServicePoint</Product>
|
<Product>ServicePoint</Product>
|
||||||
|
@ -27,30 +25,28 @@
|
||||||
|
|
||||||
<!-- generate C# bindings -->
|
<!-- generate C# bindings -->
|
||||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="PrepareForBuild">
|
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="PrepareForBuild">
|
||||||
<Exec Command="cargo build --release"/>
|
<Exec Command="cargo build -p servicepoint_binding_uniffi --release"/>
|
||||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml --release"/>
|
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="PrepareForBuild">
|
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="PrepareForBuild">
|
||||||
<Exec Command="cargo build"/>
|
<Exec Command="cargo build -p servicepoint_binding_uniffi"/>
|
||||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/>
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<!-- include native binary in output -->
|
<!-- include native binary in output -->
|
||||||
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
<ItemGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
<Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
|
<Content Include="../../../../../target/debug/libservicepoint_binding_uniffi.so" CopyToOutputDirectory="Always">
|
||||||
<Link>libservicepoint_binding_c.so</Link>
|
<Link>libservicepoint_binding_uniffi.so</Link>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
||||||
<Content Include="../../../target/release/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">
|
<Content Include="../../../../../target/release/libservicepoint_binding_uniffi.so" CopyToOutputDirectory="Always">
|
||||||
<Link>libservicepoint_binding_c.so</Link>
|
<Link>libservicepoint_binding_uniffi.so</Link>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- include link to source code at revision -->
|
|
||||||
<None Include="../README.md" Pack="true" PackagePath="\"/>
|
|
||||||
<!-- add README.md to package -->
|
<!-- 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"/>
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
|
||||||
</ItemGroup>
|
</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
|
lzma
|
||||||
];
|
];
|
||||||
makeExample =
|
makeExample =
|
||||||
package: example:
|
{
|
||||||
|
package,
|
||||||
|
example,
|
||||||
|
features ? "",
|
||||||
|
}:
|
||||||
naersk'.buildPackage {
|
naersk'.buildPackage {
|
||||||
pname = example;
|
pname = example;
|
||||||
cargoBuildOptions =
|
cargoBuildOptions =
|
||||||
|
@ -68,11 +72,14 @@
|
||||||
nativeBuildInputs = nativeBuildInputs;
|
nativeBuildInputs = nativeBuildInputs;
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
buildInputs = buildInputs;
|
buildInputs = buildInputs;
|
||||||
|
gitSubmodules = true;
|
||||||
overrideMain = old: {
|
overrideMain = old: {
|
||||||
preConfigure = ''
|
preConfigure = ''
|
||||||
cargo_build_options="$cargo_build_options --example ${example}"
|
cargo_build_options="$cargo_build_options --example ${example} ${
|
||||||
'';
|
if features == "" then "" else "--features " + features
|
||||||
};
|
}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
makePackage =
|
makePackage =
|
||||||
package:
|
package:
|
||||||
|
@ -95,11 +102,28 @@
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
servicepoint = makePackage "servicepoint";
|
servicepoint = makePackage "servicepoint";
|
||||||
announce = makeExample "servicepoint" "announce";
|
announce = makeExample {
|
||||||
game-of-life = makeExample "servicepoint" "game_of_life";
|
package = "servicepoint";
|
||||||
moving-line = makeExample "servicepoint" "moving_line";
|
example = "announce";
|
||||||
random-brightness = makeExample "servicepoint" "random_brightness";
|
};
|
||||||
wiping-clear = makeExample "servicepoint" "wiping_clear";
|
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
|
clippy
|
||||||
cargo-expand
|
cargo-expand
|
||||||
cargo-tarpaulin
|
cargo-tarpaulin
|
||||||
gcc
|
|
||||||
gnumake
|
|
||||||
dotnet-sdk_8
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
default = pkgs.mkShell rec {
|
default = pkgs.mkShell rec {
|
||||||
inputsFrom = [ self.packages.${system}.servicepoint ];
|
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)}";
|
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}";
|
||||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue