Skip to content

Commit

Permalink
✨ Razer: Add wireless max brightness feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
hexawyz committed Sep 29, 2024
1 parent e4191e9 commit ade588d
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 59 deletions.
29 changes: 29 additions & 0 deletions src/Exo/Core/Exo.Core/Features/PowerManagementFeature.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Exo.Features.Lighting;

namespace Exo.Features.PowerManagement;

/// <summary>Devices can allow access to their battery level by providing this feature.</summary>
Expand Down Expand Up @@ -40,3 +42,30 @@ public interface ILowPowerModeBatteryThresholdFeature : IPowerManagementDeviceFe
/// <param name="cancellationToken"></param>
Task SetLowPowerBatteryThresholdAsync(Half lowPowerThreshold, CancellationToken cancellationToken);
}

/// <summary>This feature is exposed by devices that have a lighting brightness setting for wireless mode.</summary>
/// <remarks>This is related to <see cref="ILightingBrightnessFeature"/>.</remarks>
public interface IWirelessMaximumBrightnessFeature : IPowerManagementDeviceFeature
{
/// <summary>Get the minimum brightness level.</summary>
byte MinimumValue => 1;

/// <summary>Get the maximum brightness level.</summary>
/// <remarks>
/// <para>
/// Generally, devices will support setting 100 or 255 levels of brightness, but some devices may use more unusual values.
/// Brightness values could always be abstracted to <c>100%</c> but it is more helpful to surface the ticks in the UI when possible.
/// </para>
/// </remarks>
byte MaximumValue { get; }

/// <summary>Gets the current maximum brightness brightness level.</summary>
/// <remarks>The brightness value must be between <see cref="MinimumBrightness"/> and <see cref="MaximumBrightness"/> inclusive.</remarks>
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="brightness"/> parameter is out of range.</exception>
byte WirelessMaximumBrightness { get; }

/// <summary>Sets the maximum brightness level of the device in wireless mode.</summary>
/// <param name="maximumBrightness">The new maximum brightness.</param>
/// <param name="cancellationToken"></param>
Task SetWirelessMaximumBrightnessAsync(byte maximumBrightness, CancellationToken cancellationToken);
}
3 changes: 3 additions & 0 deletions src/Exo/Devices/Exo.Devices.Razer/IRazerProtocolTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ internal interface IRazerProtocolTransport : IDisposable
ValueTask<byte> GetLowPowerThresholdAsync(CancellationToken cancellationToken);
Task SetLowPowerThresholdAsync(byte value, CancellationToken cancellationToken);

ValueTask<byte> GetWirelessMaximumBrightnessAsync(CancellationToken cancellationToken);
Task SetWirelessMaximumBrightnessAsync(byte value, CancellationToken cancellationToken);

