diff --git a/src/wpilibsharp/Simulation/AddressableLEDSim.cs b/src/wpilibsharp/Simulation/AddressableLEDSim.cs new file mode 100644 index 00000000..6613251c --- /dev/null +++ b/src/wpilibsharp/Simulation/AddressableLEDSim.cs @@ -0,0 +1,19 @@ +using WPIHal; +using WPIHal.Natives.Simulation; + +namespace WPILib.Simulation; + +public class AddressableLEDSim +{ + private readonly int m_index; + + public AddressableLEDSim() + { + m_index = 0; + } + + public unsafe CallbackStore registerInitializedCallback(Action callback, bool initialNotify) + { + return new CallbackStore(callback, m_index, initialNotify, &HalAddressableLEDData.RegisterAddressableLEDInitializedCallback, &HalAddressableLEDData.CancelAddressableLEDInitializedCallback); + } +} diff --git a/src/wpilibsharp/Simulation/CallbackStore.cs b/src/wpilibsharp/Simulation/CallbackStore.cs new file mode 100644 index 00000000..0667ef16 --- /dev/null +++ b/src/wpilibsharp/Simulation/CallbackStore.cs @@ -0,0 +1,60 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WPIHal; +using unsafe HalGlobalCreate = delegate* managed, void*, bool, int>; +using unsafe HalGlobalFree = delegate* managed; +using unsafe HalIndexedCreate = delegate* managed, void*, bool, int>; +using unsafe HalIndexedFree = delegate* managed; +using unsafe HalNativeNotifyCallback = delegate* unmanaged[Cdecl]; + +namespace WPILib.Simulation; + +public class CallbackStore : IDisposable +{ + private GCHandle delegateHandle; + private readonly int nativeHandle; + private readonly int? index; + private unsafe readonly HalIndexedFree indexedFree; + private unsafe readonly HalGlobalFree globalFree; + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + public static unsafe void HalNotifyCallback(byte* name, void* param, HalValue* value) + { + GCHandle handle = GCHandle.FromIntPtr((nint)param); + if (handle.Target is Action stringCallback) + { + string n = Marshal.PtrToStringUTF8((nint)name) ?? ""; + stringCallback(n, *value); + } + } + + public unsafe void Dispose() + { + GC.SuppressFinalize(this); + if (index is { } x) + { + indexedFree(x, nativeHandle); + } + else + { + globalFree(nativeHandle); + } + delegateHandle.Free(); + } + + public unsafe CallbackStore(Action callback, bool immediateNotify, HalGlobalCreate create, HalGlobalFree free) + { + delegateHandle = GCHandle.Alloc(callback); + nativeHandle = create(&HalNotifyCallback, (void*)(nint)delegateHandle, immediateNotify); + index = null; + globalFree = free; + } + + public unsafe CallbackStore(Action callback, int index, bool immediateNotify, HalIndexedCreate create, HalIndexedFree free) + { + delegateHandle = GCHandle.Alloc(callback); + nativeHandle = create(index, &HalNotifyCallback, (void*)(nint)delegateHandle, immediateNotify); + this.index = index; + indexedFree = free; + } +}