Merge branch 'uniffi2'
This commit is contained in:
		
						commit
						93657c9f85
					
				
					 66 changed files with 7298 additions and 2469 deletions
				
			
		
							
								
								
									
										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 | ||||
| 
 | ||||
| [](https://crates.io/crates/servicepoint) | ||||
| [](https://crates.io/crates/servicepoint) | ||||
| [](https://docs.rs/servicepoint/latest/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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter