Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corsair device changed event #339

Open
wants to merge 8 commits into
base: Development
Choose a base branch
from
11 changes: 11 additions & 0 deletions RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;

namespace RGB.NET.Core;

Expand Down Expand Up @@ -94,6 +95,15 @@ public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwE
Reset();
throw;
}
catch (RGBDeviceException)
{
Reset();
if (throwExceptions)
{
throw;
}
return false;
}
catch (Exception ex)
{
Reset();
Expand Down Expand Up @@ -158,6 +168,7 @@ protected virtual IEnumerable<IRGBDevice> GetLoadedDevices(RGBDeviceType loadFil
/// <param name="id">The id of the update trigger.</param>
/// <param name="updateRateHardLimit">The update rate hard limit to be set in the update trigger.</param>
/// <returns>The update trigger mapped to the specified id.</returns>
[MethodImpl(MethodImplOptions.Synchronized)]
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
Expand Down
8 changes: 4 additions & 4 deletions RGB.NET.Core/Ids/IdGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public static class IdGenerator

// ReSharper disable InconsistentNaming
private static readonly HashSet<string> _registeredIds = [];
private static readonly Dictionary<Assembly, Dictionary<string, string>> _idMappings = [];
private static readonly Dictionary<Assembly, Dictionary<string, int>> _counter = [];
private static readonly Dictionary<object, Dictionary<string, string>> _idMappings = [];
private static readonly Dictionary<object, Dictionary<string, int>> _counter = [];
// ReSharper restore InconsistentNaming

#endregion
Expand All @@ -29,7 +29,7 @@ public static class IdGenerator
[MethodImpl(MethodImplOptions.NoInlining)]
public static string MakeUnique(string id) => MakeUnique(Assembly.GetCallingAssembly(), id);

internal static string MakeUnique(Assembly callingAssembly, string id)
public static string MakeUnique(object callingAssembly, string id)
{
if (!_idMappings.TryGetValue(callingAssembly, out Dictionary<string, string>? idMapping))
{
Expand Down Expand Up @@ -63,7 +63,7 @@ internal static string MakeUnique(Assembly callingAssembly, string id)
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ResetCounter() => ResetCounter(Assembly.GetCallingAssembly());

internal static void ResetCounter(Assembly callingAssembly)
public static void ResetCounter(object callingAssembly)
{
if (_counter.TryGetValue(callingAssembly, out Dictionary<string, int>? counter))
counter.Clear();
Expand Down
8 changes: 6 additions & 2 deletions RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,15 @@ public virtual async void Stop()

UpdateTokenSource?.Cancel();
if (UpdateTask != null)
try { await UpdateTask.ConfigureAwait(false); }
try
{
await UpdateTask.ConfigureAwait(false);
UpdateTask?.Dispose();
}
catch (TaskCanceledException) { }
catch (OperationCanceledException) { }
catch (InvalidOperationException) { }

UpdateTask?.Dispose();
UpdateTask = null;
}

Expand Down
346 changes: 194 additions & 152 deletions RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions RGB.NET.Devices.Corsair/Enum/CorsairEventId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace RGB.NET.Devices.Corsair;

internal enum CorsairEventId
{
Invalid = 0,
DeviceConnectionStatusChangedEvent = 1,
KeyEvent = 2,
}
25 changes: 24 additions & 1 deletion RGB.NET.Devices.Corsair/Generic/CorsairRGBDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ public abstract class CorsairRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceI
{
#region Properties & Fields

/// <summary>
/// <inheritdoc cref="ICorsairRGBDevice"/>
/// </summary>
public string DeviceId => DeviceInfo.DeviceId;

/// <summary>
/// Gets the mapping of <see cref="LedId"/> to <see cref="CorsairLedId"/> used to update the LEDs of this device.
/// </summary>
Expand All @@ -29,14 +34,32 @@ public abstract class CorsairRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceI
/// <param name="info">The generic information provided by CUE for the device.</param>
/// <param name="mapping">The mapping <see cref="LedId"/> to <see cref="CorsairLedId"/> used to update the LEDs of this device.</param>
/// <param name="updateQueue">The queue used to update this device.</param>
protected CorsairRGBDevice(TDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
protected CorsairRGBDevice(TDeviceInfo info, IUpdateQueue updateQueue)
: base(info, updateQueue)
{ }

#endregion

#region Methods

protected bool Equals(CorsairRGBDevice<TDeviceInfo> other)
{
return DeviceId == other.DeviceId;
}

public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CorsairRGBDevice<TDeviceInfo>)obj);
}

public override int GetHashCode()
{
return DeviceId.GetHashCode();
}

void ICorsairRGBDevice.Initialize() => InitializeLayout();

/// <summary>
Expand Down
45 changes: 41 additions & 4 deletions RGB.NET.Devices.Corsair/Generic/CorsairRGBDeviceInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Text.RegularExpressions;
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using RGB.NET.Core;
using RGB.NET.Devices.Corsair.Native;

Expand Down Expand Up @@ -33,7 +36,7 @@ public class CorsairRGBDeviceInfo : IRGBDeviceInfo
/// Returns the unique ID provided by the Corsair-SDK.
/// Returns string.Empty for Custom devices.
/// </summary>
public string DeviceId { get; }
public string DeviceId { get; init; }

/// <inheritdoc />
public object? LayoutMetadata { get; set; }
Expand Down Expand Up @@ -67,7 +70,14 @@ internal CorsairRGBDeviceInfo(RGBDeviceType deviceType, _CorsairDeviceInfo nativ
this.LedCount = ledCount;
this.LedOffset = ledOffset;

DeviceName = DeviceHelper.CreateDeviceName(Manufacturer, Model);
if (nativeInfo.id == null) // this device is 99% unpluggable
{
DeviceName = IdGenerator.MakeUnique(typeof(CorsairDeviceProvider), Manufacturer + " " + Model);
}
else
{
DeviceName = Manufacturer + " " + Model + " #" + HashAndShorten(DeviceId);
}
}

/// <summary>
Expand All @@ -86,7 +96,34 @@ internal CorsairRGBDeviceInfo(RGBDeviceType deviceType, _CorsairDeviceInfo nativ
this.LedCount = ledCount;
this.LedOffset = ledOffset;

DeviceName = DeviceHelper.CreateDeviceName(Manufacturer, Model);
if (nativeInfo.id == null)
{
DeviceName = IdGenerator.MakeUnique(typeof(CorsairDeviceProvider),Manufacturer + " " + Model) + " " + ledOffset;;
}
else
{
DeviceName = Manufacturer + " " + Model + " #" + HashAndShorten(DeviceId) + " " + ledOffset;
}
}

#endregion

#region Methods

private static string HashAndShorten(string input)
{
using SHA256 sha256Hash = SHA256.Create();
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
// Take the first 4 bytes of the hash
byte[] shortenedBytes = new byte[4];
Array.Copy(bytes, shortenedBytes, 4);
// Convert the bytes to a string
StringBuilder shortenedHash = new();
foreach (byte b in shortenedBytes)
{
shortenedHash.Append(b.ToString("X2"));
}
return shortenedHash.ToString();
}

#endregion
Expand Down
2 changes: 2 additions & 0 deletions RGB.NET.Devices.Corsair/Generic/ICorsairRGBDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ namespace RGB.NET.Devices.Corsair;
/// </summary>
public interface ICorsairRGBDevice : IRGBDevice
{
internal string DeviceId { get; }

internal void Initialize();
}
67 changes: 56 additions & 11 deletions RGB.NET.Devices.Corsair/Native/_CUESDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace RGB.NET.Devices.Corsair.Native;

internal delegate void CorsairSessionStateChangedHandler(nint context, _CorsairSessionStateChanged eventData);
internal delegate void CorsairEventHandler(nint context, _CorsairEvent corsairEvent);

// ReSharper disable once InconsistentNaming
internal static unsafe class _CUESDK
Expand Down Expand Up @@ -47,23 +48,59 @@ internal static unsafe class _CUESDK

#region Properties & Fields

internal static bool IsConnected => SesionState == CorsairSessionState.Connected;
internal static CorsairSessionState SesionState { get; private set; }
internal static bool IsConnected => SessionState == CorsairSessionState.Connected;
internal static CorsairSessionState SessionState { get; private set; }

#endregion

#region Events

internal static event EventHandler<CorsairSessionState>? SessionStateChanged;
internal static event EventHandler<_CorsairDeviceConnectionStatusChangedEvent>? DeviceConnectionEvent;

#endregion

#region Methods

private static void CorsairSessionStateChangedCallback(nint context, _CorsairSessionStateChanged eventdata)
private static void CorsairSessionStateChangedCallback(nint context, _CorsairSessionStateChanged eventData)
{
SesionState = eventdata.state;
SessionStateChanged?.Invoke(null, eventdata.state);
SessionState = eventData.state;
try
{
SessionStateChanged?.Invoke(null, eventData.state);
}
catch { /* dont let exception go to sdk */ }

switch (eventData.state)
{
case CorsairSessionState.Connected:
_corsairSubscribeForEvents(CorsairEventCallback, 0);
break;
case CorsairSessionState.Closed:
_corsairUnsubscribeForEvents();
break;
}
}

private static void CorsairEventCallback(nint context, _CorsairEvent eventData)
{
if (eventData.id != CorsairEventId.DeviceConnectionStatusChangedEvent)
{
return;
}

try
{
if (eventData.eventPointer == 0)
{
return;
}

_CorsairDeviceConnectionStatusChangedEvent connectionStatusChangedEvent =
Marshal.PtrToStructure<_CorsairDeviceConnectionStatusChangedEvent>(eventData.eventPointer)!;

DeviceConnectionEvent?.Invoke(null, connectionStatusChangedEvent);
}catch { /* dont let exception go to sdk */ }
}

#endregion
Expand Down Expand Up @@ -97,7 +134,7 @@ private static void LoadCUESDK()
_corsairGetSessionDetails = (delegate* unmanaged[Cdecl]<nint, CorsairError>)LoadFunction("CorsairGetSessionDetails");
_corsairDisconnect = (delegate* unmanaged[Cdecl]<CorsairError>)LoadFunction("CorsairDisconnect");
_corsairGetDevices = (delegate* unmanaged[Cdecl]<_CorsairDeviceFilter, int, nint, out int, CorsairError>)LoadFunction("CorsairGetDevices");
_corsairGetDeviceInfo = (delegate* unmanaged[Cdecl]<string, _CorsairDeviceInfo, CorsairError>)LoadFunction("CorsairGetDeviceInfo");
_corsairGetDeviceInfo = (delegate* unmanaged[Cdecl]<string, ref _CorsairDeviceInfo, CorsairError>)LoadFunction("CorsairGetDeviceInfo");
_corsairGetLedPositions = (delegate* unmanaged[Cdecl]<string, int, nint, out int, CorsairError>)LoadFunction("CorsairGetLedPositions");
_corsairSetLedColors = (delegate* unmanaged[Cdecl]<string, int, nint, CorsairError>)LoadFunction("CorsairSetLedColors");
_corsairSetLayerPriority = (delegate* unmanaged[Cdecl]<uint, CorsairError>)LoadFunction("CorsairSetLayerPriority");
Expand All @@ -106,6 +143,8 @@ private static void LoadCUESDK()
_corsairReleaseControl = (delegate* unmanaged[Cdecl]<string, CorsairError>)LoadFunction("CorsairReleaseControl");
_getDevicePropertyInfo = (delegate* unmanaged[Cdecl]<string, CorsairDevicePropertyId, uint, out CorsairDataType, out CorsairPropertyFlag, CorsairError>)LoadFunction("CorsairGetDevicePropertyInfo");
_readDeviceProperty = (delegate* unmanaged[Cdecl]<string, CorsairDevicePropertyId, uint, nint, CorsairError>)LoadFunction("CorsairReadDeviceProperty");
_corsairSubscribeForEvents = (delegate* unmanaged[Cdecl]<CorsairEventHandler, nint, CorsairError>)LoadFunction("CorsairSubscribeForEvents");
_corsairUnsubscribeForEvents = (delegate* unmanaged[Cdecl]<CorsairError>)LoadFunction("CorsairSubscribeForEvents");
}

private static nint LoadFunction(string function)
Expand Down Expand Up @@ -158,7 +197,7 @@ internal static void UnloadCUESDK()
private static delegate* unmanaged[Cdecl]<nint, CorsairError> _corsairGetSessionDetails;
private static delegate* unmanaged[Cdecl]<CorsairError> _corsairDisconnect;
private static delegate* unmanaged[Cdecl]<_CorsairDeviceFilter, int, nint, out int, CorsairError> _corsairGetDevices;
private static delegate* unmanaged[Cdecl]<string, _CorsairDeviceInfo, CorsairError> _corsairGetDeviceInfo;
private static delegate* unmanaged[Cdecl]<string, ref _CorsairDeviceInfo, CorsairError> _corsairGetDeviceInfo;
private static delegate* unmanaged[Cdecl]<string, int, nint, out int, CorsairError> _corsairGetLedPositions;
private static delegate* unmanaged[Cdecl]<string, int, nint, CorsairError> _corsairSetLedColors;
private static delegate* unmanaged[Cdecl]<uint, CorsairError> _corsairSetLayerPriority;
Expand All @@ -167,13 +206,18 @@ internal static void UnloadCUESDK()
private static delegate* unmanaged[Cdecl]<string, CorsairError> _corsairReleaseControl;
private static delegate* unmanaged[Cdecl]<string, CorsairDevicePropertyId, uint, out CorsairDataType, out CorsairPropertyFlag, CorsairError> _getDevicePropertyInfo;
private static delegate* unmanaged[Cdecl]<string, CorsairDevicePropertyId, uint, nint, CorsairError> _readDeviceProperty;
private static delegate* unmanaged[Cdecl]<CorsairEventHandler, nint, CorsairError> _corsairSubscribeForEvents;
private static delegate* unmanaged[Cdecl]<CorsairError> _corsairUnsubscribeForEvents;

#endregion

internal static CorsairError CorsairConnect()
{
if (_corsairConnectPtr == null) throw new RGBDeviceException("The Corsair-SDK is not initialized.");
if (IsConnected) throw new RGBDeviceException("The Corsair-SDK is already connected.");
if (SessionState is CorsairSessionState.Connecting or CorsairSessionState.Timeout)
{
return CorsairError.Success;
}
return _corsairConnectPtr(CorsairSessionStateChangedCallback, 0);
}

Expand All @@ -197,7 +241,6 @@ internal static CorsairError CorsairGetSessionDetails(out _CorsairSessionDetails

internal static CorsairError CorsairDisconnect()
{
if (!IsConnected) throw new RGBDeviceException("The Corsair-SDK is not connected.");
return _corsairDisconnect();
}

Expand All @@ -220,10 +263,12 @@ internal static CorsairError CorsairGetDevices(_CorsairDeviceFilter filter, out
}
}

internal static CorsairError CorsairGetDeviceInfo(string deviceId, _CorsairDeviceInfo deviceInfo)
internal static CorsairError CorsairGetDeviceInfo(string deviceId, out _CorsairDeviceInfo deviceInfo)
{
if (!IsConnected) throw new RGBDeviceException("The Corsair-SDK is not connected.");
return _corsairGetDeviceInfo(deviceId, deviceInfo);

deviceInfo = new _CorsairDeviceInfo();
return _corsairGetDeviceInfo(deviceId, ref deviceInfo);
}

internal static CorsairError CorsairGetLedPositions(string deviceId, out _CorsairLedPosition[] ledPositions)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;

namespace RGB.NET.Devices.Corsair.Native;

[StructLayout(LayoutKind.Sequential)]
public class _CorsairDeviceConnectionStatusChangedEvent
{
/// <summary>
/// iCUE-SDK: null terminated Unicode string that contains unique device identifier
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = _CUESDK.CORSAIR_STRING_SIZE_M)]
internal string? deviceId;

/// <summary>
/// iCUE-SDK: true if connected, false if disconnected
/// </summary>
[MarshalAs(UnmanagedType.U1)]
internal bool isConnected;
}
2 changes: 1 addition & 1 deletion RGB.NET.Devices.Corsair/Native/_CorsairDeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace RGB.NET.Devices.Corsair.Native;
/// iCUE-SDK: contains information about device
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal sealed class _CorsairDeviceInfo
internal struct _CorsairDeviceInfo
{
/// <summary>
/// iCUE-SDK: enum describing device type
Expand Down
Loading