diff --git a/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/DisplayModeInformation.cs b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/DisplayModeInformation.cs new file mode 100644 index 00000000..7285e2af --- /dev/null +++ b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/DisplayModeInformation.cs @@ -0,0 +1,13 @@ +namespace Exo.Devices.Nzxt.Kraken; + +internal readonly struct DisplayModeInformation +{ + public DisplayModeInformation(KrakenDisplayMode displayMode, byte imageIndex) + { + DisplayMode = displayMode; + ImageIndex = imageIndex; + } + + public KrakenDisplayMode DisplayMode { get; } + public byte ImageIndex { get; } +} diff --git a/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenDriver.cs b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenDriver.cs index c15d89cd..388ed722 100644 --- a/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenDriver.cs +++ b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenDriver.cs @@ -163,6 +163,11 @@ await KrakenImageStorageManager.CreateAsync(screenInfo.ImageCount, screenInfo.Me await hidTransport.SetPumpPowerCurveAsync(DefaultPumpCurve, cancellationToken).ConfigureAwait(false); await hidTransport.SetFanPowerCurveAsync(DefaultFanCurve, cancellationToken).ConfigureAwait(false); + // TODO: Once the image storage manager is somehow merged with the protocol (or something similar), this info should be kept as state to know which is the currently active image. + // Knowing the currently displayed image is useful to determine the best image flip strategy. (i.e. If there is enough memory, any image other than the current one) + var initialDisplayMode = await hidTransport.GetDisplayModeAsync(cancellationToken).ConfigureAwait(false); + currentDisplayMode = initialDisplayMode.DisplayMode; + if (storageManager is not null) { await hidTransport.DisplayPresetVisualAsync(KrakenPresetVisual.LiquidTemperature, cancellationToken).ConfigureAwait(false); diff --git a/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenHidTransport.cs b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenHidTransport.cs index d1f563d4..91549d25 100644 --- a/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenHidTransport.cs +++ b/src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenHidTransport.cs @@ -40,6 +40,7 @@ private sealed class FunctionTaskCompletionSource : TaskCompletionSource private const byte ScreenSettingsGetScreenInfoFunctionId = 0x01; private const byte ScreenSettingsSetBrightnessFunctionId = 0x02; + private const byte ScreenSettingsGetDisplayModeFunctionId = 0x03; private const byte ScreenSettingsGetImageInfoFunctionId = 0x04; private const byte CoolingPowerPumpFunctionId = 0x01; @@ -68,6 +69,7 @@ private sealed class FunctionTaskCompletionSource : TaskCompletionSource private TaskCompletionSource? _setBrightnessTaskCompletionSource; private TaskCompletionSource? _setDisplayModeTaskCompletionSource; private TaskCompletionSource? _screenInfoRetrievalTaskCompletionSource; + private TaskCompletionSource? _displayModeRetrievalTaskCompletionSource; private ImageInfoTaskCompletionSource? _imageInfoTaskCompletionSource; private FunctionTaskCompletionSource? _imageMemoryManagementTaskCompletionSource; private FunctionTaskCompletionSource? _imageUploadTaskCompletionSource; @@ -190,6 +192,36 @@ static void PrepareRequest(Span buffer) } } + public async ValueTask GetDisplayModeAsync(CancellationToken cancellationToken) + { + EnsureNotDisposed(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + if (Interlocked.CompareExchange(ref _displayModeRetrievalTaskCompletionSource, tcs, null) is not null) throw new InvalidOperationException(); + + static void PrepareRequest(Span buffer) + { + // NB: Write buffer is assumed to be cleared from index 2, and this part should always be cleared before releasing the write lock. + buffer[0] = ScreenSettingsRequestMessageId; + buffer[1] = ScreenSettingsGetDisplayModeFunctionId; + } + + var buffer = WriteBuffer; + try + { + using (await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false)) + { + PrepareRequest(buffer.Span); + await _stream.WriteAsync(buffer, default).ConfigureAwait(false); + } + return await WaitOrCancelAsync(tcs, cancellationToken).ConfigureAwait(false); + } + finally + { + Volatile.Write(ref _displayModeRetrievalTaskCompletionSource, null); + } + } + public async ValueTask SetBrightnessAsync(byte brightness, CancellationToken cancellationToken) { ArgumentOutOfRangeException.ThrowIfGreaterThan(brightness, 100); @@ -741,6 +773,21 @@ private void ProcessScreenInformationResponse(byte functionId, ReadOnlySpan