diff --git a/RGB.NET.Core/Devices/Update/IUpdateTrigger.cs b/RGB.NET.Core/Devices/Update/IUpdateTrigger.cs new file mode 100644 index 0000000..91d5c22 --- /dev/null +++ b/RGB.NET.Core/Devices/Update/IUpdateTrigger.cs @@ -0,0 +1,12 @@ +using System; + +namespace RGB.NET.Core +{ + public interface IUpdateTrigger + { + event EventHandler Starting; + event EventHandler Update; + + void TriggerHasData(); + } +} diff --git a/RGB.NET.Core/Devices/Update/UpdateQueue.cs b/RGB.NET.Core/Devices/Update/UpdateQueue.cs new file mode 100644 index 0000000..558c2d9 --- /dev/null +++ b/RGB.NET.Core/Devices/Update/UpdateQueue.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace RGB.NET.Core +{ + public abstract class UpdateQueue + { + #region Properties & Fields + + private readonly object _dataLock = new object(); + private readonly IUpdateTrigger _updateTrigger; + private Dictionary _currentDataSet; + + #endregion + + #region Constructors + + public UpdateQueue(IUpdateTrigger updateTrigger) + { + this._updateTrigger = updateTrigger; + + _updateTrigger.Starting += (sender, args) => OnStartup(); + _updateTrigger.Update += OnUpdate; + } + + #endregion + + #region Methods + + protected virtual void OnUpdate(object sender, EventArgs e) + { + Dictionary dataSet; + lock (_dataLock) + { + dataSet = _currentDataSet; + _currentDataSet = null; + } + + if ((dataSet != null) && (dataSet.Count != 0)) + Update(dataSet); + } + + protected virtual void OnStartup() { } + + protected abstract void Update(Dictionary dataSet); + + public virtual void SetData(Dictionary dataSet) + { + if ((dataSet == null) || (dataSet.Count == 0)) return; + + lock (_dataLock) + { + if (_currentDataSet == null) + _currentDataSet = dataSet; + else + { + foreach (KeyValuePair command in dataSet) + _currentDataSet[command.Key] = command.Value; + } + } + + _updateTrigger.TriggerHasData(); + } + + public virtual void Reset() + { + lock (_dataLock) + _currentDataSet = null; + } + + #endregion + } + + public abstract class UpdateQueue : UpdateQueue + { + #region Constructors + + /// + protected UpdateQueue(IUpdateTrigger updateTrigger) + : base(updateTrigger) + { } + + #endregion + + #region Methods + + public void SetData(IEnumerable leds) => SetData(leds?.ToDictionary(x => x.CustomData ?? x.Id, x => x.Color)); + + #endregion + } +} diff --git a/RGB.NET.Core/Devices/Update/UpdateTrigger.cs b/RGB.NET.Core/Devices/Update/UpdateTrigger.cs new file mode 100644 index 0000000..854a035 --- /dev/null +++ b/RGB.NET.Core/Devices/Update/UpdateTrigger.cs @@ -0,0 +1,128 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace RGB.NET.Core +{ + public class UpdateTrigger : IUpdateTrigger + { + #region Properties & Fields + + public int Timeout { get; set; } = 100; + + public double UpdateFrequency { get; private set; } + + private double _maxUpdateRate; + public double MaxUpdateRate + { + get => _maxUpdateRate; + set + { + _maxUpdateRate = value; + UpdateUpdateFrequency(); + } + } + + private double _updateRateHardLimit; + public double UpdateRateHardLimit + { + get => _updateRateHardLimit; + protected set + { + _updateRateHardLimit = value; + UpdateUpdateFrequency(); + } + } + + private AutoResetEvent _hasDataEvent = new AutoResetEvent(false); + + private bool _isRunning; + private Task _updateTask; + private CancellationTokenSource _updateTokenSource; + private CancellationToken _updateToken; + + #endregion + + #region Events + + /// + public event EventHandler Starting; + /// + public event EventHandler Update; + + #endregion + + #region Constructors + + public UpdateTrigger() + { } + + public UpdateTrigger(double updateRateHardLimit) + { + this._updateRateHardLimit = updateRateHardLimit; + } + + #endregion + + #region Methods + + public void Start() + { + if (_isRunning) return; + + _isRunning = true; + + _updateTokenSource?.Dispose(); + _updateTokenSource = new CancellationTokenSource(); + _updateTask = Task.Factory.StartNew(UpdateLoop, (_updateToken = _updateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default); + + } + + public async void Stop() + { + if (!_isRunning) return; + + _isRunning = false; + + _updateTokenSource.Cancel(); + await _updateTask; + _updateTask.Dispose(); + _updateTask = null; + } + + private void UpdateLoop() + { + Starting?.Invoke(this, null); + while (!_updateToken.IsCancellationRequested) + { + if (_hasDataEvent.WaitOne(Timeout)) + { + long preUpdateTicks = Stopwatch.GetTimestamp(); + + Update?.Invoke(this, null); + + if (UpdateFrequency > 0) + { + double lastUpdateTime = ((Stopwatch.GetTimestamp() - preUpdateTicks) / 10000.0); + int sleep = (int)((UpdateFrequency * 1000.0) - lastUpdateTime); + if (sleep > 0) + Thread.Sleep(sleep); + } + } + } + } + + /// + public void TriggerHasData() => _hasDataEvent.Set(); + + private void UpdateUpdateFrequency() + { + UpdateFrequency = MaxUpdateRate; + if ((UpdateFrequency <= 0) || ((UpdateRateHardLimit > 0) && (UpdateRateHardLimit < UpdateFrequency))) + UpdateFrequency = UpdateRateHardLimit; + } + + #endregion + } +} diff --git a/RGB.NET.Core/RGB.NET.Core.csproj b/RGB.NET.Core/RGB.NET.Core.csproj index f2eca93..078c3c9 100644 --- a/RGB.NET.Core/RGB.NET.Core.csproj +++ b/RGB.NET.Core/RGB.NET.Core.csproj @@ -64,6 +64,9 @@ + + + diff --git a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings index 5d92cdb..372a92a 100644 --- a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings +++ b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings @@ -3,6 +3,7 @@ True True True + True True True True diff --git a/RGB.NET.Devices.Asus/AsusDeviceProvider.cs b/RGB.NET.Devices.Asus/AsusDeviceProvider.cs index d485023..579a05f 100644 --- a/RGB.NET.Devices.Asus/AsusDeviceProvider.cs +++ b/RGB.NET.Devices.Asus/AsusDeviceProvider.cs @@ -63,6 +63,8 @@ namespace RGB.NET.Devices.Asus // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global public Func GetCulture { get; set; } = CultureHelper.GetCurrentCulture; + public UpdateTrigger UpdateTrigger { get; private set; } + #endregion #region Constructors @@ -75,6 +77,8 @@ namespace RGB.NET.Devices.Asus { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); } #endregion @@ -88,6 +92,8 @@ namespace RGB.NET.Devices.Asus try { + UpdateTrigger?.Stop(); + _AsusSDK.Reload(); IList devices = new List(); @@ -112,7 +118,7 @@ namespace RGB.NET.Devices.Asus IntPtr handle = Marshal.ReadIntPtr(mainboardHandles, i); _AsusSDK.SetMbMode(handle, 1); AsusMainboardRGBDevice device = new AsusMainboardRGBDevice(new AsusMainboardRGBDeviceInfo(RGBDeviceType.Mainboard, handle)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -143,7 +149,7 @@ namespace RGB.NET.Devices.Asus IntPtr handle = Marshal.ReadIntPtr(grapicsCardHandles, i); _AsusSDK.SetGPUMode(handle, 1); AsusGraphicsCardRGBDevice device = new AsusGraphicsCardRGBDevice(new AsusGraphicsCardRGBDeviceInfo(RGBDeviceType.GraphicsCard, handle)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -172,7 +178,7 @@ namespace RGB.NET.Devices.Asus // IntPtr handle = Marshal.ReadIntPtr(dramHandles, i); // _AsusSDK.SetDramMode(handle, 1); // AsusDramRGBDevice device = new AsusDramRGBDevice(new AsusDramRGBDeviceInfo(RGBDeviceType.DRAM, handle)); - // device.Initialize(); + // device.Initialize(UpdateTrigger); // devices.Add(device); // } //catch { if (throwExceptions) throw; } @@ -193,7 +199,7 @@ namespace RGB.NET.Devices.Asus { _AsusSDK.SetClaymoreKeyboardMode(keyboardHandle, 1); AsusKeyboardRGBDevice device = new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(RGBDeviceType.Keyboard, keyboardHandle, GetCulture())); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } } @@ -211,13 +217,15 @@ namespace RGB.NET.Devices.Asus { _AsusSDK.SetRogMouseMode(mouseHandle, 1); AsusMouseRGBDevice device = new AsusMouseRGBDevice(new AsusMouseRGBDeviceInfo(RGBDeviceType.Mouse, mouseHandle)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } } catch { if (throwExceptions) throw; } #endregion + + UpdateTrigger?.Start(); Devices = new ReadOnlyCollection(devices); IsInitialized = true; diff --git a/RGB.NET.Devices.Asus/Generic/AsusRGBDevice.cs b/RGB.NET.Devices.Asus/Generic/AsusRGBDevice.cs index 4c4b792..133048f 100644 --- a/RGB.NET.Devices.Asus/Generic/AsusRGBDevice.cs +++ b/RGB.NET.Devices.Asus/Generic/AsusRGBDevice.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using RGB.NET.Core; +using RGB.NET.Devices.Asus.Generic; namespace RGB.NET.Devices.Asus { @@ -16,17 +17,14 @@ namespace RGB.NET.Devices.Asus { #region Properties & Fields - /// - /// Gets or sets the internal color-data cache. - /// - protected byte[] ColorData { get; private set; } - /// /// /// Gets information about the . /// public override TDeviceInfo DeviceInfo { get; } + protected AsusUpdateQueue UpdateQueue { get; set; } + #endregion #region Constructors @@ -47,7 +45,7 @@ namespace RGB.NET.Devices.Asus /// /// Initializes the device. /// - public void Initialize() + public void Initialize(IUpdateTrigger updateTrigger) { InitializeLayout(); @@ -57,7 +55,8 @@ namespace RGB.NET.Devices.Asus Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } - ColorData = new byte[LedMapping.Count * 3]; + UpdateQueue = new AsusUpdateQueue(updateTrigger); + UpdateQueue.Initialize(GetUpdateColorAction(), DeviceInfo.Handle, LedMapping.Count); } /// @@ -66,28 +65,13 @@ namespace RGB.NET.Devices.Asus protected abstract void InitializeLayout(); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - - if (leds.Count > 0) - { - foreach (Led led in leds) - { - int index = ((int)led.CustomData) * 3; - ColorData[index] = led.Color.R; - ColorData[index + 1] = led.Color.B; - ColorData[index + 2] = led.Color.G; - } - - ApplyColorData(); - } - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); /// - /// Sends the color-data-cache to the device. + /// Gets a action to update the physical device. /// - protected abstract void ApplyColorData(); + /// + protected abstract Action GetUpdateColorAction(); /// /// @@ -96,8 +80,6 @@ namespace RGB.NET.Devices.Asus if ((DeviceInfo is AsusRGBDeviceInfo deviceInfo) && (deviceInfo.Handle != IntPtr.Zero)) Marshal.FreeHGlobal(deviceInfo.Handle); - ColorData = null; - base.Dispose(); } diff --git a/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs new file mode 100644 index 0000000..0dad595 --- /dev/null +++ b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Asus.Generic +{ + public class AsusUpdateQueue : UpdateQueue + { + #region Properties & Fields + + /// + /// Gets or sets the internal color-data cache. + /// + protected byte[] ColorData { get; private set; } + + private Action _updateAction; + private IntPtr _handle; + + #endregion + + #region Constructors + + public AsusUpdateQueue(IUpdateTrigger updateTrigger) + : base(updateTrigger) + { } + + #endregion + + #region Methods + + public void Initialize(Action updateAction, IntPtr handle, int ledCount) + { + _updateAction = updateAction; + _handle = handle; + + ColorData = new byte[ledCount * 3]; + } + + protected override void Update(Dictionary dataSet) + { + foreach (KeyValuePair data in dataSet) + { + int index = ((int)data.Key) * 3; + ColorData[index] = data.Value.R; + ColorData[index + 1] = data.Value.B; + ColorData[index + 2] = data.Value.G; + } + + _updateAction(_handle, ColorData); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Asus/Generic/IAsusRGBDevice.cs b/RGB.NET.Devices.Asus/Generic/IAsusRGBDevice.cs index ea31b62..3bba7bd 100644 --- a/RGB.NET.Devices.Asus/Generic/IAsusRGBDevice.cs +++ b/RGB.NET.Devices.Asus/Generic/IAsusRGBDevice.cs @@ -7,6 +7,6 @@ namespace RGB.NET.Devices.Asus /// internal interface IAsusRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(IUpdateTrigger updateTrigger); } } diff --git a/RGB.NET.Devices.Asus/GraphicsCard/AsusGraphicsCardRGBDevice.cs b/RGB.NET.Devices.Asus/GraphicsCard/AsusGraphicsCardRGBDevice.cs index 7730e88..360fa38 100644 --- a/RGB.NET.Devices.Asus/GraphicsCard/AsusGraphicsCardRGBDevice.cs +++ b/RGB.NET.Devices.Asus/GraphicsCard/AsusGraphicsCardRGBDevice.cs @@ -1,4 +1,5 @@ -using RGB.NET.Core; +using System; +using RGB.NET.Core; using RGB.NET.Devices.Asus.Native; namespace RGB.NET.Devices.Asus @@ -40,7 +41,7 @@ namespace RGB.NET.Devices.Asus protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.GraphicsCard1; /// - protected override void ApplyColorData() => _AsusSDK.SetGPUColor(DeviceInfo.Handle, ColorData); + protected override Action GetUpdateColorAction() => _AsusSDK.SetGPUColor; #endregion } diff --git a/RGB.NET.Devices.Asus/Keyboard/AsusKeyboardRGBDevice.cs b/RGB.NET.Devices.Asus/Keyboard/AsusKeyboardRGBDevice.cs index 4c47ee4..6ced56e 100644 --- a/RGB.NET.Devices.Asus/Keyboard/AsusKeyboardRGBDevice.cs +++ b/RGB.NET.Devices.Asus/Keyboard/AsusKeyboardRGBDevice.cs @@ -1,4 +1,5 @@ -using RGB.NET.Core; +using System; +using RGB.NET.Core; using RGB.NET.Devices.Asus.Native; namespace RGB.NET.Devices.Asus @@ -38,9 +39,9 @@ namespace RGB.NET.Devices.Asus /// protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Keyboard_Escape; - + /// - protected override void ApplyColorData() => _AsusSDK.SetClaymoreKeyboardColor(DeviceInfo.Handle, ColorData); + protected override Action GetUpdateColorAction() => _AsusSDK.SetClaymoreKeyboardColor; #endregion } diff --git a/RGB.NET.Devices.Asus/Mainboard/AsusMainboardRGBDevice.cs b/RGB.NET.Devices.Asus/Mainboard/AsusMainboardRGBDevice.cs index e7b495b..f1609b6 100644 --- a/RGB.NET.Devices.Asus/Mainboard/AsusMainboardRGBDevice.cs +++ b/RGB.NET.Devices.Asus/Mainboard/AsusMainboardRGBDevice.cs @@ -1,4 +1,5 @@ -using RGB.NET.Core; +using System; +using RGB.NET.Core; using RGB.NET.Devices.Asus.Native; namespace RGB.NET.Devices.Asus @@ -48,7 +49,7 @@ namespace RGB.NET.Devices.Asus } /// - protected override void ApplyColorData() => _AsusSDK.SetMbColor(DeviceInfo.Handle, ColorData); + protected override Action GetUpdateColorAction() => _AsusSDK.SetMbColor; #endregion } diff --git a/RGB.NET.Devices.Asus/Mouse/AsusMouseRGBDevice.cs b/RGB.NET.Devices.Asus/Mouse/AsusMouseRGBDevice.cs index 68414eb..03db09c 100644 --- a/RGB.NET.Devices.Asus/Mouse/AsusMouseRGBDevice.cs +++ b/RGB.NET.Devices.Asus/Mouse/AsusMouseRGBDevice.cs @@ -1,4 +1,5 @@ -using RGB.NET.Core; +using System; +using RGB.NET.Core; using RGB.NET.Devices.Asus.Native; namespace RGB.NET.Devices.Asus @@ -39,7 +40,7 @@ namespace RGB.NET.Devices.Asus protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Mouse1; /// - protected override void ApplyColorData() => _AsusSDK.SetRogMouseColor(DeviceInfo.Handle, ColorData); + protected override Action GetUpdateColorAction() => _AsusSDK.SetRogMouseColor; #endregion } diff --git a/RGB.NET.Devices.Asus/RGB.NET.Devices.Asus.csproj b/RGB.NET.Devices.Asus/RGB.NET.Devices.Asus.csproj index 14dffc4..4165691 100644 --- a/RGB.NET.Devices.Asus/RGB.NET.Devices.Asus.csproj +++ b/RGB.NET.Devices.Asus/RGB.NET.Devices.Asus.csproj @@ -54,6 +54,7 @@ + diff --git a/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs index 9537451..b205126 100644 --- a/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs +++ b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs @@ -63,6 +63,8 @@ namespace RGB.NET.Devices.CoolerMaster // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global public Func GetCulture { get; set; } = CultureHelper.GetCurrentCulture; + public UpdateTrigger UpdateTrigger { get; private set; } + #endregion #region Constructors @@ -75,6 +77,8 @@ namespace RGB.NET.Devices.CoolerMaster { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); } #endregion @@ -88,6 +92,8 @@ namespace RGB.NET.Devices.CoolerMaster try { + UpdateTrigger?.Stop(); + _CoolerMasterSDK.Reload(); if (_CoolerMasterSDK.GetSDKVersion() <= 0) return false; @@ -120,13 +126,15 @@ namespace RGB.NET.Devices.CoolerMaster _CoolerMasterSDK.EnableLedControl(true); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } } catch { if (throwExceptions) throw; } } + UpdateTrigger?.Start(); + Devices = new ReadOnlyCollection(devices); IsInitialized = true; } diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs index 3a59b96..cace83e 100644 --- a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs +++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterRGBDevice.cs @@ -15,12 +15,15 @@ namespace RGB.NET.Devices.CoolerMaster where TDeviceInfo : CoolerMasterRGBDeviceInfo { #region Properties & Fields + /// /// /// Gets information about the . /// public override TDeviceInfo DeviceInfo { get; } + protected CoolerMasterUpdateQueue UpdateQueue { get; set; } + #endregion #region Constructors @@ -42,7 +45,7 @@ namespace RGB.NET.Devices.CoolerMaster /// /// Initializes the device. /// - public void Initialize() + public void Initialize(IUpdateTrigger updateTrigger) { InitializeLayout(); @@ -51,6 +54,8 @@ namespace RGB.NET.Devices.CoolerMaster Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } + + UpdateQueue = new CoolerMasterUpdateQueue(updateTrigger, DeviceInfo.DeviceIndex); } /// @@ -59,23 +64,7 @@ namespace RGB.NET.Devices.CoolerMaster protected abstract void InitializeLayout(); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - - if (leds.Count > 0) - { - _CoolerMasterSDK.SetControlDevice(DeviceInfo.DeviceIndex); - - foreach (Led led in leds) - { - (int row, int column) = ((int, int))led.CustomData; - _CoolerMasterSDK.SetLedColor(row, column, led.Color.R, led.Color.G, led.Color.B); - } - - _CoolerMasterSDK.RefreshLed(false); - } - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); /// /// diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs new file mode 100644 index 0000000..93e32eb --- /dev/null +++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using RGB.NET.Core; +using RGB.NET.Devices.CoolerMaster.Native; + +namespace RGB.NET.Devices.CoolerMaster +{ + public class CoolerMasterUpdateQueue : UpdateQueue + { + #region Properties & Fields + + private CoolerMasterDevicesIndexes _deviceIndex; + + #endregion + + #region Constructors + + public CoolerMasterUpdateQueue(IUpdateTrigger updateTrigger, CoolerMasterDevicesIndexes deviceIndex) + : base(updateTrigger) + { + this._deviceIndex = deviceIndex; + } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + _CoolerMasterSDK.SetControlDevice(_deviceIndex); + + foreach (KeyValuePair data in dataSet) + { + (int row, int column) = ((int, int))data.Key; + _CoolerMasterSDK.SetLedColor(row, column, data.Value.R, data.Value.G, data.Value.B); + } + + _CoolerMasterSDK.RefreshLed(false); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.CoolerMaster/Generic/ICoolerMasterRGBDevice.cs b/RGB.NET.Devices.CoolerMaster/Generic/ICoolerMasterRGBDevice.cs index cf33e57..e7786ac 100644 --- a/RGB.NET.Devices.CoolerMaster/Generic/ICoolerMasterRGBDevice.cs +++ b/RGB.NET.Devices.CoolerMaster/Generic/ICoolerMasterRGBDevice.cs @@ -7,6 +7,6 @@ namespace RGB.NET.Devices.CoolerMaster /// internal interface ICoolerMasterRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(IUpdateTrigger updateTrigger); } } diff --git a/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj b/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj index f13ebd4..83b17ee 100644 --- a/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj +++ b/RGB.NET.Devices.CoolerMaster/RGB.NET.Devices.CoolerMaster.csproj @@ -55,6 +55,7 @@ + diff --git a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs index 8193a89..213ef34 100644 --- a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs +++ b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs @@ -66,6 +66,9 @@ namespace RGB.NET.Devices.Corsair /// public IEnumerable Devices { get; private set; } + public UpdateTrigger UpdateTrigger { get; private set; } + private CorsairUpdateQueue _updateQueue; + #endregion #region Constructors @@ -78,6 +81,9 @@ namespace RGB.NET.Devices.Corsair { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); + _updateQueue = new CorsairUpdateQueue(UpdateTrigger); } #endregion @@ -93,6 +99,8 @@ namespace RGB.NET.Devices.Corsair try { + UpdateTrigger?.Stop(); + _CUESDK.Reload(); ProtocolDetails = new CorsairProtocolDetails(_CUESDK.CorsairPerformProtocolHandshake()); @@ -130,7 +138,7 @@ namespace RGB.NET.Devices.Corsair ICorsairRGBDevice device = GetRGBDevice(info, i, nativeDeviceInfo); if ((device == null) || !loadFilter.HasFlag(device.DeviceInfo.DeviceType)) continue; - device.Initialize(); + device.Initialize(_updateQueue); AddSpecialParts(device); error = LastError; @@ -142,6 +150,8 @@ namespace RGB.NET.Devices.Corsair catch { if (throwExceptions) throw; } } + UpdateTrigger?.Start(); + Devices = new ReadOnlyCollection(devices); IsInitialized = true; } diff --git a/RGB.NET.Devices.Corsair/Generic/CorsairRGBDevice.cs b/RGB.NET.Devices.Corsair/Generic/CorsairRGBDevice.cs index d70ad9c..6a96811 100644 --- a/RGB.NET.Devices.Corsair/Generic/CorsairRGBDevice.cs +++ b/RGB.NET.Devices.Corsair/Generic/CorsairRGBDevice.cs @@ -29,6 +29,8 @@ namespace RGB.NET.Devices.Corsair // ReSharper disable once MemberCanBePrivate.Global protected Dictionary InternalLedMapping { get; } = new Dictionary(); + protected CorsairUpdateQueue UpdateQueue { get; set; } + #endregion #region Indexer @@ -61,8 +63,10 @@ namespace RGB.NET.Devices.Corsair /// /// Initializes the device. /// - public void Initialize() + public void Initialize(CorsairUpdateQueue updateQueue) { + UpdateQueue = updateQueue; + InitializeLayout(); foreach (Led led in LedMapping.Values) @@ -86,31 +90,7 @@ namespace RGB.NET.Devices.Corsair /// protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => (x.Color.A > 0) && (x.CustomData is CorsairLedId ledId && (ledId != CorsairLedId.Invalid))).ToList(); - - if (leds.Count > 0) // CUE seems to crash if 'CorsairSetLedsColors' is called with a zero length array - { - int structSize = Marshal.SizeOf(typeof(_CorsairLedColor)); - IntPtr ptr = Marshal.AllocHGlobal(structSize * leds.Count); - IntPtr addPtr = new IntPtr(ptr.ToInt64()); - foreach (Led led in leds) - { - _CorsairLedColor color = new _CorsairLedColor - { - ledId = (int)led.CustomData, - r = led.Color.R, - g = led.Color.G, - b = led.Color.B - }; - - Marshal.StructureToPtr(color, addPtr, false); - addPtr = new IntPtr(addPtr.ToInt64() + structSize); - } - _CUESDK.CorsairSetLedsColors(leds.Count, ptr); - Marshal.FreeHGlobal(ptr); - } - } + => UpdateQueue.SetData(ledsToUpdate.Where(x => (x.Color.A > 0) && (x.CustomData is CorsairLedId ledId && (ledId != CorsairLedId.Invalid)))); /// public override void SyncBack() diff --git a/RGB.NET.Devices.Corsair/Generic/CorsairUpdateQueue.cs b/RGB.NET.Devices.Corsair/Generic/CorsairUpdateQueue.cs new file mode 100644 index 0000000..46e84e6 --- /dev/null +++ b/RGB.NET.Devices.Corsair/Generic/CorsairUpdateQueue.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Corsair.Native; + +namespace RGB.NET.Devices.Corsair +{ + public class CorsairUpdateQueue : UpdateQueue + { + #region Constructors + + public CorsairUpdateQueue(IUpdateTrigger updateTrigger) + : base(updateTrigger) + { } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + int structSize = Marshal.SizeOf(typeof(_CorsairLedColor)); + IntPtr ptr = Marshal.AllocHGlobal(structSize * dataSet.Count); + IntPtr addPtr = new IntPtr(ptr.ToInt64()); + foreach (KeyValuePair data in dataSet) + { + _CorsairLedColor color = new _CorsairLedColor + { + ledId = (int)data.Key, + r = data.Value.R, + g = data.Value.G, + b = data.Value.B + }; + + Marshal.StructureToPtr(color, addPtr, false); + addPtr = new IntPtr(addPtr.ToInt64() + structSize); + } + _CUESDK.CorsairSetLedsColors(dataSet.Count, ptr); + Marshal.FreeHGlobal(ptr); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Corsair/Generic/ICorsairRGBDevice.cs b/RGB.NET.Devices.Corsair/Generic/ICorsairRGBDevice.cs index fbc9254..2ea2334 100644 --- a/RGB.NET.Devices.Corsair/Generic/ICorsairRGBDevice.cs +++ b/RGB.NET.Devices.Corsair/Generic/ICorsairRGBDevice.cs @@ -7,6 +7,6 @@ namespace RGB.NET.Devices.Corsair /// internal interface ICorsairRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(CorsairUpdateQueue updateQueue); } } diff --git a/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj b/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj index 7e43d22..bdb83f8 100644 --- a/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj +++ b/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj @@ -59,6 +59,7 @@ + diff --git a/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj.DotSettings b/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj.DotSettings index 1b3f9b0..355ebb9 100644 --- a/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj.DotSettings +++ b/RGB.NET.Devices.Corsair/RGB.NET.Devices.Corsair.csproj.DotSettings @@ -1,4 +1,5 @@  + True True True True diff --git a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs index 804b9b8..d8cdb24 100644 --- a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs +++ b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs @@ -37,6 +37,8 @@ namespace RGB.NET.Devices.DMX /// public List DeviceDefinitions { get; } = new List(); + public UpdateTrigger UpdateTrigger { get; private set; } + #endregion #region Constructors @@ -49,6 +51,8 @@ namespace RGB.NET.Devices.DMX { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DMXDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); } #endregion @@ -68,6 +72,8 @@ namespace RGB.NET.Devices.DMX try { + UpdateTrigger.Stop(); + IList devices = new List(); foreach (IDMXDeviceDefinition dmxDeviceDefinition in DeviceDefinitions) @@ -79,7 +85,7 @@ namespace RGB.NET.Devices.DMX if (e131DMXDeviceDefinition.Leds.Count > 0) { E131Device device = new E131Device(new E131DeviceInfo(e131DMXDeviceDefinition), e131DMXDeviceDefinition.Leds); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } } @@ -87,6 +93,8 @@ namespace RGB.NET.Devices.DMX catch { if (throwExceptions) throw; } } + UpdateTrigger.Start(); + Devices = new ReadOnlyCollection(devices); IsInitialized = true; } diff --git a/RGB.NET.Devices.DMX/E131/E131Device.cs b/RGB.NET.Devices.DMX/E131/E131Device.cs index 9ca1f1a..cf430df 100644 --- a/RGB.NET.Devices.DMX/E131/E131Device.cs +++ b/RGB.NET.Devices.DMX/E131/E131Device.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Sockets; using RGB.NET.Core; namespace RGB.NET.Devices.DMX.E131 @@ -13,27 +12,13 @@ namespace RGB.NET.Devices.DMX.E131 { #region Properties & Fields - /// - /// Gets the UDP-Connection used to send data. - /// - private UdpClient _socket; - - /// - /// Gets the byte-representation of a E1.31 packet as described in http://tsp.esta.org/tsp/documents/docs/E1-31-2016.pdf. - /// CID, SequenceNumber, Universe and PropertyValues needs to be updated before use! - /// - private byte[] _dataPacket = { 0x00, 0x10, 0x00, 0x00, 0x41, 0x53, 0x43, 0x2D, 0x45, 0x31, 0x2E, 0x31, 0x37, 0x00, 0x00, 0x00, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x72, 0x0B, 0x02, 0xA1, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - /// - /// Gets or sets the Sequence number used to detect the order in which packages where sent. - /// - private byte _sequenceNumber = 0; - /// public override E131DeviceInfo DeviceInfo { get; } private readonly Dictionary getValueFunc)>> _ledMappings; + private E131UpdateQueue _updateQueue; + #endregion #region Constructors @@ -49,18 +34,12 @@ namespace RGB.NET.Devices.DMX.E131 #region Methods - internal void Initialize() + internal void Initialize(IUpdateTrigger updateTrigger) { int count = 0; foreach (LedId id in _ledMappings.Keys) InitializeLed(id, new Rectangle((count++) * 10, 0, 10, 10)); - _socket = new UdpClient(); - _socket.Connect(DeviceInfo.Hostname, DeviceInfo.Port); - - _dataPacket.SetCID(DeviceInfo.CID); - _dataPacket.SetUniverse(DeviceInfo.Universe); - //TODO DarthAffe 18.02.2018: Allow to load a layout. if (Size == Size.Invalid) @@ -68,44 +47,17 @@ namespace RGB.NET.Devices.DMX.E131 Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } + + _updateQueue = new E131UpdateQueue(updateTrigger, DeviceInfo.Hostname, DeviceInfo.Port); + _updateQueue.DataPacket.SetCID(DeviceInfo.CID); + _updateQueue.DataPacket.SetUniverse(DeviceInfo.Universe); } /// protected override object CreateLedCustomData(LedId ledId) => new LedChannelMapping(_ledMappings[ledId]); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - if (leds.Count > 0) - { - _dataPacket.SetSequenceNumber(GetNextSequenceNumber()); - - foreach (Led led in leds) - { - LedChannelMapping mapping = (LedChannelMapping)led.CustomData; - foreach ((int channel, Func getValue) in mapping) - _dataPacket.SetChannel(channel, getValue(led.Color)); - } - - _socket.Send(_dataPacket, _dataPacket.Length); - } - } - - /// - /// Increments the sequence number and wraps it if neded. - /// - /// The next usable sequence number. - private byte GetNextSequenceNumber() - { - if (_sequenceNumber == byte.MaxValue) - { - _sequenceNumber = byte.MinValue; - return byte.MaxValue; - } - - return _sequenceNumber++; - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => _updateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); #endregion } diff --git a/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs new file mode 100644 index 0000000..b67ffca --- /dev/null +++ b/RGB.NET.Devices.DMX/E131/E131UpdateQueue.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using RGB.NET.Core; + +namespace RGB.NET.Devices.DMX.E131 +{ + public class E131UpdateQueue : UpdateQueue + { + #region Properties & Fields + + /// + /// The UDP-Connection used to send data. + /// + private UdpClient _socket; + + /// + /// Gets the byte-representation of a E1.31 packet as described in http://tsp.esta.org/tsp/documents/docs/E1-31-2016.pdf. + /// CID, SequenceNumber, Universe and PropertyValues needs to be updated before use! + /// + internal byte[] DataPacket { get; } = { 0x00, 0x10, 0x00, 0x00, 0x41, 0x53, 0x43, 0x2D, 0x45, 0x31, 0x2E, 0x31, 0x37, 0x00, 0x00, 0x00, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x72, 0x0B, 0x02, 0xA1, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + /// + /// The sequence-number used to detect the order in which packages where sent. + /// + private byte _sequenceNumber = 0; + + #endregion + + #region Constructors + + public E131UpdateQueue(IUpdateTrigger updateTrigger, string hostname, int port) + : base(updateTrigger) + { + _socket = new UdpClient(); + _socket.Connect(hostname, port); + } + + #endregion + + #region Methods + + protected override void Update(Dictionary dataSet) + { + DataPacket.SetSequenceNumber(GetNextSequenceNumber()); + + foreach (KeyValuePair data in dataSet) + { + LedChannelMapping mapping = (LedChannelMapping)data.Key; + foreach ((int channel, Func getValue) in mapping) + DataPacket.SetChannel(channel, getValue(data.Value)); + } + + _socket.Send(DataPacket, DataPacket.Length); + } + + /// + /// Increments the sequence number and wraps it if neded. + /// + /// The next usable sequence number. + private byte GetNextSequenceNumber() + { + if (_sequenceNumber == byte.MaxValue) + { + _sequenceNumber = byte.MinValue; + return byte.MaxValue; + } + + return _sequenceNumber++; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.DMX/RGB.NET.Devices.DMX.csproj b/RGB.NET.Devices.DMX/RGB.NET.Devices.DMX.csproj index 7c005c2..9b4bd4f 100644 --- a/RGB.NET.Devices.DMX/RGB.NET.Devices.DMX.csproj +++ b/RGB.NET.Devices.DMX/RGB.NET.Devices.DMX.csproj @@ -51,6 +51,7 @@ + diff --git a/RGB.NET.Devices.Logitech/Generic/ILogitechRGBDevice.cs b/RGB.NET.Devices.Logitech/Generic/ILogitechRGBDevice.cs index d8f3154..4c8e260 100644 --- a/RGB.NET.Devices.Logitech/Generic/ILogitechRGBDevice.cs +++ b/RGB.NET.Devices.Logitech/Generic/ILogitechRGBDevice.cs @@ -7,6 +7,6 @@ namespace RGB.NET.Devices.Logitech /// internal interface ILogitechRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(UpdateQueue updateQueue); } } diff --git a/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs index 8c60d0b..db0d96c 100644 --- a/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs +++ b/RGB.NET.Devices.Logitech/Generic/LogitechRGBDevice.cs @@ -19,6 +19,8 @@ namespace RGB.NET.Devices.Logitech /// public override TDeviceInfo DeviceInfo { get; } + protected UpdateQueue UpdateQueue { get; set; } + #endregion #region Constructors @@ -39,7 +41,7 @@ namespace RGB.NET.Devices.Logitech /// /// Initializes the device. /// - public void Initialize() + public void Initialize(UpdateQueue updateQueue) { InitializeLayout(); @@ -48,6 +50,8 @@ namespace RGB.NET.Devices.Logitech Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } + + UpdateQueue = updateQueue; } /// diff --git a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs index 80575c7..3363b6d 100644 --- a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs +++ b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs @@ -56,6 +56,10 @@ namespace RGB.NET.Devices.Logitech /// public Func GetCulture { get; set; } = CultureHelper.GetCurrentCulture; + public UpdateTrigger UpdateTrigger { get; private set; } + private LogitechPerDeviceUpdateQueue _perDeviceUpdateQueue; + private LogitechPerKeyUpdateQueue _perKeyUpdateQueue; + #endregion #region Constructors @@ -68,6 +72,10 @@ namespace RGB.NET.Devices.Logitech { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(LogitechDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); + _perDeviceUpdateQueue = new LogitechPerDeviceUpdateQueue(UpdateTrigger); + _perKeyUpdateQueue = new LogitechPerKeyUpdateQueue(UpdateTrigger); } #endregion @@ -88,6 +96,8 @@ namespace RGB.NET.Devices.Logitech try { + UpdateTrigger?.Stop(); + _LogitechGSDK.Reload(); if (!_LogitechGSDK.LogiLedInit()) return false; @@ -104,7 +114,7 @@ namespace RGB.NET.Devices.Logitech if (loadFilter.HasFlag(deviceType)) //TODO DarthAffe 07.12.2017: Check if it's worth to try another device if the one returned doesn't match the filter { ILogitechRGBDevice device = new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.PerKeyRGB, imageLayout, layoutPath)); - device.Initialize(); + device.Initialize(_perKeyUpdateQueue); devices.Add(device); } } @@ -119,13 +129,15 @@ namespace RGB.NET.Devices.Logitech if (loadFilter.HasFlag(deviceType)) //TODO DarthAffe 07.12.2017: Check if it's worth to try another device if the one returned doesn't match the filter { ILogitechRGBDevice device = new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.DeviceRGB, imageLayout, layoutPath)); - device.Initialize(); + device.Initialize(_perDeviceUpdateQueue); devices.Add(device); } } } catch { if (throwExceptions) throw; } + UpdateTrigger?.Start(); + Devices = new ReadOnlyCollection(devices); IsInitialized = true; } diff --git a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceRGBDevice.cs b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceRGBDevice.cs index 71a1e5f..471b438 100644 --- a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceRGBDevice.cs +++ b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceRGBDevice.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using RGB.NET.Core; -using RGB.NET.Devices.Logitech.Native; namespace RGB.NET.Devices.Logitech { @@ -37,19 +35,10 @@ namespace RGB.NET.Devices.Logitech } /// - protected override object CreateLedCustomData(LedId ledId) => LogitechLedId.DEVICE; + protected override object CreateLedCustomData(LedId ledId) => (ledId, LogitechLedId.DEVICE); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - Led led = ledsToUpdate.FirstOrDefault(x => x.Color.A > 0); - if (led == null) return; - - _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.DeviceRGB); - _LogitechGSDK.LogiLedSetLighting((int)Math.Round(led.Color.RPercent * 100), - (int)Math.Round(led.Color.GPercent * 100), - (int)Math.Round(led.Color.BPercent * 100)); - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0).Take(1)); #endregion } diff --git a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs new file mode 100644 index 0000000..dc97ef6 --- /dev/null +++ b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Devices.Logitech.Native; + +namespace RGB.NET.Devices.Logitech +{ + public class LogitechPerDeviceUpdateQueue : UpdateQueue + { + #region Constructors + + public LogitechPerDeviceUpdateQueue(IUpdateTrigger updateTrigger) + : base(updateTrigger) + { } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + Color color = dataSet.Values.First(); + + _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.DeviceRGB); + _LogitechGSDK.LogiLedSetLighting((int)Math.Round(color.RPercent * 100), + (int)Math.Round(color.GPercent * 100), + (int)Math.Round(color.BPercent * 100)); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyRGBDevice.cs b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyRGBDevice.cs index 78603ea..cdf10bf 100644 --- a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyRGBDevice.cs +++ b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyRGBDevice.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using RGB.NET.Core; -using RGB.NET.Devices.Logitech.Native; namespace RGB.NET.Devices.Logitech { @@ -28,37 +26,10 @@ namespace RGB.NET.Devices.Logitech #region Methods /// - protected override object CreateLedCustomData(LedId ledId) => PerKeyIdMapping.DEFAULT.TryGetValue(ledId, out LogitechLedId logitechLedId) ? logitechLedId : LogitechLedId.Invalid; + protected override object CreateLedCustomData(LedId ledId) => (ledId, PerKeyIdMapping.DEFAULT.TryGetValue(ledId, out LogitechLedId logitechLedId) ? logitechLedId : LogitechLedId.Invalid); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - if (leds.Count <= 0) return; - - _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.PerKeyRGB); - - byte[] bitmap = null; - foreach (Led led in leds) - { - // DarthAffe 26.03.2017: This is only needed since update by name doesn't work as expected for all keys ... - if (BitmapMapping.BitmapOffset.TryGetValue(led.Id, out int bitmapOffset)) - { - if (bitmap == null) - bitmap = BitmapMapping.CreateBitmap(); - - BitmapMapping.SetColor(ref bitmap, bitmapOffset, led.Color); - } - else - _LogitechGSDK.LogiLedSetLightingForKeyWithKeyName((int)led.CustomData, - (int)Math.Round(led.Color.RPercent * 100), - (int)Math.Round(led.Color.GPercent * 100), - (int)Math.Round(led.Color.BPercent * 100)); - } - - if (bitmap != null) - _LogitechGSDK.LogiLedSetLightingFromBitmap(bitmap); - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); #endregion } diff --git a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs new file mode 100644 index 0000000..152cb8f --- /dev/null +++ b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using RGB.NET.Core; +using RGB.NET.Devices.Logitech.Native; + +namespace RGB.NET.Devices.Logitech +{ + public class LogitechPerKeyUpdateQueue : UpdateQueue + { + #region Constructors + + public LogitechPerKeyUpdateQueue(IUpdateTrigger updateTrigger) + : base(updateTrigger) + { } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.PerKeyRGB); + + byte[] bitmap = null; + foreach (KeyValuePair data in dataSet) + { + (LedId id, int customData) = ((LedId, int))data.Key; + + // DarthAffe 26.03.2017: This is only needed since update by name doesn't work as expected for all keys ... + if (BitmapMapping.BitmapOffset.TryGetValue(id, out int bitmapOffset)) + { + if (bitmap == null) + bitmap = BitmapMapping.CreateBitmap(); + + BitmapMapping.SetColor(ref bitmap, bitmapOffset, data.Value); + } + else + _LogitechGSDK.LogiLedSetLightingForKeyWithKeyName(customData, + (int)Math.Round(data.Value.RPercent * 100), + (int)Math.Round(data.Value.GPercent * 100), + (int)Math.Round(data.Value.BPercent * 100)); + } + + if (bitmap != null) + _LogitechGSDK.LogiLedSetLightingFromBitmap(bitmap); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj index 16bce15..3e92661 100644 --- a/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj +++ b/RGB.NET.Devices.Logitech/RGB.NET.Devices.Logitech.csproj @@ -57,11 +57,13 @@ + + diff --git a/RGB.NET.Devices.Novation/Generic/INovationRGBDevice.cs b/RGB.NET.Devices.Novation/Generic/INovationRGBDevice.cs index cde5132..76332cd 100644 --- a/RGB.NET.Devices.Novation/Generic/INovationRGBDevice.cs +++ b/RGB.NET.Devices.Novation/Generic/INovationRGBDevice.cs @@ -7,6 +7,6 @@ namespace RGB.NET.Devices.Novation /// internal interface INovationRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(IUpdateTrigger updateTrigger); } } diff --git a/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs b/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs new file mode 100644 index 0000000..49e5a9d --- /dev/null +++ b/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using RGB.NET.Core; +using Sanford.Multimedia.Midi; + +namespace RGB.NET.Devices.Novation +{ + public class LimitedColorUpdateQueue : MidiUpdateQueue + { + #region Constructors + + public LimitedColorUpdateQueue(IUpdateTrigger updateTrigger, int deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override ShortMessage CreateMessage(KeyValuePair data) + { + NovationLedId ledId = (NovationLedId)data.Key; + return new ShortMessage(Convert.ToByte(ledId.GetStatus()), Convert.ToByte(ledId.GetId()), Convert.ToByte(ConvertColor(data.Value))); + } + + /// + /// Convert a to its novation-representation depending on the of the . + /// The conversion uses only a limited amount of colors (3 red, 3 yellow, 3 green). + /// + /// The to convert. + /// The novation-representation of the . + protected virtual int ConvertColor(Color color) + { + if ((color.Hue >= 330) || (color.Hue < 30)) + return (int)Math.Ceiling(color.Value * 3); // red with brightness 1, 2 or 3 + + if ((color.Hue >= 30) && (color.Hue < 90)) // yellow with brightness 17, 34 or 51 + return (int)Math.Ceiling(color.Value * 3) * 17; + + if ((color.Hue >= 90) && (color.Hue < 150)) // green with brightness 16, 32 or 48 + return (int)Math.Ceiling(color.Value * 3) * 16; + + return 0; + } + + /// + public override void Reset() + { + base.Reset(); + SendMessage(new ShortMessage(Convert.ToByte(0xB0), Convert.ToByte(0), Convert.ToByte(0))); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs b/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs new file mode 100644 index 0000000..1e31b9b --- /dev/null +++ b/RGB.NET.Devices.Novation/Generic/MidiUpdateQueue.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using RGB.NET.Core; +using Sanford.Multimedia.Midi; + +namespace RGB.NET.Devices.Novation +{ + public abstract class MidiUpdateQueue : UpdateQueue, IDisposable + { + #region Properties & Fields + + private readonly OutputDevice _outputDevice; + + #endregion + + #region Constructors + + public MidiUpdateQueue(IUpdateTrigger updateTrigger, int deviceId) + : base(updateTrigger) + { + _outputDevice = new OutputDevice(deviceId); + } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + foreach (KeyValuePair data in dataSet) + SendMessage(CreateMessage(data)); + } + + protected virtual void SendMessage(ShortMessage message) => _outputDevice.SendShort(message.Message); + + protected abstract ShortMessage CreateMessage(KeyValuePair data); + + public void Dispose() + { + _outputDevice.Dispose(); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Novation/Generic/NovationRGBDevice.cs b/RGB.NET.Devices.Novation/Generic/NovationRGBDevice.cs index f6a3e4f..6942c3b 100644 --- a/RGB.NET.Devices.Novation/Generic/NovationRGBDevice.cs +++ b/RGB.NET.Devices.Novation/Generic/NovationRGBDevice.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using RGB.NET.Core; -using Sanford.Multimedia.Midi; namespace RGB.NET.Devices.Novation { @@ -16,14 +15,13 @@ namespace RGB.NET.Devices.Novation { #region Properties & Fields - private readonly OutputDevice _outputDevice; - private readonly TDeviceInfo _deviceInfo; - /// /// /// Gets information about the . /// - public override TDeviceInfo DeviceInfo => _deviceInfo; + public override TDeviceInfo DeviceInfo { get; } + + protected MidiUpdateQueue UpdateQueue { get; set; } #endregion @@ -35,20 +33,17 @@ namespace RGB.NET.Devices.Novation /// The generic information provided by Novation for the device. protected NovationRGBDevice(TDeviceInfo info) { - this._deviceInfo = info; - - _outputDevice = new OutputDevice(info.DeviceId); + this.DeviceInfo = info; } #endregion #region Methods - /// /// Initializes the device. /// - public void Initialize() + public void Initialize(IUpdateTrigger updateTrigger) { InitializeLayout(); @@ -57,6 +52,9 @@ namespace RGB.NET.Devices.Novation Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } + + if (DeviceInfo.ColorCapabilities == NovationColorCapabilities.LimitedRG) + UpdateQueue = new LimitedColorUpdateQueue(updateTrigger, DeviceInfo.DeviceId); } /// @@ -65,96 +63,19 @@ namespace RGB.NET.Devices.Novation protected abstract void InitializeLayout(); /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - - if (leds.Count > 0) - { - foreach (Led led in leds) - { - NovationLedId ledId = (NovationLedId)led.CustomData; - - int color = ConvertColor(led.Color); - SendMessage(ledId.GetStatus(), ledId.GetId(), color); - } - } - } + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); /// - /// Resets the back top default. + /// Resets the back to default. /// - public virtual void Reset() => SendMessage(0xB0, 0, 0); - - /// - /// Convert a to its novation-representation depending on the of the . - /// - /// The to convert. - /// The novation-representation of the . - protected virtual int ConvertColor(Color color) - { - switch (_deviceInfo.ColorCapabilities) - { - case NovationColorCapabilities.RGB: - return ConvertColorFull(color); - case NovationColorCapabilities.LimitedRG: - return ConvertColorLimited(color); - default: - return 0; - } - } - - /// - /// Convert a to its novation-representation depending on the of the . - /// The conversion uses the full rgb-range. - /// - /// The to convert. - /// The novation-representation of the . - protected virtual int ConvertColorFull(Color color) - { - //TODO DarthAffe 16.08.2017: How are colors for full rgb devices encoded? - return 0; - } - - /// - /// Convert a to its novation-representation depending on the of the . - /// The conversion uses only a limited amount of colors (3 red, 3 yellow, 3 green). - /// - /// The to convert. - /// The novation-representation of the . - protected virtual int ConvertColorLimited(Color color) - { - if ((color.Hue >= 330) || (color.Hue < 30)) - return (int)Math.Ceiling(color.Value * 3); // red with brightness 1, 2 or 3 - - if ((color.Hue >= 30) && (color.Hue < 90)) // yellow with brightness 17, 34 or 51 - return (int)Math.Ceiling(color.Value * 3) * 17; - - if ((color.Hue >= 90) && (color.Hue < 150)) // green with brightness 16, 32 or 48 - return (int)Math.Ceiling(color.Value * 3) * 16; - - return 0; - } - - /// - /// Sends a message to the . - /// - /// The status-code of the message. - /// The first data-package of the message. - /// The second data-package of the message. - protected virtual void SendMessage(int status, int data1, int data2) - { - ShortMessage shortMessage = new ShortMessage(Convert.ToByte(status), Convert.ToByte(data1), Convert.ToByte(data2)); - _outputDevice.SendShort(shortMessage.Message); - } + public virtual void Reset() => UpdateQueue.Reset(); /// /// public override void Dispose() { Reset(); - _outputDevice.Dispose(); - + UpdateQueue.Dispose(); base.Dispose(); } diff --git a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs index ce2b87b..08a06a7 100644 --- a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs +++ b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs @@ -39,6 +39,8 @@ namespace RGB.NET.Devices.Novation /// public IEnumerable Devices { get; private set; } + public UpdateTrigger UpdateTrigger { get; private set; } + #endregion #region Constructors @@ -51,6 +53,8 @@ namespace RGB.NET.Devices.Novation { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(NovationDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); } #endregion @@ -64,6 +68,8 @@ namespace RGB.NET.Devices.Novation try { + UpdateTrigger?.Stop(); + IList devices = new List(); if (loadFilter.HasFlag(RGBDeviceType.LedMatrix)) @@ -81,12 +87,13 @@ namespace RGB.NET.Devices.Novation if (deviceId == null) continue; INovationRGBDevice device = new NovationLaunchpadRGBDevice(new NovationLaunchpadRGBDeviceInfo(outCaps.name, index, deviceId.GetColorCapability())); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } } + UpdateTrigger?.Start(); Devices = new ReadOnlyCollection(devices); IsInitialized = true; } diff --git a/RGB.NET.Devices.Novation/RGB.NET.Devices.Novation.csproj b/RGB.NET.Devices.Novation/RGB.NET.Devices.Novation.csproj index 75dcbd3..e791329 100644 --- a/RGB.NET.Devices.Novation/RGB.NET.Devices.Novation.csproj +++ b/RGB.NET.Devices.Novation/RGB.NET.Devices.Novation.csproj @@ -54,6 +54,8 @@ + + diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs index 3d12c74..ee98391 100644 --- a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -45,20 +42,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Custom1; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.CHROMALINK_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _ChromaLinkCustomEffect effectParams = new _ChromaLinkCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerChromaLinkUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs new file mode 100644 index 0000000..0395c21 --- /dev/null +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerChromaLinkUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerChromaLinkUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.CHROMALINK_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _ChromaLinkCustomEffect effectParams = new _ChromaLinkCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs index 879537f..7eca600 100644 --- a/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs @@ -7,7 +7,7 @@ namespace RGB.NET.Devices.Razer /// internal interface IRazerRGBDevice : IRGBDevice { - void Initialize(); + void Initialize(IUpdateTrigger updateTrigger); void Reset(); } } diff --git a/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs index 0aea83f..b931371 100644 --- a/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using RGB.NET.Core; -using RGB.NET.Devices.Razer.Native; namespace RGB.NET.Devices.Razer { @@ -16,14 +14,14 @@ namespace RGB.NET.Devices.Razer { #region Properties & Fields - private Guid? _lastEffect; - /// /// /// Gets information about the . /// public override TDeviceInfo DeviceInfo { get; } + protected RazerUpdateQueue UpdateQueue { get; set; } + #endregion #region Constructors @@ -46,7 +44,7 @@ namespace RGB.NET.Devices.Razer /// /// Initializes the device. /// - public void Initialize() + public void Initialize(IUpdateTrigger updateTrigger) { InitializeLayout(); @@ -55,50 +53,24 @@ namespace RGB.NET.Devices.Razer Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); } + + UpdateQueue = CreateUpdateQueue(updateTrigger); } + protected abstract RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger); + /// /// Initializes the and of the device. /// protected abstract void InitializeLayout(); - + /// - protected override void UpdateLeds(IEnumerable ledsToUpdate) - { - List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); - - if (leds.Count <= 0) return; - - IntPtr effectParams = CreateEffectParams(leds); - Guid effectId = Guid.NewGuid(); - _RazerSDK.CreateEffect(DeviceInfo.DeviceId, _Defines.EFFECT_ID, effectParams, ref effectId); - - _RazerSDK.SetEffect(effectId); - - if (_lastEffect.HasValue) - _RazerSDK.DeleteEffect(_lastEffect.Value); - - _lastEffect = effectId; - } - - /// - /// Creates the device-specific effect parameters for the led-update. - /// - /// The leds to be updated. - /// An pointing to the effect parameter struct. - protected abstract IntPtr CreateEffectParams(IEnumerable leds); + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); /// /// Resets the device. /// - public void Reset() - { - if (_lastEffect.HasValue) - { - _RazerSDK.DeleteEffect(_lastEffect.Value); - _lastEffect = null; - } - } + public void Reset() => UpdateQueue.Reset(); #endregion } diff --git a/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs b/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs new file mode 100644 index 0000000..97baf19 --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerUpdateQueue.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public abstract class RazerUpdateQueue : UpdateQueue + { + #region Properties & Fields + + private readonly Guid _deviceId; + private Guid? _lastEffect; + + #endregion + + #region Constructors + + protected RazerUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger) + { + this._deviceId = deviceId; + } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + IntPtr effectParams = CreateEffectParams(dataSet); + Guid effectId = Guid.NewGuid(); + _RazerSDK.CreateEffect(_deviceId, _Defines.EFFECT_ID, effectParams, ref effectId); + + _RazerSDK.SetEffect(effectId); + + if (_lastEffect.HasValue) + _RazerSDK.DeleteEffect(_lastEffect.Value); + + _lastEffect = effectId; + } + + /// + public override void Reset() + { + if (_lastEffect.HasValue) + { + _RazerSDK.DeleteEffect(_lastEffect.Value); + _lastEffect = null; + } + } + + /// + /// Creates the device-specific effect parameters for the led-update. + /// + /// The data to be updated. + /// An pointing to the effect parameter struct. + protected abstract IntPtr CreateEffectParams(Dictionary dataSet); + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs index 2594236..f4c0d1a 100644 --- a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -45,20 +42,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Headset1; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.HEADSET_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _HeadsetCustomEffect effectParams = new _HeadsetCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerHeadsetUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs new file mode 100644 index 0000000..b867a4e --- /dev/null +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerHeadsetUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerHeadsetUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.HEADSET_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _HeadsetCustomEffect effectParams = new _HeadsetCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs index 2eb060c..06d4383 100644 --- a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -51,20 +48,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Keyboard_Escape; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.KEYBOARD_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _KeyboardCustomEffect effectParams = new _KeyboardCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerKeyboardUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs new file mode 100644 index 0000000..85eab50 --- /dev/null +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerKeyboardUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerKeyboardUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.KEYBOARD_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _KeyboardCustomEffect effectParams = new _KeyboardCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs index 3098ac2..6c94347 100644 --- a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -48,20 +45,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Keypad1; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.KEYPAD_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _KeypadCustomEffect effectParams = new _KeypadCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerKeypadUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs new file mode 100644 index 0000000..ef5932f --- /dev/null +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerKeypadUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerKeypadUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.KEYPAD_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _KeypadCustomEffect effectParams = new _KeypadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs index 0e39afa..3fe4b0b 100644 --- a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -48,20 +45,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Mouse1; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.MOUSE_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _MouseCustomEffect effectParams = new _MouseCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerMouseUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs new file mode 100644 index 0000000..914fe68 --- /dev/null +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerMouseUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerMouseUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.MOUSE_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _MouseCustomEffect effectParams = new _MouseCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs index f91138e..3fd96bf 100644 --- a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs @@ -1,9 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; using RGB.NET.Core; using RGB.NET.Devices.Razer.Native; @@ -45,20 +42,7 @@ namespace RGB.NET.Devices.Razer protected override object CreateLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Mousepad1; /// - protected override IntPtr CreateEffectParams(IEnumerable leds) - { - _Color[] colors = new _Color[_Defines.MOUSEPAD_MAX_LEDS]; - - foreach (Led led in leds) - colors[(int)led.CustomData] = new _Color(led.Color.R, led.Color.G, led.Color.B); - - _MousepadCustomEffect effectParams = new _MousepadCustomEffect { Color = colors }; - - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); - Marshal.StructureToPtr(effectParams, ptr, false); - - return ptr; - } + protected override RazerUpdateQueue CreateUpdateQueue(IUpdateTrigger updateTrigger) => new RazerMousepadUpdateQueue(updateTrigger, DeviceInfo.DeviceId); #endregion } diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs new file mode 100644 index 0000000..13046c2 --- /dev/null +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadUpdateQueue.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + public class RazerMousepadUpdateQueue : RazerUpdateQueue + { + #region Constructors + + public RazerMousepadUpdateQueue(IUpdateTrigger updateTrigger, Guid deviceId) + : base(updateTrigger, deviceId) + { } + + #endregion + + #region Methods + + /// + protected override IntPtr CreateEffectParams(Dictionary dataSet) + { + _Color[] colors = new _Color[_Defines.MOUSEPAD_MAX_LEDS]; + + foreach (KeyValuePair data in dataSet) + colors[(int)data.Key] = new _Color(data.Value); + + _MousepadCustomEffect effectParams = new _MousepadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Native/_Color.cs b/RGB.NET.Devices.Razer/Native/_Color.cs index c3c5794..66b07ea 100644 --- a/RGB.NET.Devices.Razer/Native/_Color.cs +++ b/RGB.NET.Devices.Razer/Native/_Color.cs @@ -4,6 +4,7 @@ // ReSharper disable MemberCanBePrivate.Global using System.Runtime.InteropServices; +using RGB.NET.Core; namespace RGB.NET.Devices.Razer.Native { @@ -19,6 +20,9 @@ namespace RGB.NET.Devices.Razer.Native #region Constructors + public _Color(Color color) + : this(color.R, color.G, color.B) { } + public _Color(byte red, byte green, byte blue) : this() { diff --git a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj index 77d5b98..200f613 100644 --- a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj +++ b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj @@ -45,6 +45,7 @@ + @@ -55,12 +56,18 @@ + + + + + + diff --git a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs index cd54054..3eae9f1 100644 --- a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs +++ b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs @@ -68,6 +68,8 @@ namespace RGB.NET.Devices.Razer /// public bool LoadEmulatorDevices { get; set; } = false; + public UpdateTrigger UpdateTrigger { get; private set; } + #endregion #region Constructors @@ -80,6 +82,8 @@ namespace RGB.NET.Devices.Razer { if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RazerDeviceProvider)}"); _instance = this; + + UpdateTrigger = new UpdateTrigger(); } #endregion @@ -96,10 +100,12 @@ namespace RGB.NET.Devices.Razer try { + UpdateTrigger?.Stop(); + _RazerSDK.Reload(); RazerError error; - if (((error = _RazerSDK.Init()) != RazerError.Success) + if (((error = _RazerSDK.Init()) != RazerError.Success) && Enum.IsDefined(typeof(RazerError), error)) //HACK DarthAffe 08.02.2018: The x86-SDK seems to have a problem here ... ThrowRazerError(error); @@ -113,7 +119,7 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.KEYBOARDS.FirstOrDefault().guid != guid))) continue; RazerKeyboardRGBDevice device = new RazerKeyboardRGBDevice(new RazerKeyboardRGBDeviceInfo(guid, model, GetCulture())); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -126,7 +132,7 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.MICE.FirstOrDefault().guid != guid))) continue; RazerMouseRGBDevice device = new RazerMouseRGBDevice(new RazerMouseRGBDeviceInfo(guid, model)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -139,7 +145,7 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.HEADSETS.FirstOrDefault().guid != guid))) continue; RazerHeadsetRGBDevice device = new RazerHeadsetRGBDevice(new RazerHeadsetRGBDeviceInfo(guid, model)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -152,7 +158,7 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.MOUSEMATS.FirstOrDefault().guid != guid))) continue; RazerMousepadRGBDevice device = new RazerMousepadRGBDevice(new RazerMousepadRGBDeviceInfo(guid, model)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -165,7 +171,7 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.KEYPADS.FirstOrDefault().guid != guid))) continue; RazerKeypadRGBDevice device = new RazerKeypadRGBDevice(new RazerKeypadRGBDeviceInfo(guid, model)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } @@ -178,11 +184,12 @@ namespace RGB.NET.Devices.Razer && (!LoadEmulatorDevices || (Razer.Devices.CHROMALINKS.FirstOrDefault().guid != guid))) continue; RazerChromaLinkRGBDevice device = new RazerChromaLinkRGBDevice(new RazerChromaLinkRGBDeviceInfo(guid, model)); - device.Initialize(); + device.Initialize(UpdateTrigger); devices.Add(device); } catch { if (throwExceptions) throw; } + UpdateTrigger?.Start(); Devices = new ReadOnlyCollection(devices); IsInitialized = true; }