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 ); } }
void patternAnimator_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = (BackgroundWorker)sender; try { log.InfoFormat("[{0}] Starting pattern playback", this.Serial); List <TriggeredEvent> eventsPlaying = new List <TriggeredEvent>(); while (!worker.CancellationPending) { TriggeredEvent ev = null; lock (EventQueue) { for (int i = 0; i < EventQueue.Count; i++) { ev = EventQueue.Dequeue(); for (int j = eventsPlaying.Count - 1; j >= 0; j--) { if (ev.Notification == eventsPlaying[j].Notification && (eventsPlaying[j].Repeat < 0 || eventsPlaying[j].Duration > 0)) { log.DebugFormat("Removing infinite playback notifications as there is another pending"); TriggeredEvent evPlaying = eventsPlaying[j]; eventsPlaying.RemoveAt(j); AssignBusyLeds(evPlaying, false); } } if (CanPlayEvent(ev)) { break; } else { EventQueue.Enqueue(ev); ev = null; } } } if (ev != null) { if (ev.NotificationSnapshot is PatternNotification) { ev.EventStarted = DateTime.Now; ev.AnimationStarted = DateTime.Now; PatternNotification notification = ev.NotificationSnapshot as PatternNotification; foreach (Animation animation in notification.Pattern.Animations) { Animation copyAnimation = new Animation(); copyAnimation.Assign(animation); ev.Animations.Add(copyAnimation); } ev.Animations[0].ReferenceColor = GetColor(ev.NotificationSnapshot); AssignBusyLeds(ev, true); eventsPlaying.Add(ev); } else { ev.AnimationStarted = DateTime.Now; ev.EventStarted = DateTime.Now; foreach (Animation animation in ev.Pattern.Animations) { Animation copyAnimation = new Animation(); copyAnimation.Assign(animation); ev.Animations.Add(copyAnimation); } ev.Animations[0].ReferenceColor = GetColor(ev.NotificationSnapshot); AssignBusyLeds(ev, true); eventsPlaying.Add(ev); } } //Prepare next frame of data to send to LEDs for (int ii = eventsPlaying.Count - 1; ii >= 0; ii--) { TriggeredEvent evnt = eventsPlaying[ii]; RgbColor color = evnt.Animations[evnt.AnimationIndex].GetColor(evnt.AnimationStarted.Value, DateTime.Now, evnt.Animations[evnt.AnimationIndex].ReferenceColor); PatternNotification notification = evnt.NotificationSnapshot as PatternNotification; lock (this) { SetColor(evnt, color.R, color.G, color.B); } if (evnt.Animations[evnt.AnimationIndex].AnimationFinished) { evnt.AnimationIndex += 1; if (evnt.Duration > 0 && evnt.EventStarted.Value.AddMilliseconds(evnt.Duration) <= DateTime.Now) { eventsPlaying.RemoveAt(ii); AssignBusyLeds(evnt, false); } else if (evnt.AnimationIndex == evnt.Animations.Count) { evnt.RepeatCount += 1; if (evnt.Duration > 0 || evnt.Repeat < 0 || evnt.RepeatCount < evnt.Repeat) { evnt.AnimationStarted = DateTime.Now; evnt.AnimationIndex = 0; evnt.Animations.ForEach(delegate(Animation a) { a.Reset(); }); } else { eventsPlaying.RemoveAt(ii); AssignBusyLeds(evnt, false); } } else { evnt.Animations[evnt.AnimationIndex].ReferenceColor = GetColor(evnt.NotificationSnapshot); evnt.AnimationStarted = DateTime.Now; } } } if (Led != null && NeedsLedUpdate) { lock (this) { NeedsLedUpdate = false; } int retryCount = 5; if (Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStick || Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStickPro && Led.Mode < 2) { while (retryCount > 0) //Retry loop { try { Led.SetColor(LedFrame[0][1], LedFrame[0][0], LedFrame[0][2]); retryCount = 0; } catch (Exception ex) { retryCount--; log.ErrorFormat("Failed to set color: {0}", ex); if (retryCount > 0) { Thread.Sleep(20); log.InfoFormat("Retry set color #{0}", 5 - retryCount); } else { log.Warn("Failed to set color 5 times, giving up..."); } } } } else { byte[] frame = new byte[this.LedsR * 3]; Array.Copy(LedFrame[0], 0, frame, 0, frame.Length); while (retryCount > 0) //Retry loop { try { Led.SetColors(0, frame); retryCount = 0; } catch (Exception ex) { retryCount--; log.ErrorFormat("Failed to set color: {0}", ex); if (retryCount > 0) { Thread.Sleep(20); log.InfoFormat("Retry set color #{0}", 5 - retryCount); } else { log.Warn("Failed to set color 5 times, giving up..."); } } } int sleep = Math.Max(2, (int)(this.LedsR * 3 * 8f / 400f * 1.2)); //number of LEDs times 3 color elements times 8 bytes divided by speed Thread.Sleep(sleep); if (Led != null && Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStickPro) { frame = new byte[this.LedsG * 3]; Array.Copy(LedFrame[1], 0, frame, 0, frame.Length); while (retryCount > 0) //Retry loop { try { Led.SetColors(1, frame); retryCount = 0; } catch (Exception ex) { retryCount--; log.ErrorFormat("Failed to set color: {0}", ex); if (retryCount > 0) { Thread.Sleep(20); log.InfoFormat("Retry set color #{0}", 5 - retryCount); } else { log.Warn("Failed to set color 5 times, giving up..."); } } } sleep = Math.Max(2, (int)(this.LedsG * 3 * 8f / 400f * 1.2)); Thread.Sleep(sleep); frame = new byte[this.LedsB * 3]; Array.Copy(LedFrame[2], 0, frame, 0, frame.Length); while (retryCount > 0) //Retry loop { try { Led.SetColors(2, frame); retryCount = 0; } catch (Exception ex) { retryCount--; log.ErrorFormat("Failed to set color: {0}", ex); if (retryCount > 0) { Thread.Sleep(20); log.InfoFormat("Retry set color #{0}", 5 - retryCount); } else { log.Warn("Failed to set color 5 times, giving up..."); } } } sleep = Math.Max(2, (int)(this.LedsB * 3 * 8f / 400f * 1.2)); Thread.Sleep(sleep); } } } else { Thread.Sleep(1); } } } catch (Exception ex) { log.ErrorFormat("Pattern playback crash {0}", ex); } log.InfoFormat("[{0}] Pattern playback stopped", this.Serial); Running = false; }
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)); } }