LenovoLegionToolkit
311 строк · 8.5 Кб
1using System.Runtime.InteropServices;2using LenovoLegionToolkit.Lib.Extensions;3using LenovoLegionToolkit.Lib.System;4using Windows.Win32;5
6// ReSharper disable all
7
8Console.WriteLine(@"How to use:91. Make sure Vantage and LLT is closed.
102. Set the keyboard brightness to maximum.
11
12When ready, press any key to continue...
13");14Console.ReadKey();15
16var device = Devices.GetSpectrumRGBKeyboard();17
18Console.WriteLine("Finding Spectrum keyboard...");19
20if (device is null)21{
22Console.WriteLine("Spectrum not supported");23Console.ReadKey();24return;25}
26
27
28Console.WriteLine("Spectrum keyboard found");29Console.WriteLine();30
31Console.WriteLine("Reading response for 0xD1...");32SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownD1, 0, 0));33GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resD1);34Print(resD1.Bytes);35Console.WriteLine();36
37Console.WriteLine("Reading response for 0xC6...");38SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC6, 0, 0));39GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC6);40Print(resC6.Bytes);41Console.WriteLine();42
43Console.WriteLine("Reading response for 0x04...");44SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.Unknown04, 0, 0));45GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE res04);46Print(res04.Bytes);47Console.WriteLine();48
49Console.WriteLine("Reading response for 0xC7...");50SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC7, 0, 0));51GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC7);52Print(resC7.Bytes);53Console.WriteLine();54
55Console.WriteLine("Reading response for 0xC4 7...");56SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC4, 7, 0));57GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC47);58Print(resC47.Bytes);59Console.WriteLine();60
61Console.WriteLine("Reading response for 0xC4 8...");62SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC4, 8, 0));63GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC48);64Print(resC48.Bytes);65Console.WriteLine();66
67for (var i = 0; i < 10; i++)68{
69Console.WriteLine($"Reading response for 0xC5 7 {i}...");70SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC5, 7, (byte)i));71GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC57);72Print(resC57.Bytes);73Console.WriteLine();74}
75
76Console.WriteLine($"Reading response for 0xC5 8...");77SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC5, 8, 0));78GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC58);79Print(resC58.Bytes);80Console.WriteLine();81
82Console.WriteLine(resD1.Bytes[4] == 0 ? "Keyboard is RGB." : "Not compatible.");83
84var spectrumLayout = (resC47.Bytes[6], resC47.Bytes[5]) switch85{
86(22, 9) => "Full",87(20, 8) => "KeyboardAndFront",88(20, 7) => "KeyboardOnly",89_ => "Unknown"90};91Console.WriteLine($"Layout is {spectrumLayout}.");92Console.WriteLine("Reading config complete.");93Console.WriteLine();94Console.ReadKey();95
96Console.WriteLine(@"*** TEST 1***97
98This program will now try to control brightness of the keyboard. Keyboard should turn off and then gradually increase brightness and turn off at the end.
99
100When ready, press any key to continue...
101");102
103var brightnessLevels = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };104
105foreach (var level in brightnessLevels)106{
107Console.WriteLine($"Setting brightness level to {level}...");108SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.Brightness, level, 0));109await Task.Delay(250);110}
111
112Console.WriteLine("Test complete, press any key to continue...");113Console.WriteLine();114Console.ReadKey();115
116Console.WriteLine(@"***TEST 2***117
118How to find a keycode for a specific key:
1191. Open Vantage
1202. Select the key that you want to find keycode for
1213. Set the key to plain white (Hex: #FFFFFF, RGB: 255,255,255)
1224. Make sure all other keys don't have any effect set (are off)
1235. Set the keyboard brightness to maximum
124
125When ready, press any key to continue...
126");127Console.ReadKey();128
129Console.WriteLine("Reading white key keycodes...");130Console.WriteLine();131
132const int Iterations = 5;133
134for (var i = 0; i < Iterations; i++)135{
136GetFeature(device, out LENOVO_SPECTRUM_STATE state);137
138var keys = state.Data139.Where(kv => HasColor(kv.Color))140.Select(kv => kv.Key)141.Select(k => "0x" + k.ToString("X4"))142.Split(8);143
144await Task.Delay(1000);145
146if (keys.SelectMany(k => k).IsEmpty())147{148Console.WriteLine($"[{i + 1}/{Iterations}] No keys found");149continue;150}151
152Console.WriteLine($"[{i + 1}/{Iterations}] Keys with color found:");153
154foreach (var line in keys)155Console.WriteLine(" " + string.Join(", ", line));156
157}
158
159Console.WriteLine();160Console.WriteLine("Test complete.");161Console.WriteLine("Press any key to exit...");162Console.ReadLine();163
164#region Methods165
166void Print(byte[] bytes)167{
168var length = bytes[2] + 4;169foreach (var i in bytes.Take(length).Split(16))170Console.WriteLine(string.Join(" ", i.Select(i => $"{i:X2}")));171}
172
173bool HasColor(LENOVO_SPECTRUM_COLOR rgbColor) => rgbColor.Red == 255 && rgbColor.Green == 255 && rgbColor.Blue == 255;174
175unsafe void SetFeature<T>(SafeHandle handle, T str) where T : notnull176{
177var ptr = IntPtr.Zero;178try179{180int size;181if (str is byte[] bytes)182{183size = bytes.Length;184ptr = Marshal.AllocHGlobal(size);185Marshal.Copy(bytes, 0, ptr, size);186}187else188{189size = Marshal.SizeOf<T>();190ptr = Marshal.AllocHGlobal(size);191Marshal.StructureToPtr(str, ptr, false);192}193
194var result = PInvoke.HidD_SetFeature(handle, ptr.ToPointer(), (uint)size);195if (!result)196PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name);197}198finally199{200Marshal.FreeHGlobal(ptr);201}202}
203
204unsafe void GetFeature<T>(SafeHandle handle, out T str) where T : struct205{
206var ptr = IntPtr.Zero;207try208{209var size = Marshal.SizeOf<T>();210ptr = Marshal.AllocHGlobal(size);211Marshal.Copy(new byte[] { 7 }, 0, ptr, 1);212
213var result = PInvoke.HidD_GetFeature(handle, ptr.ToPointer(), (uint)size);214if (!result)215PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name);216
217str = Marshal.PtrToStructure<T>(ptr);218}219finally220{221Marshal.FreeHGlobal(ptr);222}223}
224
225#endregion226
227#region Structs228
229[StructLayout(LayoutKind.Sequential)]230internal struct LENOVO_SPECTRUM_HEADER231{
232public byte Head = 7;233public LENOVO_SPECTRUM_OPERATION_TYPE Type;234public byte Size;235public byte Tail = 3;236
237public LENOVO_SPECTRUM_HEADER(LENOVO_SPECTRUM_OPERATION_TYPE type, int size)238{239Type = type;240Size = (byte)(size % 256);241}242}
243
244[StructLayout(LayoutKind.Sequential, Size = 960)]245internal struct LENOVO_SPECTRUM_GENERIC_REQUEST246{
247public LENOVO_SPECTRUM_HEADER Header;248public byte Value;249public byte Value2;250
251public LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE operation, byte value, byte value2)252{253Header = new LENOVO_SPECTRUM_HEADER(operation, 0xC0);254Value = value;255Value2 = value2;256}257}
258
259[StructLayout(LayoutKind.Sequential, Size = 960)]260internal struct LENOVO_SPECTRUM_GENERIC_RESPONSE261{
262[MarshalAs(UnmanagedType.ByValArray, SizeConst = 960)]263public byte[] Bytes;264}
265
266internal enum LENOVO_SPECTRUM_OPERATION_TYPE : byte267{
268ProfileSet1 = 0xC8,269GetProfile = 0xCA,270EffectChange = 0xCB,271ProfileSet2 = 0xCC,272GetBrightness = 0xCD,273Brightness = 0xCE,274AuroraSendBitmap = 0xA1,275State = 0x03,276Unknown04 = 0x04,277UnknownC4 = 0xC4,278UnknownC5 = 0xC5,279UnknownC6 = 0xC6,280UnknownC7 = 0xC7,281UnknownD1 = 0xD1,282}
283
284[StructLayout(LayoutKind.Sequential)]285internal struct LENOVO_SPECTRUM_COLOR286{
287public byte Red;288public byte Green;289public byte Blue;290}
291
292[StructLayout(LayoutKind.Sequential, Pack = 1)]293internal struct LENOVO_SPECTRUM_KEY_STATE294{
295public ushort Key;296public LENOVO_SPECTRUM_COLOR Color;297}
298
299[StructLayout(LayoutKind.Sequential, Size = 960)]300internal struct LENOVO_SPECTRUM_STATE301{
302public byte ReportId;303public LENOVO_SPECTRUM_OPERATION_TYPE Type;304public byte Unknown2;305public byte Unknown3;306[MarshalAs(UnmanagedType.ByValArray, SizeConst = 191)]307public LENOVO_SPECTRUM_KEY_STATE[] Data;308public byte Unknown4;309}
310
311#endregion312