using OpenMacroBoard.SDK.Internals; using System; using System.Linq; using System.Threading; using System.Threading.Tasks; #nullable enable namespace OpenMacroBoard.SDK { /// /// A collection of extensions for . /// public static class DeviceContextExtensions { /// /// Wait for and open the first detected in this context. /// /// /// /// If there are multiple devices connected it's undefined which one you will get. /// If you want to specify a filter to get a specific device use /// . /// /// public static Task OpenAsync( this IDeviceContext context, CancellationToken cancellationToken = default ) { return context.OpenAsync(_ => true, cancellationToken); } /// /// Wait for and open the first matching in this context. /// public static async Task OpenAsync( this IDeviceContext context, Func selector, CancellationToken cancellationToken = default ) { return (await context.GetDeviceReferenceAsync(selector, cancellationToken)).Open(); } /// /// Wait for and return the first matching . /// public static async Task GetDeviceReferenceAsync( this IDeviceContext context, Func selector, CancellationToken cancellationToken = default ) { // check if there already is a matching device. var devRef = context.KnownDevices.FirstOrDefault(selector); if (devRef is not null) { return devRef; } // if not, register event handler // and wait for a matching device to show up var eventSync = new object(); var waitForDeviceBlocker = new TaskCompletionSource(); IDeviceReference? foundDevice = null; void ProcessReport(DeviceStateReport report) { lock (eventSync!) { if (foundDevice != null) { // ignore events if we've already found a device. return; } if (report.Connected && selector(report.DeviceReference)) { foundDevice = report.DeviceReference; waitForDeviceBlocker!.TrySetResult(0); } } } IDisposable subscription = null!; CancellationTokenRegistration registration = default; try { registration = cancellationToken.Register(() => waitForDeviceBlocker.TrySetCanceled()); var deviceReportObserver = new DeviceStateObserver(ProcessReport); subscription = context.DeviceStateReports.Subscribe(deviceReportObserver); await waitForDeviceBlocker.Task; } finally { #if NET8_0_OR_GREATER await registration.DisposeAsync(); #else registration.Dispose(); #endif subscription.Dispose(); } #pragma warning disable S2583 // Conditionally executed code should be reachable if (foundDevice is null) #pragma warning restore S2583 { // We don't document that exception because it shouldn't happen, // and we don't expect the consumer to handle this exception type. #pragma warning disable RCS1140 // Add exception to documentation comment. throw new InvalidOperationException("Couldn't locate device with matching criteria. This is a bug, please file an issue."); #pragma warning restore RCS1140 } return foundDevice; } } }