diff --git a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs index 03410fb..04cfb92 100644 --- a/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs +++ b/RGB.NET.Core/Update/Devices/DeviceUpdateTrigger.cs @@ -53,12 +53,12 @@ namespace RGB.NET.Core } } - private AutoResetEvent _hasDataEvent = new AutoResetEvent(false); + protected AutoResetEvent HasDataEvent = new AutoResetEvent(false); - private bool _isRunning; - private Task _updateTask; - private CancellationTokenSource _updateTokenSource; - private CancellationToken _updateToken; + protected bool IsRunning; + protected Task UpdateTask; + protected CancellationTokenSource UpdateTokenSource; + protected CancellationToken UpdateToken; #endregion @@ -88,13 +88,13 @@ namespace RGB.NET.Core /// public void Start() { - if (_isRunning) return; + if (IsRunning) return; - _isRunning = true; + IsRunning = true; - _updateTokenSource?.Dispose(); - _updateTokenSource = new CancellationTokenSource(); - _updateTask = Task.Factory.StartNew(UpdateLoop, (_updateToken = _updateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default); + UpdateTokenSource?.Dispose(); + UpdateTokenSource = new CancellationTokenSource(); + UpdateTask = Task.Factory.StartNew(UpdateLoop, (UpdateToken = UpdateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default); } /// @@ -102,22 +102,22 @@ namespace RGB.NET.Core /// public async void Stop() { - if (!_isRunning) return; + if (!IsRunning) return; - _isRunning = false; + IsRunning = false; - _updateTokenSource.Cancel(); - await _updateTask; - _updateTask.Dispose(); - _updateTask = null; + UpdateTokenSource.Cancel(); + await UpdateTask; + UpdateTask.Dispose(); + UpdateTask = null; } - private void UpdateLoop() + protected virtual void UpdateLoop() { OnStartup(); - while (!_updateToken.IsCancellationRequested) + while (!UpdateToken.IsCancellationRequested) { - if (_hasDataEvent.WaitOne(Timeout)) + if (HasDataEvent.WaitOne(Timeout)) { long preUpdateTicks = Stopwatch.GetTimestamp(); @@ -135,7 +135,7 @@ namespace RGB.NET.Core } /// - public void TriggerHasData() => _hasDataEvent.Set(); + public void TriggerHasData() => HasDataEvent.Set(); private void UpdateUpdateFrequency() { diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs index 9b43d5c..fe3d576 100644 --- a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs @@ -15,6 +15,7 @@ namespace RGB.NET.Devices.SteelSeries #region Properties & Fields private string _deviceType; + private Dictionary _lastDataSet; #endregion @@ -35,9 +36,21 @@ namespace RGB.NET.Devices.SteelSeries #region Methods + protected override void OnUpdate(object sender, CustomUpdateData customData) + { + if ((customData != null) && (customData["refresh"] as bool? ?? false)) + { + if ((_lastDataSet != null) && (_lastDataSet.Count != 0)) + Update(_lastDataSet); + } + else + base.OnUpdate(sender, customData); + } + /// protected override void Update(Dictionary dataSet) { + _lastDataSet = dataSet; SteelSeriesSDK.UpdateLeds(_deviceType, dataSet.ToDictionary(x => ((SteelSeriesLedId)x.Key).GetAPIName(), x => x.Value.ToIntArray())); } diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs new file mode 100644 index 0000000..88c04ac --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateTrigger.cs @@ -0,0 +1,75 @@ +// ReSharper disable MemberCanBePrivate.Global + +using System; +using System.Diagnostics; +using System.Threading; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// Represents an update-trigger used to update SteelSeries devices + /// + public class SteelSeriesDeviceUpdateTrigger : DeviceUpdateTrigger + { + #region Constants + + private const long FLUSH_TIMER = 5 * 1000 * TimeSpan.TicksPerMillisecond; // flush the device every 5 seconds to prevent timeouts + + #endregion + + #region Properties & Fields + + private long _lastUpdateTimestamp; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The hard limit of the update rate of this trigger. + public SteelSeriesDeviceUpdateTrigger() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The hard limit of the update rate of this trigger. + public SteelSeriesDeviceUpdateTrigger(double updateRateHardLimit) + : base(updateRateHardLimit) + { } + + #endregion + + #region Methods + + protected override void UpdateLoop() + { + OnStartup(); + while (!UpdateToken.IsCancellationRequested) + { + if (HasDataEvent.WaitOne(Timeout)) + { + long preUpdateTicks = Stopwatch.GetTimestamp(); + + OnUpdate(); + + if (UpdateFrequency > 0) + { + _lastUpdateTimestamp = Stopwatch.GetTimestamp(); + double lastUpdateTime = ((_lastUpdateTimestamp - preUpdateTicks) / (double)TimeSpan.TicksPerMillisecond); + int sleep = (int)((UpdateFrequency * 1000.0) - lastUpdateTime); + if (sleep > 0) + Thread.Sleep(sleep); + } + } + else if (((Stopwatch.GetTimestamp() - _lastUpdateTimestamp) > FLUSH_TIMER)) + OnUpdate(new CustomUpdateData(("refresh", true))); + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs index 432c51b..9f59d7d 100644 --- a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -37,9 +37,9 @@ namespace RGB.NET.Devices.SteelSeries public IEnumerable Devices { get; private set; } /// - /// The used to trigger the updates for SteelSeries devices. + /// The used to trigger the updates for SteelSeries devices. /// - public DeviceUpdateTrigger UpdateTrigger { get; } + public SteelSeriesDeviceUpdateTrigger UpdateTrigger { get; } #endregion @@ -54,7 +54,7 @@ namespace RGB.NET.Devices.SteelSeries if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(SteelSeriesDeviceProvider)}"); _instance = this; - UpdateTrigger = new DeviceUpdateTrigger(); + UpdateTrigger = new SteelSeriesDeviceUpdateTrigger(); } #endregion