ValueTask<PairedDeviceInformation[]> GetDevicePairingInformationAsync(CancellationToken cancellationToken);
ValueTask<PairedDeviceInformation> GetDeviceInformationAsync(CancellationToken cancellationToken);
ValueTask<Version> GetFirmwareVersionAsync(CancellationToken cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -956,4 +956,6 @@ static unsafe void WriteData(SafeFileHandle serviceHandle, in BluetoothLeCharact
ValueTask<(byte ColorCount, RgbColor Color1, RgbColor Color2)> IRazerProtocolTransport.GetBreathingEffectParametersV1Async(RazerLedId ledId, CancellationToken cancellationToken) => throw new NotSupportedException();
Task IRazerProtocolTransport.SetSynchronizedLightingV1Async(RazerLedId ledId, bool enable, CancellationToken cancellationToken) => throw new NotSupportedException();
ValueTask<bool> IRazerProtocolTransport.IsSynchronizedLightingEnabledV1Async(RazerLedId ledId, CancellationToken cancellationToken) => throw new NotSupportedException();
ValueTask<byte> IRazerProtocolTransport.GetWirelessMaximumBrightnessAsync(CancellationToken cancellationToken) => throw new NotSupportedException();
Task IRazerProtocolTransport.SetWirelessMaximumBrightnessAsync(byte value, CancellationToken cancellationToken) => throw new NotSupportedException();
}
32 changes: 27 additions & 5 deletions src/Exo/Devices/Exo.Devices.Razer/RazerDeviceDriver.BaseDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ private abstract class BaseDevice :
ILightingBrightnessFeature,
IBatteryStateDeviceFeature,
IIdleSleepTimerFeature,
ILowPowerModeBatteryThresholdFeature
ILowPowerModeBatteryThresholdFeature,
IWirelessMaximumBrightnessFeature
{
protected abstract class LightingZone :
ILightingZone,
Expand Down Expand Up @@ -508,11 +509,13 @@ public IEnumerator<KeyValuePair<Type, ILightingDeviceFeature>> GetEnumerator()
private readonly IDeviceFeatureSet<IPowerManagementDeviceFeature> _powerManagementFeatures;
private readonly IDeviceFeatureSet<ILightingDeviceFeature> _lightingFeatures;
private bool _isUnifiedLightingEnabled;
private byte _wirelessMaximumBrightness;

protected bool HasUnifiedLighting => _unifiedLightingZone is not null;
protected bool HasLightingZones => !_lightingZones.IsDefaultOrEmpty;

protected bool HasBattery => (_deviceFlags & RazerDeviceFlags.HasBattery) != 0;
protected bool HasWirelessMaximumBrightness => (_deviceFlags & RazerDeviceFlags.HasWirelessMaximumBrightness) != 0;
protected bool HasLighting => (_deviceFlags & RazerDeviceFlags.HasLighting) != 0;
protected bool HasLightingV2 => (_deviceFlags & RazerDeviceFlags.HasLightingV2) != 0;
protected bool HasDpi => (_deviceFlags & RazerDeviceFlags.HasDpi) != 0;
Expand Down Expand Up @@ -546,13 +549,17 @@ RazerDeviceFlags deviceFlags
// TODO: Better (No devices with proper multiple zones at the moment)
_isUnifiedLightingEnabled = _unifiedLightingZone is not null && _lightingZones.Length == 0;

_powerManagementFeatures = HasBattery ?
FeatureSet.Create<IPowerManagementDeviceFeature, BaseDevice, IBatteryStateDeviceFeature, IIdleSleepTimerFeature, ILowPowerModeBatteryThresholdFeature>(this) :
FeatureSet.Empty<IPowerManagementDeviceFeature>();

_powerManagementFeatures = CreatePowerManagementFeatures();
_lightingFeatures = CreateLightingFeatures();
}

protected virtual IDeviceFeatureSet<IPowerManagementDeviceFeature> CreatePowerManagementFeatures()
=> HasBattery ?
HasWirelessMaximumBrightness ?
FeatureSet.Create<IPowerManagementDeviceFeature, BaseDevice, IBatteryStateDeviceFeature, IIdleSleepTimerFeature, ILowPowerModeBatteryThresholdFeature, IWirelessMaximumBrightnessFeature>(this) :
FeatureSet.Create<IPowerManagementDeviceFeature, BaseDevice, IBatteryStateDeviceFeature, IIdleSleepTimerFeature, ILowPowerModeBatteryThresholdFeature>(this) :
FeatureSet.Empty<IPowerManagementDeviceFeature>();

protected virtual IDeviceFeatureSet<ILightingDeviceFeature> CreateLightingFeatures()
=> HasLighting ? new LightingFeatureSet(this) : FeatureSet.Empty<ILightingDeviceFeature>();

Expand Down Expand Up @@ -591,6 +598,11 @@ protected virtual (LightingZone? UnifiedLightingZone, ImmutableArray<LightingZon

protected override async ValueTask InitializeAsync(CancellationToken cancellationToken)
{
if (HasWirelessMaximumBrightness)
{
_wirelessMaximumBrightness = await _transport.GetWirelessMaximumBrightnessAsync(cancellationToken).ConfigureAwait(false);
}

if (MustSetDeviceMode3)
{
await _transport.SetDeviceModeAsync(0x03, cancellationToken).ConfigureAwait(false);
Expand Down Expand Up @@ -765,5 +777,15 @@ async Task IIdleSleepTimerFeature.SetIdleTimeAsync(TimeSpan idleTime, Cancellati

Task ILowPowerModeBatteryThresholdFeature.SetLowPowerBatteryThresholdAsync(Half lowPowerThreshold, CancellationToken cancellationToken)
=> _transport.SetLowPowerThresholdAsync((byte)(255 * lowPowerThreshold), cancellationToken);

byte IWirelessMaximumBrightnessFeature.MaximumValue => 255;

byte IWirelessMaximumBrightnessFeature.WirelessMaximumBrightness => _wirelessMaximumBrightness;

async Task IWirelessMaximumBrightnessFeature.SetWirelessMaximumBrightnessAsync(byte maximumBrightness, CancellationToken cancellationToken)
{
await _transport.SetWirelessMaximumBrightnessAsync(maximumBrightness, cancellationToken);
_wirelessMaximumBrightness = maximumBrightness;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using DeviceTools;
using Exo.Features;
using Exo.Features.PowerManagement;

namespace Exo.Devices.Razer;

Expand Down Expand Up @@ -41,6 +43,9 @@ RazerDeviceFlags deviceFlags

public override DeviceCategory DeviceCategory => DeviceCategory.MouseDock;

//protected override IDeviceFeatureSet<IPowerManagementDeviceFeature> CreatePowerManagementFeatures()
// => FeatureSet.Create<IPowerManagementDeviceFeature, IWirelessMaximumBrightnessFeature>(this);

protected override (LightingZone? UnifiedLightingZone, ImmutableArray<LightingZone> LightingZones) CreateLightingZones(in DeviceInformation deviceInformation, ImmutableArray<RazerLedId> ledIds)
=> (new DockLightingZoneV1(this, deviceInformation.LightingZoneGuid.GetValueOrDefault(), RazerLedId.Dongle), []);

Expand Down
17 changes: 7 additions & 10 deletions src/Exo/Devices/Exo.Devices.Razer/RazerDeviceDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ ImmutableArray<ushort> defaultDpiPresets
public bool HasReactiveLighting => (Flags & RazerDeviceFlags.HasReactiveLighting) != 0;
public bool UseNonUnifiedLightingAsUnified => (Flags & RazerDeviceFlags.UseNonUnifiedLightingAsUnified) != 0;

public bool IsReceiver => (Flags & RazerDeviceFlags.IsReceiver) != 0;
public bool IsReceiver => DeviceCategory is RazerDeviceCategory.UsbReceiver or RazerDeviceCategory.DockReceiver;

public ushort GetMainProductId()
=> (Flags & (RazerDeviceFlags.HasDongleProductId | RazerDeviceFlags.IsReceiver)) == (RazerDeviceFlags.HasDongleProductId | RazerDeviceFlags.IsReceiver) ?
=> HasDongleProductId && IsReceiver ?
DongleDeviceProductId :
(Flags & RazerDeviceFlags.HasWiredProductId) == 0 ?
(Flags & RazerDeviceFlags.HasBluetoothLowEnergyProductId) == 0 ?
Expand Down Expand Up @@ -167,9 +167,8 @@ private enum RazerDeviceFlags : ushort
HasBluetoothProductId = 0x04,
HasBluetoothLowEnergyProductId = 0x08,

IsReceiver = 0x10,

HasBattery = 0x20,
HasBattery = 0x10,
HasWirelessMaximumBrightness = 0x20,

HasLighting = 0x40,
HasLightingV2 = 0x80,
Expand All @@ -181,7 +180,7 @@ private enum RazerDeviceFlags : ushort
HasDpiPresetsRead = 0x1000,
HasDpiPresetsV2 = 0x2000,

// Added for Mamba Chroma but maybe nor necessary ?
// Added for Mamba Chroma but maybe not necessary ?
MustSetDeviceMode3 = 0x4000,
MustSetSensorState5 = 0x8000,
}
Expand All @@ -208,6 +207,7 @@ private enum RazerDeviceFlags : ushort
RazerDeviceCategory.Mouse,
RazerLedId.Backlight,
RazerDeviceFlags.HasBattery |
RazerDeviceFlags.HasWirelessMaximumBrightness |
RazerDeviceFlags.HasWiredProductId |
RazerDeviceFlags.HasDongleProductId |
RazerDeviceFlags.HasLighting |
Expand All @@ -232,8 +232,7 @@ private enum RazerDeviceFlags : ushort
RazerDeviceFlags.HasWiredProductId |
RazerDeviceFlags.HasDongleProductId |
RazerDeviceFlags.HasLighting |
RazerDeviceFlags.UseNonUnifiedLightingAsUnified |
RazerDeviceFlags.IsReceiver,
RazerDeviceFlags.UseNonUnifiedLightingAsUnified,
0x0044,
0x0045,
0xFFFF,
Expand Down Expand Up @@ -279,7 +278,6 @@ private enum RazerDeviceFlags : ushort
RazerDeviceFlags.HasWiredProductId |
RazerDeviceFlags.HasDongleProductId |
RazerDeviceFlags.HasBluetoothLowEnergyProductId |
RazerDeviceFlags.IsReceiver |
RazerDeviceFlags.HasDpi |
RazerDeviceFlags.HasDpiPresets |
RazerDeviceFlags.HasDpiPresetsRead |
Expand Down Expand Up @@ -334,7 +332,6 @@ private enum RazerDeviceFlags : ushort
RazerDeviceFlags.HasBattery |
RazerDeviceFlags.HasWiredProductId |
RazerDeviceFlags.HasDongleProductId |
RazerDeviceFlags.IsReceiver |
RazerDeviceFlags.HasDpi |
RazerDeviceFlags.HasDpiPresets |
RazerDeviceFlags.HasDpiPresetsRead |
Expand Down
Loading

0 comments on commit ade588d

Please sign in to comment.