Skip to content

Commit

Permalink
✨ Kraken: Read the current display mode from the device.
Browse files Browse the repository at this point in the history
Turns out that this information was here all along. (It is queried by NZXT CAM)
  • Loading branch information
hexawyz committed Jan 25, 2025
1 parent 02634c0 commit ca4d6b1
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/Exo/Devices/Exo.Devices.Nzxt.Kraken/DisplayModeInformation.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
5 changes: 5 additions & 0 deletions src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
47 changes: 47 additions & 0 deletions src/Exo/Devices/Exo.Devices.Nzxt.Kraken/KrakenHidTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -68,6 +69,7 @@ private sealed class FunctionTaskCompletionSource : TaskCompletionSource
private TaskCompletionSource? _setBrightnessTaskCompletionSource;
private TaskCompletionSource? _setDisplayModeTaskCompletionSource;
private TaskCompletionSource<ScreenInformation>? _screenInfoRetrievalTaskCompletionSource;
private TaskCompletionSource<DisplayModeInformation>? _displayModeRetrievalTaskCompletionSource;
private ImageInfoTaskCompletionSource? _imageInfoTaskCompletionSource;
private FunctionTaskCompletionSource? _imageMemoryManagementTaskCompletionSource;
private FunctionTaskCompletionSource? _imageUploadTaskCompletionSource;
Expand Down Expand Up @@ -190,6 +192,36 @@ static void PrepareRequest(Span<byte> buffer)
}
}

public async ValueTask<DisplayModeInformation> GetDisplayModeAsync(CancellationToken cancellationToken)
{
EnsureNotDisposed();

var tcs = new TaskCompletionSource<DisplayModeInformation>(TaskCreationOptions.RunContinuationsAsynchronously);
if (Interlocked.CompareExchange(ref _displayModeRetrievalTaskCompletionSource, tcs, null) is not null) throw new InvalidOperationException();

static void PrepareRequest(Span<byte> 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);
Expand Down Expand Up @@ -741,6 +773,21 @@ private void ProcessScreenInformationResponse(byte functionId, ReadOnlySpan<byte

_screenInfoRetrievalTaskCompletionSource?.TrySetResult(new(brightness, imageCount, imageWidth, imageHeight, memoryBlockCount));
}
else if (functionId == ScreenSettingsGetDisplayModeFunctionId)
{
// This function returns other information that is not well identified yet.
// Example responses:
// 31 03 1a0041000a51383430353132 04 0d 00 0d 00 ff 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
// 31 03 1a0041000a51383430353132 04 05 00 05 00 ff 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
// 31 03 1a0041000a51383430353132 04 01 00 05 00 ff 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
// 31 03 1a0041000a51383430353132 04 07 00 07 00 ff 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
// First two bytes correspond well to the display mode parameters, but the 4th byte seems to sometimes be synchronized with the image index, and sometimes not.
// Sixth byte is always 0xff?
if (_displayModeRetrievalTaskCompletionSource is { } tcs)
{
tcs.TrySetResult(new((KrakenDisplayMode)response[0], response[1]));
}
}
else if (functionId == ScreenSettingsGetImageInfoFunctionId)
{
byte imageIndex = response[0];
Expand Down

0 comments on commit ca4d6b1

Please sign in to comment.