private void FormSpectralTest_Load(object sender, EventArgs e) { comboDeviceType.DataSource = Enum.GetValues(typeof(DeviceType)); comboLedName.DataSource = Enum.GetValues(typeof(LedName)); Led.Initialize(); string status = ""; status += $"Logitech is enabled: {Led.LogitechIsEnabled().ToString()}\n"; status += $"Corsair is enabled: {Led.CorsairIsEnabled().ToString()}\n"; // status += $"Razer is enabled: {Led.RazerIsEnabled().ToString()}\n"; labelStatus.Text = status; Led.SetColor(Color.Black); Led.SetColorForLeds(new List <LedName> { LedName.W, LedName.A, LedName.S, LedName.D }, Color.OrangeRed); Led.SetColorForLeds(new List <LedName> { LedName.LeftShift, LedName.E, LedName.Q }, Color.Green); Led.SetColorForLeds(new List <LedName> { LedName.One, LedName.Two, LedName.Three, LedName.Four }, Color.Blue); Led.SetColorForLed(LedName.R, Color.Red); Led.SetColorForLed(LedName.F, Color.Purple); Led.SetColorForLed(LedName.Spacebar, Color.White); Led.SetColorForLed(LedName.MouseZone1, Color.ForestGreen); Led.SetColorForLed(LedName.HeadsetLeftZone, Color.SeaGreen); Led.SetColorForLed(LedName.MousepadZone1, Color.PaleVioletRed); Led.SetColorForLed(LedName.SpeakerLeft, Color.MediumOrchid); }
void Start() { if (Led.Initialize()) { Led.SetColor(Color.black); Led.SetColorForLeds( new[] { LedName.W, LedName.A, LedName.S, LedName.D }, Color.magenta ); } }
public static void LedUpdateThread_Task() { Led.Initialize(); Led.SetColor(0, 0, 0); int delay = 100; // Target delay between updates in milliseconds // Define a bitmap that will be used to hold the screen data while we copy it to an easy-access array of bytes Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); // Instantiation of the pixel array requires knowing the scan width of the screen /*It is beneficial to do this once rather than in the loop because creating large * arrays is computationally expensive and they aren't disposed immediately*/ // I was able to reduce average memory usage from 250MB to 63MB by doing this. BitmapData init_screen = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); int size = Math.Abs(init_screen.Stride) * bitmap.Height; // Stride * Height IS EQUAL TO Number of pixels * bytes per pixel bitmap.UnlockBits(init_screen); // We don't need this anymore byte[] pixels = new byte[size]; // Creates the array for direct access to pixel data copied via the Marshal while (true) { // Keep track of how long we take to process a screencapture Stopwatch timer = new Stopwatch(); timer.Start(); using (Graphics capture = Graphics.FromImage(bitmap)) { try { // Capture the screen and copy it into the bitmap capture.CopyFromScreen( Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, bitmap.Size, CopyPixelOperation.SourceCopy); } catch { // The copy operation can fail if the user locks the screen, so handling that exception is a good idea. Thread.Sleep(delay); continue; } // Lock the bitmap. This is required if we want to gain direct access to the pixel data, which we absolutely do. BitmapData screen = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); // Copy the pixels to the array IntPtr pointer = screen.Scan0; Marshal.Copy(pointer, pixels, 0, size); foreach (var key in KeyMap.LookupTable) { // Calculate an average colour for the area around the position int r, g, b, pixel_count; r = g = b = pixel_count = 0; // Loop over region centered around a key's coordinates for (int y = key.Value.Y - key.Value.height / 2; y <= key.Value.Y + key.Value.height / 2; y += 5) { for (int x = key.Value.X - key.Value.width / 2; x <= key.Value.X + key.Value.width / 2; x += 5) { // Skip out of bounds coordinates if (x < 0 || y < 0 || x >= Screen.PrimaryScreen.Bounds.Width || y >= Screen.PrimaryScreen.Bounds.Height) { continue; } /* Index is the result of a formula for calculating the index to a target pixel * We multiply x by 4 is because pixel data is encoded in 4 bytes: b,g,r,a * Note that screen.Stride is the image width also multiplied by 4 * Once you have the index for a desired pixel, an offset of 2,1,0 will yield the red,green,blue values. * The transparency/alpha channel value at offset 3 is seemingly unused because it appears to be a constant 255.*/ long index = (y * screen.Stride) + (x * 4); // Mean average add r += pixels[index + 2]; g += pixels[index + 1]; b += pixels[index]; pixel_count++; // Keep track of how many pixels we are averaging } } if (pixel_count == 0) { continue; } // Mean average divide r /= pixel_count; g /= pixel_count; b /= pixel_count; if (Flags.saturation_enable) { // Extract hue and lightness from colour RGB2HSL.RgbToHls(r, g, b, out double h, out double l, out double s); // Set saturation to max and change that back to rgb RGB2HSL.HlsToRgb(h, l, Math.Min(1, s * Flags.saturation_factor), out r, out g, out b); } // Maximum value clamping at 255 r = Math.Min(r, 255); g = Math.Min(g, 255); b = Math.Min(b, 255); // If enabled, draw the debug squares across the screen to illustrate if (Flags.debug_enable) { IntPtr desktop = GetDC(IntPtr.Zero); using (Graphics g_out = Graphics.FromHdc(desktop)) { if (Flags.debug_showscanregion) { // Outline the scan region g_out.DrawRectangle(Pens.White, // Top left point key.Value.X + (-key.Value.width / 2), key.Value.Y + (-key.Value.height / 2), // Dimensions key.Value.width, key.Value.height); } if (Flags.debug_showkeynames) { // Write key name for reference g_out.DrawString(key.Key.ToString(), new Font("Arial", 7, FontStyle.Bold), Brushes.Cyan, // Position top left with 5px margin key.Value.X + (-key.Value.width / 2) + 5, key.Value.Y + (-key.Value.height / 2) + 5); } // Center box containing calculated colour, with 1px white border g_out.FillRectangle(Brushes.White, key.Value.X - 6, key.Value.Y - 6, 12, 12); g_out.FillRectangle(new SolidBrush(Color.FromArgb(r, g, b)), key.Value.X - 5, key.Value.Y - 5, 10, 10); } ReleaseDC(IntPtr.Zero, desktop); } // Update the led Led.SetColorForLed(key.Key, (byte)r, (byte)g, (byte)b); } bitmap.UnlockBits(screen); // Don't forget to unlock the bitmap at the end } // Calculate how long we should sleep to wait the rest of the 100 milliseconds // Should the elapsed time exceed the desired wait time, the Max function ensures we never pass a value below 0. // If that were to happen then an exception would be thrown. timer.Stop(); Thread.Sleep(Math.Max(0, delay - (int)timer.ElapsedMilliseconds)); } }