From 43d2ed656a87d55f579a4ba8f728e0639d5563a4 Mon Sep 17 00:00:00 2001 From: seerge Date: Sat, 4 Mar 2023 00:11:59 +0100 Subject: [PATCH] Added animatrix control thanks to https://github.com/vddCore/Starlight --- ASUSWmi.cs | 16 +- AnimeMatrix/AnimeMatrixDevice.cs | 230 +++++++++++++++++++ AppConfig.cs | 5 +- Communication/Device.cs | 33 +++ Communication/Packet.cs | 61 +++++ Communication/Platform/UsbProvider.cs | 19 ++ Communication/Platform/WindowsUsbProvider.cs | 79 +++++++ GHelper.csproj | 4 +- Program.cs | 1 + Properties/Resources.Designer.cs | 10 + Properties/Resources.resx | 7 +- Resources/icons8-matrix-desktop-48.png | Bin 0 -> 428 bytes Settings.Designer.cs | 180 +++++++++++---- Settings.cs | 170 +++++++++++++- 14 files changed, 742 insertions(+), 73 deletions(-) create mode 100644 AnimeMatrix/AnimeMatrixDevice.cs create mode 100644 Communication/Device.cs create mode 100644 Communication/Packet.cs create mode 100644 Communication/Platform/UsbProvider.cs create mode 100644 Communication/Platform/WindowsUsbProvider.cs create mode 100644 Resources/icons8-matrix-desktop-48.png diff --git a/ASUSWmi.cs b/ASUSWmi.cs index 2dde755a..baa3e2b9 100644 --- a/ASUSWmi.cs +++ b/ASUSWmi.cs @@ -1,6 +1,6 @@ -using System.Management; +using System.Diagnostics; +using System.Management; using System.Runtime.InteropServices; -using System.Diagnostics; public class ASUSWmi { @@ -30,7 +30,7 @@ public class ASUSWmi public const int PPT_CPUB0 = 0x001200B0; public const int PPT_CPUB1 = 0x001200B1; - public const int PPT_CPUA2 = 0x001200A2; + public const int PPT_CPUA2 = 0x001200A2; public const int PerformanceBalanced = 0; public const int PerformanceTurbo = 1; @@ -174,9 +174,9 @@ public class ASUSWmi Debug.WriteLine(BitConverter.ToString(curve)); - if (device == 1) + if (device == 1) DeviceSet(DevsGPUFanCurve, curve); - else + else DeviceSet(DevsCPUFanCurve, curve); } @@ -187,14 +187,14 @@ public class ASUSWmi // because it's asus, and modes are swapped here switch (mode) { - case 1:fan_mode = 2; break; + case 1: fan_mode = 2; break; case 2: fan_mode = 1; break; default: fan_mode = 0; break; } - if (device == 1) + if (device == 1) return DeviceGetBuffer(DevsGPUFanCurve, fan_mode); - else + else return DeviceGetBuffer(DevsCPUFanCurve, fan_mode); } diff --git a/AnimeMatrix/AnimeMatrixDevice.cs b/AnimeMatrix/AnimeMatrixDevice.cs new file mode 100644 index 00000000..9c94f3c8 --- /dev/null +++ b/AnimeMatrix/AnimeMatrixDevice.cs @@ -0,0 +1,230 @@ +// Source thanks to https://github.com/vddCore/Starlight :) + +using System.Text; +using Starlight.Communication; + +namespace Starlight.AnimeMatrix +{ + + public class BuiltInAnimation + { + public enum Startup + { + GlitchConstruction, + StaticEmergence + } + + public enum Shutdown + { + GlitchOut, + SeeYa + } + + public enum Sleeping + { + BannerSwipe, + Starfield + } + + public enum Running + { + BinaryBannerScroll, + RogLogoGlitch + } + + public byte AsByte { get; } + + public BuiltInAnimation( + Running running, + Sleeping sleeping, + Shutdown shutdown, + Startup startup + ) + { + AsByte |= (byte)(((int)running & 0x01) << 0); + AsByte |= (byte)(((int)sleeping & 0x01) << 1); + AsByte |= (byte)(((int)shutdown & 0x01) << 2); + AsByte |= (byte)(((int)startup & 0x01) << 3); + } + } + + internal class AnimeMatrixPacket : Packet + { + public AnimeMatrixPacket(byte[] command) + : base(0x5E, 640, command) + { + } + } + + public enum BrightnessMode : byte + { + Off = 0, + Dim = 1, + Medium = 2, + Full = 3 + } + + + public class AnimeMatrixDevice : Device + { + private const int UpdatePageLength = 0x0278; + + public int LedCount => 1450; + public int Rows => 61; + + private readonly byte[] _displayBuffer = new byte[UpdatePageLength * 3]; + + public AnimeMatrixDevice() + : base(0x0B05, 0x193B, 640) + { + } + + public void SendRaw(params byte[] data) + { + Set(Packet(data)); + } + + + public int EmptyColumns(int row) + { + return (int)Math.Ceiling(Math.Max(0, row - 11) / 2.0); + } + public int Columns(int row) + { + EnsureRowInRange(row); + return 34 - EmptyColumns(row); + } + + public int RowToLinearAddress(int row) + { + EnsureRowInRange(row); + + var ret = 0; + + if (row > 0) + { + for (var i = 0; i < row; i++) + ret += Columns(i); + } + + return ret; + } + + public void WakeUp() + { + Set(Packet(Encoding.ASCII.GetBytes("ASUS Tech.Inc."))); + } + + public void SetLedLinear(int address, byte value) + { + EnsureAddressableLed(address); + _displayBuffer[address] = value; + } + + public void SetLedLinearImmediate(int address, byte value) + { + EnsureAddressableLed(address); + _displayBuffer[address] = value; + + Set(Packet(0xC0, 0x02) + .AppendData(BitConverter.GetBytes((ushort)(address + 1))) + .AppendData(BitConverter.GetBytes((ushort)0x0001)) + .AppendData(value) + ); + + Set(Packet(0xC0, 0x03)); + } + + public void SetLedPlanar(int x, int y, byte value) + { + EnsureRowInRange(y); + var ledsInRow = Columns(y); + var start = RowToLinearAddress(y) - EmptyColumns(y); + + if (x > EmptyColumns(y)) + SetLedLinear(start + x, value); + } + + public void Clear(bool present = false) + { + for (var i = 0; i < _displayBuffer.Length; i++) + _displayBuffer[i] = 0; + + if (present) + Present(); + } + + public void Present() + { + Set(Packet(0xC0, 0x02) + .AppendData(BitConverter.GetBytes((ushort)(UpdatePageLength * 0 + 1))) + .AppendData(BitConverter.GetBytes((ushort)UpdatePageLength)) + .AppendData(_displayBuffer[(UpdatePageLength * 0)..(UpdatePageLength * 1)]) + ); + + Set(Packet(0xC0, 0x02) + .AppendData(BitConverter.GetBytes((ushort)(UpdatePageLength * 1 + 1))) + .AppendData(BitConverter.GetBytes((ushort)UpdatePageLength)) + .AppendData(_displayBuffer[(UpdatePageLength * 1)..(UpdatePageLength * 2)]) + ); + + Set(Packet(0xC0, 0x02) + .AppendData(BitConverter.GetBytes((ushort)(UpdatePageLength * 2 + 1))) + .AppendData(BitConverter.GetBytes((ushort)(LedCount - UpdatePageLength * 2))) + .AppendData( + _displayBuffer[(UpdatePageLength * 2)..(UpdatePageLength * 2 + (LedCount - UpdatePageLength * 2))]) + ); + + Set(Packet(0xC0, 0x03)); + } + + public void SetDisplayState(bool enable) + { + if (enable) + { + Set(Packet(0xC3, 0x01) + .AppendData(0x00)); + } + else + { + Set(Packet(0xC3, 0x01) + .AppendData(0x80)); + } + } + + public void SetBrightness(BrightnessMode mode) + { + Set(Packet(0xC0, 0x04) + .AppendData((byte)mode) + ); + } + + public void SetBuiltInAnimation(bool enable) + { + var enabled = enable ? (byte)0x00 : (byte)0x80; + Set(Packet(0xC4, 0x01, enabled)); + } + + public void SetBuiltInAnimation(bool enable, BuiltInAnimation animation) + { + SetBuiltInAnimation(enable); + Set(Packet(0xC5, animation.AsByte)); + } + + private void EnsureRowInRange(int row) + { + if (row < 0 || row >= Rows) + { + throw new IndexOutOfRangeException($"Y-coordinate should fall in range of [0, {Rows - 1}]."); + } + } + + private void EnsureAddressableLed(int address) + { + if (address < 0 || address >= LedCount) + { + throw new IndexOutOfRangeException($"Linear LED address must be in range of [0, {LedCount - 1}]."); + } + } + } +} \ No newline at end of file diff --git a/AppConfig.cs b/AppConfig.cs index db27733c..4a717098 100644 --- a/AppConfig.cs +++ b/AppConfig.cs @@ -1,9 +1,8 @@ using System.Text.Json; - public class AppConfig { - string appPath; + public string appPath; string configFile; public Dictionary config = new Dictionary(); @@ -82,7 +81,7 @@ public class AppConfig else name = "cpu"; - return paramName+"_" + name + "_" + mode; + return paramName + "_" + name + "_" + mode; } public byte[] getFanConfig(int device) diff --git a/Communication/Device.cs b/Communication/Device.cs new file mode 100644 index 00000000..2603d222 --- /dev/null +++ b/Communication/Device.cs @@ -0,0 +1,33 @@ +// Source thanks to https://github.com/vddCore/Starlight :) + + +using Starlight.Communication.Platform; + +namespace Starlight.Communication +{ + public abstract class Device : IDisposable + { + private static UsbProvider _usbProvider; + + protected Device(ushort vendorId, ushort productId, int maxFeatureReportLength) + { + _usbProvider = new WindowsUsbProvider(vendorId, productId, maxFeatureReportLength); + } + + protected T Packet(params byte[] command) where T : Packet + { + return (T)Activator.CreateInstance(typeof(T), command)!; + } + + public void Set(Packet packet) + => _usbProvider?.Set(packet.Data); + + public byte[] Get(Packet packet) + => _usbProvider?.Get(packet.Data); + + public void Dispose() + { + _usbProvider?.Dispose(); + } + } +} \ No newline at end of file diff --git a/Communication/Packet.cs b/Communication/Packet.cs new file mode 100644 index 00000000..0aa0bc87 --- /dev/null +++ b/Communication/Packet.cs @@ -0,0 +1,61 @@ +// Source thanks to https://github.com/vddCore/Starlight :) + +using System.ComponentModel; +using HidSharp; + +namespace Starlight.Communication +{ + public abstract class Packet + { + private int _currentDataIndex = 1; + + public byte[] Data { get; } + + internal Packet(byte reportId, int packetLength, params byte[] data) + { + if (packetLength < 1) + { + throw new ArgumentOutOfRangeException( + nameof(packetLength), + "Packet length must be at least 1." + ); + } + + Data = new byte[packetLength]; + Data[0] = reportId; + + if (data.Length > 0) + { + if (_currentDataIndex >= Data.Length) + { + throw new ArgumentOutOfRangeException( + nameof(data), + "Your packet length does not allow for initial data to be appended." + ); + } + + AppendData(data); + } + } + + public Packet AppendData(params byte[] data) + => AppendData(out _, data); + + public Packet AppendData(out int bytesWritten, params byte[] data) + { + bytesWritten = 0; + + for (var i = 0; + i < data.Length && _currentDataIndex < Data.Length - 1; + i++, bytesWritten++, _currentDataIndex++) + { + if (_currentDataIndex > Data.Length - 1) + break; + + Data[_currentDataIndex] = data[i]; + } + + return this; + } + } +} \ No newline at end of file diff --git a/Communication/Platform/UsbProvider.cs b/Communication/Platform/UsbProvider.cs new file mode 100644 index 00000000..c3394cf3 --- /dev/null +++ b/Communication/Platform/UsbProvider.cs @@ -0,0 +1,19 @@ +namespace Starlight.Communication.Platform +{ + internal abstract class UsbProvider : IDisposable + { + protected ushort VendorID { get; } + protected ushort ProductID { get; } + + protected UsbProvider(ushort vendorId, ushort productId) + { + VendorID = vendorId; + ProductID = productId; + } + + public abstract void Set(byte[] data); + public abstract byte[] Get(byte[] data); + + public abstract void Dispose(); + } +} \ No newline at end of file diff --git a/Communication/Platform/WindowsUsbProvider.cs b/Communication/Platform/WindowsUsbProvider.cs new file mode 100644 index 00000000..8933ca49 --- /dev/null +++ b/Communication/Platform/WindowsUsbProvider.cs @@ -0,0 +1,79 @@ +using System.ComponentModel; +using HidSharp; + +namespace Starlight.Communication.Platform +{ + internal class WindowsUsbProvider : UsbProvider + { + protected HidDevice HidDevice { get; } + protected HidStream HidStream { get; } + + public WindowsUsbProvider(ushort vendorId, ushort productId, int maxFeatureReportLength) + : base(vendorId, productId) + { + try + { + HidDevice = DeviceList.Local + .GetHidDevices(vendorId, productId) + .First(x => x.GetMaxFeatureReportLength() == maxFeatureReportLength); + } + catch + { + throw new IOException("AniMe Matrix control device was not found on your machine."); + } + + var config = new OpenConfiguration(); + config.SetOption(OpenOption.Interruptible, true); + config.SetOption(OpenOption.Exclusive, false); + config.SetOption(OpenOption.Priority, 10); + + HidStream = HidDevice.Open(config); + } + + public override void Set(byte[] data) + { + WrapException(() => + { + HidStream.SetFeature(data); + HidStream.Flush(); + }); + } + + public override byte[] Get(byte[] data) + { + var outData = new byte[data.Length]; + Array.Copy(data, outData, data.Length); + + WrapException(() => + { + HidStream.GetFeature(outData); + HidStream.Flush(); + }); + + return data; + } + + public override void Dispose() + { + HidStream.Dispose(); + } + + private void WrapException(Action action) + { + try + { + action(); + } + catch (IOException e) + { + if (e.InnerException is Win32Exception w32e) + { + if (w32e.NativeErrorCode != 0) + { + throw; + } + } + } + } + } +} \ No newline at end of file diff --git a/GHelper.csproj b/GHelper.csproj index 82624507..4e26af8b 100644 --- a/GHelper.csproj +++ b/GHelper.csproj @@ -15,7 +15,8 @@ GHelper x64 False - 0.14.1 + 0.15 + True @@ -37,6 +38,7 @@ + diff --git a/Program.cs b/Program.cs index 7cb37791..8e7e4447 100644 --- a/Program.cs +++ b/Program.cs @@ -1,6 +1,7 @@ using Microsoft.Win32; using System.Diagnostics; using System.Management; +using Starlight.AnimeMatrix; public class HardwareMonitor { diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index 43839093..af44bc02 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -120,6 +120,16 @@ namespace GHelper.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_matrix_desktop_48 { + get { + object obj = ResourceManager.GetObject("icons8-matrix-desktop-48", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/Properties/Resources.resx b/Properties/Resources.resx index a22315f2..f8966b05 100644 --- a/Properties/Resources.resx +++ b/Properties/Resources.resx @@ -133,6 +133,9 @@ ..\Resources\icons8-speed-48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\everything-is-fine-itsfine.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\icons8-speed-96.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -148,7 +151,7 @@ ..\Resources\icons8-laptop-48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\everything-is-fine-itsfine.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-matrix-desktop-48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/Resources/icons8-matrix-desktop-48.png b/Resources/icons8-matrix-desktop-48.png new file mode 100644 index 0000000000000000000000000000000000000000..23cb9f47eae779cf1f1277fc107e1d5fd29ca73c GIT binary patch literal 428 zcmV;d0aN~oP)L{E<|*p zeMA?+zK^8kCXQK%e=-Y6D*>fuA@L@_It%sQrctYP-lkD&Acj`cs5LY0{*iS?LhicI z@P}_J&id{2myZ6=v5q*jSFM1h(-WF|3|Ki5u>~HPfkisj85 + { + OpenFileDialog of = new OpenFileDialog(); + of.Filter = "Image Files (*.bmp;*.jpg;*.jpeg,*.png)|*.BMP;*.JPG;*.JPEG;*.PNG"; + if (of.ShowDialog() == DialogResult.OK) + { + Program.config.setConfig("matrix_picture", of.FileName); + SetMatrixPicture(of.FileName); + BeginInvoke(delegate + { + comboMatrixRunning.SelectedIndex = 2; + }); + } + return; + })); + + t.SetApartmentState(ApartmentState.STA); + t.Start(); + t.Join(); + } + + private void ComboMatrixRunning_SelectedValueChanged(object? sender, EventArgs e) + { + SetAnimeMatrix(); + } + + + private void ComboMatrix_SelectedValueChanged(object? sender, EventArgs e) + { + SetAnimeMatrix(); + } + + private void SetAnimeMatrix() + { + + int brightness = comboMatrix.SelectedIndex; + int running = comboMatrixRunning.SelectedIndex; + + var mat = new AnimeMatrixDevice(); + + BuiltInAnimation animation = new BuiltInAnimation( + (BuiltInAnimation.Running)running, + BuiltInAnimation.Sleeping.Starfield, + BuiltInAnimation.Shutdown.SeeYa, + BuiltInAnimation.Startup.StaticEmergence + ); + + + if (brightness == 0) + { + mat.SetDisplayState(false); + } + else + { + mat.SetDisplayState(true); + mat.SetBrightness((BrightnessMode)brightness); + + if (running == 2) + { + string fileName = Program.config.getConfigString("matrix_picture"); + SetMatrixPicture(fileName); + } + else + { + mat.SetBuiltInAnimation(true, animation); + } + } + + mat.Dispose(); + + Program.config.setConfig("matrix_brightness", comboMatrix.SelectedIndex); + Program.config.setConfig("matrix_running", comboMatrixRunning.SelectedIndex); + } + + + private void LabelCPUFan_Click(object? sender, EventArgs e) { Program.config.setConfig("fan_rpm", (Program.config.getConfig("fan_rpm") == 1) ? 0 : 1); @@ -188,10 +334,18 @@ namespace GHelper SetAuraMode(mode, false); Aura.Mode = mode; - - } + public void InitMatrix() + { + int brightness = Program.config.getConfig("matrix_brightness"); + int running = Program.config.getConfig("matrix_running"); + + comboMatrix.SelectedIndex = (brightness != -1) ? brightness : 0; + comboMatrixRunning.SelectedIndex = (running != -1) ? running : 0; + } + + public void SetAuraColor(Color? color1 = null, Color? color2 = null, bool apply = true) { @@ -498,7 +652,7 @@ namespace GHelper } int oldMode = Program.config.getConfig("performance_mode"); - Program.config.setConfig("performance_"+(int)SystemInformation.PowerStatus.PowerLineStatus, PerformanceMode); + Program.config.setConfig("performance_" + (int)SystemInformation.PowerStatus.PowerLineStatus, PerformanceMode); Program.config.setConfig("performance_mode", PerformanceMode); Program.wmi.DeviceSet(ASUSWmi.PerformanceMode, PerformanceMode); @@ -537,10 +691,10 @@ namespace GHelper public void AutoPerformance(PowerLineStatus Plugged = PowerLineStatus.Online) { - int mode = Program.config.getConfig("performance_"+(int)Plugged); + int mode = Program.config.getConfig("performance_" + (int)Plugged); if (mode != -1) SetPerformanceMode(mode, true); - else + else SetPerformanceMode(Program.config.getConfig("performance_mode")); }