// ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; namespace RGB.NET.Core { /// /// /// /// Represents a RGB-surface containing multiple devices. /// public partial class RGBSurface : AbstractBindable, IDisposable { #region Properties & Fields /// /// Gets the singelot-instance of the class. /// public static RGBSurface Instance { get; } = new RGBSurface(); private Stopwatch _deltaTimeCounter; private IList _deviceProvider = new List(); private IList _devices = new List(); private IList _updateTriggers = new List(); // ReSharper disable InconsistentNaming private readonly LinkedList _ledGroups = new LinkedList(); // ReSharper restore InconsistentNaming /// /// Gets a readonly list containing all loaded . /// public IEnumerable Devices { get { lock (_devices) return new ReadOnlyCollection(_devices); } } /// /// Gets a readonly list containing all registered . /// public IEnumerable UpdateTriggers => new ReadOnlyCollection(_updateTriggers); /// /// Gets a copy of the representing this . /// public Rectangle SurfaceRectangle { get; private set; } /// /// Gets a list of all on this . /// public IEnumerable Leds { get { lock (_devices) return _devices.SelectMany(x => x); } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// private RGBSurface() { _deltaTimeCounter = Stopwatch.StartNew(); } #endregion #region Methods /// /// Perform a full update for all devices. Updates only dirty by default, or all , if flushLeds is set to true. /// /// Specifies whether all , (including clean ones) should be updated. public void Update(bool flushLeds = false) => Update(null, new CustomUpdateData(("flushLeds", flushLeds))); private void Update(object updateTrigger, CustomUpdateData customData) => Update(updateTrigger as IUpdateTrigger, customData); private void Update(IUpdateTrigger updateTrigger, CustomUpdateData customData) { if (customData == null) customData = new CustomUpdateData(); try { bool flushLeds = customData["flushLeds"] as bool? ?? false; bool syncBack = customData["syncBack"] as bool? ?? true; bool render = customData["render"] as bool? ?? true; bool updateDevices = customData["updateDevices"] as bool? ?? true; lock (_updateTriggers) lock (_devices) { OnUpdating(updateTrigger, customData); if (syncBack) foreach (IRGBDevice device in _devices) if (device.UpdateMode.HasFlag(DeviceUpdateMode.SyncBack) && device.DeviceInfo.SupportsSyncBack) try { device.SyncBack(); } catch (Exception ex) { OnException(ex); } if (render) lock (_ledGroups) { // Render brushes foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex)) try { Render(ledGroup); } catch (Exception ex) { OnException(ex); } } if (updateDevices) foreach (IRGBDevice device in _devices) if (!device.UpdateMode.HasFlag(DeviceUpdateMode.NoUpdate)) try { device.Update(flushLeds); } catch (Exception ex) { OnException(ex); } OnUpdated(); } } catch (Exception ex) { OnException(ex); } } /// public void Dispose() { lock (_devices) foreach (IRGBDevice device in _devices) try { device.Dispose(); } catch { /* We do what we can */} lock (_deviceProvider) foreach (IRGBDeviceProvider deviceProvider in _deviceProvider) try { deviceProvider.Dispose(); } catch { /* We do what we can */} foreach (IUpdateTrigger updateTrigger in _updateTriggers) try { updateTrigger.Dispose(); } catch { /* We do what we can */} _ledGroups.Clear(); _devices = null; _deviceProvider = null; } /// /// Renders a ledgroup. /// /// The led group to render. private void Render(ILedGroup ledGroup) { IList leds = ledGroup.GetLeds().ToList(); IBrush brush = ledGroup.Brush; if ((brush == null) || !brush.IsEnabled) return; switch (brush.BrushCalculationMode) { case BrushCalculationMode.Relative: Rectangle brushRectangle = new Rectangle(leds.Select(led => led.AbsoluteLedRectangle)); Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y); brushRectangle = brushRectangle.SetLocation(new Point(0, 0)); brush.PerformRender(brushRectangle, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteLedRectangle.Translate(offset)))); break; case BrushCalculationMode.Absolute: brush.PerformRender(SurfaceRectangle, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteLedRectangle))); break; default: throw new ArgumentException(); } //brush.UpdateEffects(); brush.PerformFinalize(); foreach (KeyValuePair renders in brush.RenderedTargets) renders.Key.Led.Color = renders.Value; } /// /// Attaches the given . /// /// The to attach. /// true if the could be attached; otherwise, false. public bool AttachLedGroup(ILedGroup ledGroup) { if (ledGroup == null) return false; lock (_ledGroups) { if (_ledGroups.Contains(ledGroup)) return false; _ledGroups.AddLast(ledGroup); ledGroup.OnAttach(); return true; } } /// /// Detaches the given . /// /// The to detached. /// true if the could be detached; otherwise, false. public bool DetachLedGroup(ILedGroup ledGroup) { if (ledGroup == null) return false; lock (_ledGroups) { LinkedListNode node = _ledGroups.Find(ledGroup); if (node == null) return false; _ledGroups.Remove(node); node.Value.OnDetach(); return true; } } private void UpdateSurfaceRectangle() { lock (_devices) { Rectangle devicesRectangle = new Rectangle(_devices.Select(d => d.DeviceRectangle)); SurfaceRectangle = SurfaceRectangle.SetSize(new Size(devicesRectangle.Location.X + devicesRectangle.Size.Width, devicesRectangle.Location.Y + devicesRectangle.Size.Height)); } } /// /// Gets all devices of a specific type. /// /// The type of devices to get. /// A list of devices with the specified type. public IList GetDevices() where T : class { lock (_devices) return new ReadOnlyCollection(_devices.Select(x => x as T).Where(x => x != null).ToList()); } /// /// Gets all devices of the specified . /// /// The of the devices to get. /// a list of devices matching the specified . public IList GetDevices(RGBDeviceType deviceType) { lock (_devices) return new ReadOnlyCollection(_devices.Where(d => deviceType.HasFlag(d.DeviceInfo.DeviceType)).ToList()); } /// /// Registers the provided . /// /// The to register. public void RegisterUpdateTrigger(IUpdateTrigger updateTrigger) { if (!_updateTriggers.Contains(updateTrigger)) { _updateTriggers.Add(updateTrigger); updateTrigger.Update += Update; } } /// /// Unregisters the provided . /// /// The to unregister. public void UnregisterUpdateTrigger(IUpdateTrigger updateTrigger) { if (_updateTriggers.Remove(updateTrigger)) updateTrigger.Update -= Update; } #endregion } }