diff --git a/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs b/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs new file mode 100644 index 0000000..a3f1f85 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class CoreProps + { + [JsonProperty(PropertyName = "address")] + public string Address { get; set; } + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/Event.cs b/RGB.NET.Devices.SteelSeries/API/Model/Event.cs new file mode 100644 index 0000000..ba3eb60 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/Event.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class Event + { + #region Properties & Fields + + [JsonProperty("game")] + public string Game { get; set; } + + [JsonProperty("event")] + public string Name { get; set; } + + [JsonProperty("data")] + public Dictionary Data { get; } = new Dictionary(); + + #endregion + + #region Constructors + + public Event() + { } + + public Event(Game game, string name) + { + this.Name = name; + Game = game.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/Game.cs b/RGB.NET.Devices.SteelSeries/API/Model/Game.cs new file mode 100644 index 0000000..d93c7e4 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/Game.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class Game + { + #region Properties & Fields + + [JsonProperty("game")] + public string Name { get; set; } + + [JsonProperty("game_display_name")] + public string DisplayName { get; set; } + + #endregion + + #region Constructors + + public Game() + { } + + public Game(string name, string displayName) + { + Name = name; + DisplayName = displayName; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs b/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs new file mode 100644 index 0000000..27d5315 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class GoLispHandler + { + #region Properties & Fields + + [JsonProperty("game")] + public string Game { get; set; } + + [JsonProperty("golisp")] + public string Handler { get; set; } + + #endregion + + #region Constructors + + public GoLispHandler() + { } + + public GoLispHandler(Game game, string handler) + { + this.Handler = handler; + Game = game.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs b/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs new file mode 100644 index 0000000..be7f5ac --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Text; +using Newtonsoft.Json; +using RGB.NET.Devices.SteelSeries.API.Model; + +namespace RGB.NET.Devices.SteelSeries.API +{ + internal static class SteelSeriesSDK + { + #region Constants + + private const string GAME_NAME = "RGBNET"; + private const string GAME_DISPLAYNAME = "RGB.NET"; + private const string EVENT_NAME = "UPDATELEDS"; + private const string HANDLER = @"(handler """ + EVENT_NAME + @""" + (lambda (data) + (let* ((device (value: data)) + (zoneData (frame: data)) + (zones (frame-keys zoneData))) + (do ((zoneDo zones (cdr zoneDo))) + ((nil? zoneDo)) + (let* ((zone (car zoneDo)) + (color (get-slot zoneData zone))) + (on-device device show-on-zone: color zone))))))"; + + private const string CORE_PROPS_WINDOWS = "%PROGRAMDATA%/SteelSeries/SteelSeries Engine 3/coreProps.json"; + private const string CORE_PROPS_OSX = "/Library/Application Support/SteelSeries Engine 3/coreProps.json"; + + #endregion + + #region Properties & Fields + // ReSharper disable InconsistentNaming + + private static readonly HttpClient _client = new HttpClient(); + private static readonly Game _game = new Game(GAME_NAME, GAME_DISPLAYNAME); + private static readonly Event _event = new Event(_game, EVENT_NAME); + private static string _baseUrl; + + internal static bool IsInitialized => !string.IsNullOrWhiteSpace(_baseUrl); + + // ReSharper restore InconsistentNaming + #endregion + + #region Methods + + internal static bool Initialize() + { + try + { + string corePropsPath = GetCorePropsPath(); + if (!string.IsNullOrWhiteSpace(corePropsPath) && File.Exists(corePropsPath)) + { + CoreProps coreProps = JsonConvert.DeserializeObject(File.ReadAllText(corePropsPath)); + _baseUrl = coreProps.Address; + if (!_baseUrl.StartsWith("http://", StringComparison.Ordinal)) + _baseUrl = "http://" + _baseUrl; + + RegisterGame(_game); + RegisterGoLispHandler(new GoLispHandler(_game, HANDLER)); + } + } + catch + { + _baseUrl = null; + } + return IsInitialized; + } + + internal static void UpdateLeds(string device, Dictionary data) + { + _event.Data.Clear(); + _event.Data.Add("value", device); + _event.Data.Add("frame", data); + + TriggerEvent(_event); + } + + internal static void SendHeartbeat() => SendHeartbeat(_game); + + internal static void ResetLeds() => StopGame(_game); + + internal static void Dispose() + { + ResetLeds(); + _client.Dispose(); + } + + private static string TriggerEvent(Event e) => PostJson("/game_event", e); + private static string RegisterGoLispHandler(GoLispHandler handler) => PostJson("/load_golisp_handlers", handler); + private static string RegisterEvent(Event e) => PostJson("/register_game_event", e); + private static string UnregisterEvent(Event e) => PostJson("/remove_game_event", e); + private static string RegisterGame(Game game) => PostJson("/game_metadata", game); + private static string UnregisterGame(Game game) => PostJson("/remove_game", game); + private static string StopGame(Game game) => PostJson("/stop_game", game); + private static string SendHeartbeat(Game game) => PostJson("/game_heartbeat", game); + + private static string PostJson(string urlSuffix, object o) + { + string payload = JsonConvert.SerializeObject(o); + return _client.PostAsync(_baseUrl + urlSuffix, new StringContent(payload, Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync().Result; + } + + private static string GetCorePropsPath() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Environment.ExpandEnvironmentVariables(CORE_PROPS_WINDOWS); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return CORE_PROPS_OSX; + + throw new InvalidOperationException("Unknown operating system."); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs b/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs new file mode 100644 index 0000000..5091145 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs @@ -0,0 +1,20 @@ +namespace RGB.NET.Devices.SteelSeries +{ + internal class APIName : System.Attribute + { + #region Properties & Fields + + public string Name { get; set; } + + #endregion + + #region Constructors + + public APIName(string name) + { + this.Name = name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs b/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs new file mode 100644 index 0000000..3331a50 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace RGB.NET.Devices.SteelSeries +{ + internal static class SteelSeriesEnumExtension + { + #region Properties & Fields + // ReSharper disable InconsistentNaming + + private static readonly Dictionary _deviceTypeNames = new Dictionary(); + private static readonly Dictionary _ledIdNames = new Dictionary(); + + // ReSharper restore InconsistentNaming + #endregion + + #region Methods + + internal static string GetAPIName(this SteelSeriesDeviceType deviceType) + { + if (!_deviceTypeNames.TryGetValue(deviceType, out string apiName)) + _deviceTypeNames.Add(deviceType, apiName = GetAPIName(typeof(SteelSeriesDeviceType), deviceType)); + + return apiName; + } + + internal static string GetAPIName(this SteelSeriesLedId ledId) + { + if (!_ledIdNames.TryGetValue(ledId, out string apiName)) + _ledIdNames.Add(ledId, apiName = GetAPIName(typeof(SteelSeriesLedId), ledId)); + + return apiName; + } + + private static string GetAPIName(Type type, Enum value) + { + MemberInfo[] memInfo = type.GetMember(value.ToString()); + if (memInfo.Length == 0) return null; + return (memInfo.FirstOrDefault()?.GetCustomAttributes(typeof(APIName), false).FirstOrDefault() as APIName)?.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs new file mode 100644 index 0000000..2a426af --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs @@ -0,0 +1,23 @@ +namespace RGB.NET.Devices.SteelSeries +{ + public enum SteelSeriesDeviceType + { + [APIName("rgb-per-key-zones")] + PerKey, + + [APIName("rgb-1-zone")] + OneZone, + + [APIName("rgb-2-zone")] + TwoZone, + + [APIName("rgb-3-zone")] + ThreeZone, + + [APIName("rgb-4-zone")] + FourZone, + + [APIName("rgb-5-zone")] + FiveZone + } +} diff --git a/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs new file mode 100644 index 0000000..87244e0 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs @@ -0,0 +1,243 @@ +namespace RGB.NET.Devices.SteelSeries +{ + public enum SteelSeriesLedId + { + [APIName("one")] + ZoneOne, + [APIName("two")] + ZoneTwo, + [APIName("three")] + ZoneThree, + [APIName("four")] + ZoneFour, + [APIName("five")] + ZoneFive, + + [APIName("logo")] + Logo, + [APIName("a")] + A, + [APIName("b")] + B, + [APIName("c")] + C, + [APIName("d")] + D, + [APIName("e")] + E, + [APIName("f")] + F, + [APIName("g")] + G, + [APIName("h")] + H, + [APIName("i")] + I, + [APIName("j")] + J, + [APIName("k")] + K, + [APIName("l")] + L, + [APIName("m")] + M, + [APIName("n")] + N, + [APIName("o")] + O, + [APIName("p")] + P, + [APIName("q")] + Q, + [APIName("r")] + R, + [APIName("s")] + S, + [APIName("t")] + T, + [APIName("u")] + U, + [APIName("v")] + V, + [APIName("w")] + W, + [APIName("x")] + X, + [APIName("y")] + Y, + [APIName("z")] + Z, + [APIName("keyboard-1")] + Keyboard1, + [APIName("keyboard-2")] + Keyboard2, + [APIName("keyboard-3")] + Keyboard3, + [APIName("keyboard-4")] + Keyboard4, + [APIName("keyboard-5")] + Keyboard5, + [APIName("keyboard-6")] + Keyboard6, + [APIName("keyboard-7")] + Keyboard7, + [APIName("keyboard-8")] + Keyboard8, + [APIName("keyboard-9")] + Keyboard9, + [APIName("keyboard-0")] + Keyboard0, + [APIName("return")] + Return, + [APIName("escape")] + Escape, + [APIName("backspace")] + Backspace, + [APIName("tab")] + Tab, + [APIName("spacebar")] + Spacebar, + [APIName("caps")] + Caps, + [APIName("dash")] + Dash, + [APIName("equal")] + Equal, + [APIName("l-bracket")] + LBracket, + [APIName("r-bracket")] + RBracket, + [APIName("backslash")] + Backslash, + [APIName("pound")] + Pound, + [APIName("semicolon")] + Semicolon, + [APIName("quote")] + Quote, + [APIName("backquote")] + Backqoute, + [APIName("comma")] + Comma, + [APIName("period")] + Period, + [APIName("slash")] + Slash, + [APIName("f1")] + F1, + [APIName("f2")] + F2, + [APIName("f3")] + F3, + [APIName("f4")] + F4, + [APIName("f5")] + F5, + [APIName("f6")] + F6, + [APIName("f7")] + F7, + [APIName("f8")] + F8, + [APIName("f9")] + F9, + [APIName("f10")] + F10, + [APIName("f11")] + F11, + [APIName("f12")] + F12, + [APIName("printscreen")] + PrintScreen, + [APIName("scrolllock")] + ScrollLock, + [APIName("pause")] + Pause, + [APIName("insert")] + Insert, + [APIName("home")] + Home, + [APIName("pageup")] + PageUp, + [APIName("delete")] + Delete, + [APIName("end")] + End, + [APIName("pagedown")] + PageDown, + [APIName("rightarrow")] + RightArrow, + [APIName("leftarrow")] + LeftArrow, + [APIName("downarrow")] + DownArrow, + [APIName("uparrow")] + UpArrow, + [APIName("keypad-num-lock")] + KeypadNumLock, + [APIName("keypad-divide")] + KeypadDivide, + [APIName("keypad-times")] + KeypadTimes, + [APIName("keypad-minus")] + KeypadMinus, + [APIName("keypad-plus")] + KeypadPlus, + [APIName("keypad-enter")] + KeypadEnter, + [APIName("keypad-period")] + KeypadPeriod, + [APIName("keypad-1")] + Keypad1, + [APIName("keypad-2")] + Keypad2, + [APIName("keypad-3")] + Keypad3, + [APIName("keypad-4")] + Keypad4, + [APIName("keypad-5")] + Keypad5, + [APIName("keypad-6")] + Keypad6, + [APIName("keypad-7")] + Keypad7, + [APIName("keypad-8")] + Keypad8, + [APIName("keypad-9")] + Keypad9, + [APIName("keypad-0")] + Keypad0, + [APIName("l-ctrl")] + LCtrl, + [APIName("l-shift")] + LShift, + [APIName("l-alt")] + LAlt, + [APIName("l-win")] + LWin, + [APIName("r-ctrl")] + RCtrl, + [APIName("r-shift")] + RShift, + [APIName("r-alt")] + RAlt, + [APIName("r-win")] + RWin, + [APIName("ss-key")] + SSKey, + [APIName("win-menu")] + WinMenu, + [APIName("m0")] + M0, + [APIName("m1")] + M1, + [APIName("m2")] + M2, + [APIName("m3")] + M3, + [APIName("m4")] + M4, + [APIName("m5")] + M5, + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs b/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs new file mode 100644 index 0000000..57364d9 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// Represents a steelseries RGB-device. + /// + internal interface ISteelSeriesRGBDevice : IRGBDevice + { + void Initialize(UpdateQueue updateQueue, Dictionary ledMapping); + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs new file mode 100644 index 0000000..0c264df --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Devices.SteelSeries.API; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents the update-queue performing updates for steelseries devices. + /// + internal class SteelSeriesDeviceUpdateQueue : UpdateQueue + { + #region Properties & Fields + + private string _deviceType; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The device type used to identify the device. + public SteelSeriesDeviceUpdateQueue(IDeviceUpdateTrigger updateTrigger, string deviceType) + : base(updateTrigger) + { + this._deviceType = deviceType; + } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + SteelSeriesSDK.UpdateLeds(_deviceType, dataSet.ToDictionary(x => ((SteelSeriesLedId)x.Key).GetAPIName(), x => new int[] { x.Value.R, x.Value.G, x.Value.B })); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs new file mode 100644 index 0000000..b30d85b --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// + /// Represents a SteelSeries-device. (keyboard, mouse, headset, mousepad). + /// + public class SteelSeriesRGBDevice : AbstractRGBDevice, ISteelSeriesRGBDevice + { + #region Properties & Fields + + private Dictionary _ledMapping; + + /// + /// + /// Gets information about the . + /// + public override SteelSeriesRGBDeviceInfo DeviceInfo { get; } + + /// + /// Gets or sets the update queue performing updates for this device. + /// + // ReSharper disable once MemberCanBePrivate.Global + protected UpdateQueue UpdateQueue { get; set; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The generic information provided by SteelSeries for the device. + internal SteelSeriesRGBDevice(SteelSeriesRGBDeviceInfo info) + { + this.DeviceInfo = info; + } + + #endregion + + #region Methods + + /// + /// Initializes the device. + /// + void ISteelSeriesRGBDevice.Initialize(UpdateQueue updateQueue, Dictionary ledMapping) + { + _ledMapping = ledMapping; + + int counter = 0; + foreach (KeyValuePair mapping in ledMapping) + InitializeLed(mapping.Key, new Rectangle((counter++) * 10, 0, 10, 10)); + + InitializeLayout(); + + if (Size == Size.Invalid) + { + Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); + Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); + } + + UpdateQueue = updateQueue; + } + + protected override object CreateLedCustomData(LedId ledId) => _ledMapping[ledId]; + + /// + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); + + /// + /// Initializes the and of the device. + /// + protected virtual void InitializeLayout() + { + if (!(DeviceInfo is SteelSeriesRGBDeviceInfo info)) return; + + string layout = info.ImageLayout; + string layoutPath = info.LayoutPath; + ApplyLayoutFromFile(PathHelper.GetAbsolutePath($@"Layouts\SteelSeries\{layoutPath}.xml"), layout, true); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs new file mode 100644 index 0000000..afb2e7d --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs @@ -0,0 +1,72 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents a generic information for a SteelSeries-. + /// + public class SteelSeriesRGBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + public RGBDeviceType DeviceType { get; } + + /// + public string DeviceName { get; } + + /// + public string Manufacturer => "SteelSeries"; + + /// + public string Model { get; } + + /// + public Uri Image { get; set; } + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + /// + public bool SupportsSyncBack => false; + + public SteelSeriesDeviceType SteelSeriesDeviceType { get; } + + /// + /// Gets the layout used to decide which images to load. + /// + internal string ImageLayout { get; } + + /// + /// Gets the path/name of the layout-file. + /// + internal string LayoutPath { get; } + + #endregion + + #region Constructors + + /// + /// Internal constructor of managed . + /// + /// The type of the . + /// The represented device model. + /// The lighting-capabilities of the device. + /// The layout used to decide which images to load. + /// The path/name of the layout-file. + internal SteelSeriesRGBDeviceInfo(RGBDeviceType deviceType, string model, SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath) + { + this.DeviceType = deviceType; + this.Model = model; + this.SteelSeriesDeviceType = steelSeriesDeviceType; + this.ImageLayout = imageLayout; + this.LayoutPath = layoutPath; + + DeviceName = $"{Manufacturer} {Model}"; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs b/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs new file mode 100644 index 0000000..249cf2a --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using HidSharp; +using RGB.NET.Core; +using DeviceDataList = System.Collections.Generic.List<(string model, RGB.NET.Core.RGBDeviceType deviceType, int id, RGB.NET.Devices.SteelSeries.SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath, System.Collections.Generic.Dictionary ledMapping)>; +using LedMapping = System.Collections.Generic.Dictionary; + +namespace RGB.NET.Devices.SteelSeries.HID +{ + internal static class DeviceChecker + { + #region Constants + + private const int VENDOR_ID = 0x1038; + + //TODO DarthAffe 16.02.2019: Add devices + private static readonly DeviceDataList DEVICES = new DeviceDataList + { + ("Rival 500", RGBDeviceType.Mouse, 0x170E, SteelSeriesDeviceType.TwoZone, "default", @"Mice\Rival500", new LedMapping { { LedId.Mouse1, SteelSeriesLedId.ZoneOne}, + { LedId.Mouse2, SteelSeriesLedId.ZoneTwo} }), + }; + + #endregion + + #region Properties & Fields + + public static DeviceDataList ConnectedDevices { get; } = new DeviceDataList(); + + #endregion + + #region Methods + + internal static void LoadDeviceList(RGBDeviceType loadFilter) + { + ConnectedDevices.Clear(); + + HashSet ids = new HashSet(DeviceList.Local.GetHidDevices(VENDOR_ID).Select(x => x.ProductID).Distinct()); + DeviceDataList connectedDevices = DEVICES.Where(d => ids.Contains(d.id) && loadFilter.HasFlag(d.deviceType)).ToList(); + + List connectedDeviceTypes = connectedDevices.Select(d => d.steelSeriesDeviceType).ToList(); + foreach (SteelSeriesDeviceType deviceType in connectedDeviceTypes) + ConnectedDevices.Add(connectedDevices.Where(d => d.steelSeriesDeviceType == deviceType).OrderByDescending(d => d.ledMapping.Count).First()); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj new file mode 100644 index 0000000..bfe4e06 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj @@ -0,0 +1,62 @@ + + + netstandard2.0 + win7-x86;win7-x64 + + Darth Affe + Wyrez + en-US + en-US + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + SteelSeries-Device-Implementations of RGB.NET + SteelSeries-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Wyrez 2017 + Copyright © Wyrez 2017 + http://lib.arge.be/icon.png + https://github.com/DarthAffe/RGB.NET + https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE + Github + https://github.com/DarthAffe/RGB.NET + True + + + + 0.0.1 + 0.0.1 + 0.0.1 + + ..\bin\ + true + True + True + latest + + + + NETCORE;NETSTANDARD;NETSTANDARD2_0 + + + + $(DefineConstants);TRACE;DEBUG + true + full + false + + + + pdbonly + true + $(NoWarn);CS1591;CS1572;CS1573 + $(DefineConstants);RELEASE + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings new file mode 100644 index 0000000..20d4792 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs new file mode 100644 index 0000000..432c51b --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using RGB.NET.Core; +using RGB.NET.Devices.SteelSeries.API; +using RGB.NET.Devices.SteelSeries.HID; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents a device provider responsible for SteelSeries- devices. + /// + public class SteelSeriesDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static SteelSeriesDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static SteelSeriesDeviceProvider Instance => _instance ?? new SteelSeriesDeviceProvider(); + + /// + /// + /// Indicates if the SDK is initialized and ready to use. + /// + public bool IsInitialized { get; private set; } + + /// + /// + /// Gets whether the application has exclusive access to the SDK or not. + /// + public bool HasExclusiveAccess => false; + + /// + public IEnumerable Devices { get; private set; } + + /// + /// The used to trigger the updates for SteelSeries devices. + /// + public DeviceUpdateTrigger UpdateTrigger { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// Thrown if this constructor is called even if there is already an instance of this class. + public SteelSeriesDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(SteelSeriesDeviceProvider)}"); + _instance = this; + + UpdateTrigger = new DeviceUpdateTrigger(); + } + + #endregion + + #region Methods + + /// + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + try + { + IsInitialized = false; + + UpdateTrigger?.Stop(); + + if (!SteelSeriesSDK.IsInitialized) + SteelSeriesSDK.Initialize(); + + IList devices = new List(); + DeviceChecker.LoadDeviceList(loadFilter); + + try + { + foreach ((string model, RGBDeviceType deviceType, int _, SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath, Dictionary ledMapping) in DeviceChecker.ConnectedDevices) + { + ISteelSeriesRGBDevice device = new SteelSeriesRGBDevice(new SteelSeriesRGBDeviceInfo(deviceType, model, steelSeriesDeviceType, imageLayout, layoutPath)); + SteelSeriesDeviceUpdateQueue updateQueue = new SteelSeriesDeviceUpdateQueue(UpdateTrigger, steelSeriesDeviceType.GetAPIName()); + device.Initialize(updateQueue, ledMapping); + devices.Add(device); + } + } + catch { if (throwExceptions) throw; } + + UpdateTrigger?.Start(); + + Devices = new ReadOnlyCollection(devices); + IsInitialized = true; + } + catch + { + IsInitialized = false; + if (throwExceptions) throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { + if (IsInitialized) + try + { + SteelSeriesSDK.ResetLeds(); + } + catch {/* shit happens */} + } + + /// + public void Dispose() + { + try + { + SteelSeriesSDK.Dispose(); + } + catch {/* shit happens */} + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs new file mode 100644 index 0000000..ffe1938 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs @@ -0,0 +1,25 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// Represents a device provider loaded used to dynamically load steelseries devices into an application. + /// + // ReSharper disable once UnusedMember.Global + public class SteelSeriesDeviceProviderLoader : IRGBDeviceProviderLoader + { + #region Properties & Fields + + /// + public bool RequiresInitialization => false; + + #endregion + + #region Methods + + /// + public IRGBDeviceProvider GetDeviceProvider() => SteelSeriesDeviceProvider.Instance; + + #endregion + } +} diff --git a/RGB.NET.sln b/RGB.NET.sln index b6ea9d4..020e145 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Groups", "RGB.NET.G EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.WS281X", "RGB.NET.Devices.WS281X\RGB.NET.Devices.WS281X.csproj", "{0AD382DA-E999-4F74-9658-8D402EE9BF3F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.SteelSeries", "RGB.NET.Devices.SteelSeries\RGB.NET.Devices.SteelSeries.csproj", "{FFDE4387-60F2-47B6-9704-3A57D02B8C64}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +111,10 @@ Global {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.Build.0 = Release|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -129,6 +135,7 @@ Global {8725C448-818C-41F7-B23F-F97E062BF233} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {0AD382DA-E999-4F74-9658-8D402EE9BF3F} = {D13032C6-432E-4F43-8A32-071133C22B16} + {FFDE4387-60F2-47B6-9704-3A57D02B8C64} = {D13032C6-432E-4F43-8A32-071133C22B16} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F222AD4-1F9E-4AAB-9D69-D62372D4C1BA}