// ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMethodReturnValue.Global // ReSharper disable VirtualMemberNeverOverridden.Global using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using CUE.NET.Brushes; using CUE.NET.Devices.Generic.Enums; using CUE.NET.Devices.Generic.EventArgs; using CUE.NET.Devices.Keyboard.Enums; using CUE.NET.Effects; using CUE.NET.Groups; using CUE.NET.Helper; using CUE.NET.Native; namespace CUE.NET.Devices.Generic { /// /// Represents a generic CUE-device. (keyboard, mouse, headset, ...) /// public abstract class AbstractCueDevice : ICueDevice { #region Properties & Fields private static DateTime _lastUpdate = DateTime.Now; /// /// Gets generic information provided by CUE for the device. /// public IDeviceInfo DeviceInfo { get; } /// /// Gets the rectangle containing all LEDs of the device. /// public RectangleF DeviceRectangle { get; protected set; } /// /// Gets a dictionary containing all LEDs of the device. /// protected Dictionary LedMapping { get; } = new Dictionary(); /// /// Gets a read-only collection containing the LEDs of the device. /// public IEnumerable Leds => new ReadOnlyCollection(LedMapping.Values.ToList()); /// /// Gets a list of attached ledgroups. /// protected LinkedList LedGroups { get; } = new LinkedList(); /// /// Gets or sets the background brush of the keyboard. /// public IBrush Brush { get; set; } /// /// Gets or sets the z-index of the background brush of the keyboard.
/// This value has absolutely no effect. ///
public int ZIndex { get; set; } = 0; #region Indexers /// /// Gets the with the specified ID. /// /// The ID of the LED to get. /// The LED with the specified ID or null if no LED is found. public CorsairLed this[CorsairLedId ledId] { get { CorsairLed key; return LedMapping.TryGetValue(ledId, out key) ? key : null; } } /// /// Gets the at the given physical location. /// /// The point to get the location from. /// The LED at the given point or null if no location is found. public CorsairLed this[PointF location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location)); /// /// Gets a list of inside the given rectangle. /// /// The rectangle to check. /// The minimal percentage overlay a location must have with the to be taken into the list. /// public IEnumerable this[RectangleF referenceRect, float minOverlayPercentage = 0.5f] => LedMapping.Values .Where(x => RectangleHelper.CalculateIntersectPercentage(x.LedRectangle, referenceRect) >= minOverlayPercentage); #endregion #endregion #region Events /// /// Occurs when a catched exception is thrown inside the device. /// public event ExceptionEventHandler Exception; /// /// Occurs when the device starts updating. /// public event UpdatingEventHandler Updating; /// /// Occurs when the device update is done. /// public event UpdatedEventHandler Updated; /// /// Occurs when the device starts to update the leds. /// public event LedsUpdatingEventHandler LedsUpdating; /// /// Occurs when the device updated the leds. /// public event LedsUpdatedEventHandler LedsUpdated; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The generic information provided by CUE for the device. protected AbstractCueDevice(IDeviceInfo info) { this.DeviceInfo = info; } #endregion #region Methods #region Initialize /// /// Initializes the device. /// public virtual void Initialize() { DeviceRectangle = RectangleHelper.CreateRectangleFromRectangles((this).Select(x => x.LedRectangle)); } /// /// Initializes the LED-Object with the specified id. /// /// The LED-Id to initialize. /// The rectangle representing the position of the LED to initialize. /// protected CorsairLed InitializeLed(CorsairLedId ledId, RectangleF ledRectangle) { if (LedMapping.ContainsKey(ledId)) return null; CorsairLed led = new CorsairLed(this, ledId, ledRectangle); LedMapping.Add(ledId, led); return led; } /// /// Resets all loaded LEDs back to default. /// internal void ResetLeds() { foreach (CorsairLed led in LedMapping.Values) led.Reset(); } #endregion #region Update /// /// Performs an update for all dirty keys, or all keys if flushLeds is set to true. /// /// Specifies whether all keys (including clean ones) should be updated. public void Update(bool flushLeds = false) { OnUpdating(); // Update effects foreach (ILedGroup ledGroup in LedGroups) ledGroup.UpdateEffects(); // Render brushes Render(this); foreach (ILedGroup ledGroup in LedGroups.OrderBy(x => x.ZIndex)) Render(ledGroup); // Device-specific updates DeviceUpdate(); // Send LEDs to SDK ICollection ledsToUpdate = (flushLeds ? LedMapping : LedMapping.Where(x => x.Value.IsDirty)).Select(x => new LedUpateRequest(x.Key, x.Value.RequestedColor)).ToList(); foreach (LedUpateRequest updateRequest in ledsToUpdate) LedMapping[updateRequest.LedId].Update(); UpdateLeds(ledsToUpdate); OnUpdated(); } /// /// Performs device specific updates. /// protected virtual void DeviceUpdate() { } /// /// Renders a ledgroup. /// /// The led group to render. // ReSharper disable once MemberCanBeMadeStatic.Local - idc protected virtual void Render(ILedGroup ledGroup) { if (ledGroup == null) return; IList leds = ledGroup.GetLeds().ToList(); IBrush brush = ledGroup.Brush; if (brush == null) return; try { switch (brush.BrushCalculationMode) { case BrushCalculationMode.Relative: RectangleF brushRectangle = RectangleHelper.CreateRectangleFromRectangles(leds.Select(x => x.LedRectangle)); float offsetX = -brushRectangle.X; float offsetY = -brushRectangle.Y; brushRectangle.X = 0; brushRectangle.Y = 0; brush.PerformRender(brushRectangle, leds.Select(x => new BrushRenderTarget(x.Id, x.LedRectangle.Move(offsetX, offsetY)))); break; case BrushCalculationMode.Absolute: brush.PerformRender(DeviceRectangle, leds.Select(x => new BrushRenderTarget(x.Id, x.LedRectangle))); break; default: throw new ArgumentException(); } brush.UpdateEffects(); brush.PerformFinalize(); foreach (KeyValuePair renders in brush.RenderedTargets) this[renders.Key.LedId].Color = renders.Value; } // ReSharper disable once CatchAllClause catch (Exception ex) { OnException(ex); } } private void UpdateLeds(ICollection updateRequests) { updateRequests = updateRequests.Where(x => x.Color != CorsairColor.Transparent).ToList(); OnLedsUpdating(updateRequests); if (updateRequests.Any()) // CUE seems to crash if 'CorsairSetLedsColors' is called with a zero length array { int structSize = Marshal.SizeOf(typeof(_CorsairLedColor)); IntPtr ptr = Marshal.AllocHGlobal(structSize * updateRequests.Count); IntPtr addPtr = new IntPtr(ptr.ToInt64()); foreach (LedUpateRequest ledUpdateRequest in updateRequests) { _CorsairLedColor color = new _CorsairLedColor { ledId = (int)ledUpdateRequest.LedId, r = ledUpdateRequest.Color.R, g = ledUpdateRequest.Color.G, b = ledUpdateRequest.Color.B }; Marshal.StructureToPtr(color, addPtr, false); addPtr = new IntPtr(addPtr.ToInt64() + structSize); } _CUESDK.CorsairSetLedsColors(updateRequests.Count, ptr); Marshal.FreeHGlobal(ptr); } OnLedsUpdated(updateRequests); } #endregion #region LedGroup /// /// Attaches the given ledgroup. /// /// The ledgroup to attach. /// true if the ledgroup could be attached; otherwise, false. public bool AttachLedGroup(ILedGroup ledGroup) { lock (LedGroups) { if (ledGroup == null || LedGroups.Contains(ledGroup)) return false; LedGroups.AddLast(ledGroup); return true; } } /// /// Detaches the given ledgroup. /// /// The ledgroup to detached. /// true if the ledgroup could be detached; otherwise, false. public bool DetachLedGroup(ILedGroup ledGroup) { lock (LedGroups) { if (ledGroup == null) return false; LinkedListNode node = LedGroups.Find(ledGroup); if (node == null) return false; LedGroups.Remove(node); return true; } } /// /// Gets a list containing all LEDs of this group. /// /// The list containing all LEDs of this group. public IEnumerable GetLeds() { return Leds; } #endregion #region Effects /// /// Gets a list of all active effects of this target. /// For this device this is always null. /// public IList> Effects => null; /// /// NOT IMPLEMENTED: Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead. /// public void UpdateEffects() { throw new NotSupportedException("Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead."); } /// /// NOT IMPLEMENTED: Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead. /// /// The effect to add. public void AddEffect(IEffect effect) { throw new NotSupportedException("Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead."); } /// /// NOT IMPLEMENTED: Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead. /// /// The effect to remove. public void RemoveEffect(IEffect effect) { throw new NotSupportedException("Effects can't be applied directly to the device. Add it to the Brush or create a ledgroup instead."); } #endregion #region EventCaller /// /// Handles the needed event-calls for an exception. /// /// The exception previously thrown. protected virtual void OnException(Exception ex) { try { Exception?.Invoke(this, new ExceptionEventArgs(ex)); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls before updating. /// protected virtual void OnUpdating() { try { long lastUpdateTicks = _lastUpdate.Ticks; _lastUpdate = DateTime.Now; Updating?.Invoke(this, new UpdatingEventArgs((DateTime.Now.Ticks - lastUpdateTicks) / 10000000f)); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls after an update. /// protected virtual void OnUpdated() { try { Updated?.Invoke(this, new UpdatedEventArgs()); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls before the leds are updated. /// protected virtual void OnLedsUpdating(ICollection updatingLeds) { try { LedsUpdating?.Invoke(this, new LedsUpdatingEventArgs(updatingLeds)); } catch { // Well ... that's not my fault } } /// /// Handles the needed event-calls after the leds are updated. /// protected virtual void OnLedsUpdated(IEnumerable updatedLeds) { try { LedsUpdated?.Invoke(this, new LedsUpdatedEventArgs(updatedLeds)); } catch { // Well ... that's not my fault } } #endregion #region IEnumerable /// /// Returns an enumerator that iterates over all LEDs of the device. /// /// An enumerator for all LEDs of the device. public IEnumerator GetEnumerator() { return LedMapping.Values.GetEnumerator(); } /// /// Returns an enumerator that iterates over all LEDs of the device. /// /// An enumerator for all LEDs of the device. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #endregion } }