diff --git a/Demo/Program.cs b/Demo/Program.cs index 5f99995..ec5fd46 100644 --- a/Demo/Program.cs +++ b/Demo/Program.cs @@ -9,17 +9,36 @@ namespace Demo { private static async Task Main(string[] args) { await IllumilibLighting.Initialize(); + Console.WriteLine("Setting specific positions"); + IllumilibLighting.SetKeyboardLighting(0, 1, 0); + IllumilibLighting.SetKeyboardLighting(6, 1, 1, 0, 1); + IllumilibLighting.SetKeyboardLighting(16, 5, 1, 0, 1); + Thread.Sleep(TimeSpan.FromSeconds(5)); + IllumilibLighting.SetKeyboardLighting(1, 0, 0); + IllumilibLighting.SetKeyboardLighting(8, 2, 2, 2, 0, 1, 0); + Thread.Sleep(TimeSpan.FromSeconds(5)); + Console.WriteLine("Setting all lights to blue"); IllumilibLighting.SetAllLighting(0, 0, 1); Thread.Sleep(TimeSpan.FromSeconds(3)); IllumilibLighting.SetAllLighting(0, 0, 0); + Console.WriteLine("Doing a fun effect"); + for (var x = 0; x < IllumilibLighting.KeyboardWidth; x++) { + IllumilibLighting.SetKeyboardLighting(x, 0, 1, IllumilibLighting.KeyboardHeight, 0, 0, 1); + Thread.Sleep(TimeSpan.FromSeconds(0.25F)); + } + for (var x = IllumilibLighting.KeyboardWidth - 1; x >= 0; x--) { + IllumilibLighting.SetKeyboardLighting(x, 0, 1, IllumilibLighting.KeyboardHeight, 0, 0, 0); + Thread.Sleep(TimeSpan.FromSeconds(0.25F)); + } + Console.WriteLine("Going through the alphabet"); for (var i = 65; i <= 90; i++) { var key = (KeyboardKeys) i; - IllumilibLighting.SetKeyLighting(key, 0, 1, 0); + IllumilibLighting.SetKeyboardLighting(key, 0, 1, 0); Thread.Sleep(TimeSpan.FromSeconds(0.25F)); - IllumilibLighting.SetKeyLighting(key, 0, 0, 0); + IllumilibLighting.SetKeyboardLighting(key, 0, 0, 0); } Thread.Sleep(TimeSpan.FromSeconds(1)); @@ -33,7 +52,7 @@ namespace Demo { Console.WriteLine("Setting all supported keys"); foreach (var key in Enum.GetValues()) { - IllumilibLighting.SetKeyLighting(key, 1, 0, 0); + IllumilibLighting.SetKeyboardLighting(key, 1, 0, 0); Thread.Sleep(50); } Thread.Sleep(TimeSpan.FromSeconds(15)); diff --git a/Illumilib/IllumilibLighting.cs b/Illumilib/IllumilibLighting.cs index d004ce5..9673a14 100644 --- a/Illumilib/IllumilibLighting.cs +++ b/Illumilib/IllumilibLighting.cs @@ -10,6 +10,15 @@ namespace Illumilib { /// public static class IllumilibLighting { + /// + /// The maximum width that a keyboard can have, in amount of keys + /// + public const int KeyboardWidth = 22; + /// + /// The maximum height that a keyboard can have, in amount of keys + /// + public const int KeyboardHeight = 6; + private static List systems; /// /// A property that returns whether Illumilib is currently initialized @@ -54,8 +63,7 @@ namespace Illumilib { } /// - /// Sets the lighting for all keyboards to the given color. - /// Note that, if Logitech is used, some keyboards do not support this method. + /// Sets the lighting for all keyboards to the given color /// /// The color's red value, between 0 and 1 /// The color's green value, between 0 and 1 @@ -64,6 +72,57 @@ namespace Illumilib { ForEach(s => s.SetKeyboardLighting(r, g, b)); } + /// + /// Sets the lighting for the given x, y position on the keyboard to the given color. + /// The position is zero-based, with 0, 0 being in the top left corner of the keyboard. + /// + /// The zero-based x position of the key + /// The zero-based y position of the key + /// The color's red value, between 0 and 1 + /// The color's green value, between 0 and 1 + /// The color's blue value, between 0 and 1 + /// Thrown if the positions are out of range in relation to and + public static void SetKeyboardLighting(int x, int y, float r, float g, float b) { + if (x < 0 || x >= KeyboardWidth) + throw new ArgumentOutOfRangeException(nameof(x)); + if (y < 0 || y >= KeyboardHeight) + throw new ArgumentOutOfRangeException(nameof(y)); + ForEach(s => s.SetKeyboardLighting(x, y, r, g, b)); + } + + /// + /// Sets the lighting in the given area on the keyboard to the given color. + /// The position is zero-based, with 0, 0 being in the top left corner of the keyboard. + /// The position is the top left corner of the rectangle that represents the area to set colors in. + /// + /// The zero-based x position of the key + /// The zero-based y position of the key + /// The width of the area to set the color in + /// The height of the area to set the color in + /// The color's red value, between 0 and 1 + /// The color's green value, between 0 and 1 + /// The color's blue value, between 0 and 1 + /// Thrown if the positions are out of range in relation to and + public static void SetKeyboardLighting(int x, int y, int width, int height, float r, float g, float b) { + if (x < 0 || x + width > KeyboardWidth) + throw new ArgumentOutOfRangeException(nameof(x)); + if (y < 0 || y + height > KeyboardHeight) + throw new ArgumentOutOfRangeException(nameof(y)); + ForEach(s => s.SetKeyboardLighting(x, y, width, height, r, g, b)); + } + + /// + /// Sets the lighting for the specified to the given color. + /// Only a single key can be specified at a time. + /// + /// The key value to set the lighting for + /// The color's red value, between 0 and 1 + /// The color's green value, between 0 and 1 + /// The color's blue value, between 0 and 1 + public static void SetKeyboardLighting(KeyboardKeys key, float r, float g, float b) { + ForEach(s => s.SetKeyboardLighting(key, r, g, b)); + } + /// /// Sets the lighting for all mice to the given color /// @@ -74,18 +133,6 @@ namespace Illumilib { ForEach(s => s.SetMouseLighting(r, g, b)); } - /// - /// Sets the lighting for the specified to the given color. - /// Only a single key can be specified at a time. - /// - /// The key value to set the lighting for - /// The color's red value, between 0 and 1 - /// The color's green value, between 0 and 1 - /// The color's blue value, between 0 and 1 - public static void SetKeyLighting(KeyboardKeys key, float r, float g, float b) { - ForEach(s => s.SetKeyLighting(key, r, g, b)); - } - private static void ForEach(Action action) { if (!Initialized) throw new InvalidOperationException("Illumilib has not been initialized yet"); diff --git a/Illumilib/System/LightingSystem.cs b/Illumilib/System/LightingSystem.cs index 735b079..8dae9d9 100644 --- a/Illumilib/System/LightingSystem.cs +++ b/Illumilib/System/LightingSystem.cs @@ -12,9 +12,13 @@ namespace Illumilib.System { public abstract void SetKeyboardLighting(float r, float g, float b); - public abstract void SetMouseLighting(float r, float g, float b); + public abstract void SetKeyboardLighting(int x, int y, float r, float g, float b); - public abstract void SetKeyLighting(KeyboardKeys key, float r, float g, float b); + public abstract void SetKeyboardLighting(int x, int y, int width, int height, float r, float g, float b); + + public abstract void SetKeyboardLighting(KeyboardKeys key, float r, float g, float b); + + public abstract void SetMouseLighting(float r, float g, float b); } } \ No newline at end of file diff --git a/Illumilib/System/LogitechLighting.cs b/Illumilib/System/LogitechLighting.cs index cb791ef..2da4be3 100644 --- a/Illumilib/System/LogitechLighting.cs +++ b/Illumilib/System/LogitechLighting.cs @@ -4,6 +4,9 @@ using Illumilib.Lib; namespace Illumilib.System { internal class LogitechLighting : LightingSystem { + private readonly byte[] bitmap = new byte[LogitechGsdk.LogiLedBitmapSize]; + private bool bitmapDirty; + public override Task Initialize() { try { LogitechGsdk.LogiLedInit(); @@ -14,25 +17,65 @@ namespace Illumilib.System { } public override void Dispose() { + this.ClearBitmap(); LogitechGsdk.LogiLedShutdown(); } public override void SetAllLighting(float r, float g, float b) { - LogitechGsdk.LogiLedSetLighting((int) (r * 100F), (int) (g * 100F), (int) (b * 100F)); + this.ClearBitmap(); + LogitechGsdk.LogiLedSetLighting((int) (r * 100), (int) (g * 100), (int) (b * 100)); } public override void SetKeyboardLighting(float r, float g, float b) { - for (var i = 0; i <= 3; i++) - LogitechGsdk.LogiLedSetLightingForTargetZone(DeviceType.Keyboard, i, (int) (r * 100F), (int) (g * 100F), (int) (b * 100F)); + // setting keyboard zone lighting doesn't work for some keyboards, so we fill the bitmap instead + for (var x = 0; x < LogitechGsdk.LogiLedBitmapWidth; x++) { + for (var y = 0; y < LogitechGsdk.LogiLedBitmapHeight; y++) + this.SetBitmapColor(x, y, r, g, b, 1); + } + LogitechGsdk.LogiLedSetLightingFromBitmap(this.bitmap); + } + + public override void SetKeyboardLighting(int x, int y, float r, float g, float b) { + this.SetBitmapColor(x, y, r, g, b, 1); + LogitechGsdk.LogiLedSetLightingFromBitmap(this.bitmap); + } + + public override void SetKeyboardLighting(int x, int y, int width, int height, float r, float g, float b) { + for (var xAdd = 0; xAdd < width; xAdd++) { + for (var yAdd = 0; yAdd < height; yAdd++) + this.SetBitmapColor(x + xAdd, y + yAdd, r, g, b, 1); + } + LogitechGsdk.LogiLedSetLightingFromBitmap(this.bitmap); + } + + public override void SetKeyboardLighting(KeyboardKeys key, float r, float g, float b) { + this.ClearBitmap(); + LogitechGsdk.LogiLedSetLightingForKeyWithKeyName(ConvertKey(key), (int) (r * 100), (int) (g * 100), (int) (b * 100)); } public override void SetMouseLighting(float r, float g, float b) { for (var i = 0; i <= 2; i++) - LogitechGsdk.LogiLedSetLightingForTargetZone(DeviceType.Mouse, i, (int) (r * 100F), (int) (g * 100F), (int) (b * 100F)); + LogitechGsdk.LogiLedSetLightingForTargetZone(DeviceType.Mouse, i, (int) (r * 100), (int) (g * 100), (int) (b * 100)); } - public override void SetKeyLighting(KeyboardKeys key, float r, float g, float b) { - LogitechGsdk.LogiLedSetLightingForKeyWithKeyName(ConvertKey(key), (int) (r * 100F), (int) (g * 100F), (int) (b * 100F)); + private void SetBitmapColor(int x, int y, float r, float g, float b, float a) { + // since illumilib constants are a bit bigger than logi's constants, we need to make sure here + if (x >= LogitechGsdk.LogiLedBitmapWidth || y >= LogitechGsdk.LogiLedBitmapHeight) + return; + var i = LogitechGsdk.LogiLedBitmapBytesPerKey * (y * LogitechGsdk.LogiLedBitmapWidth + x); + this.bitmap[i + 0] = (byte) (b * 255); + this.bitmap[i + 1] = (byte) (g * 255); + this.bitmap[i + 2] = (byte) (r * 255); + this.bitmap[i + 3] = (byte) (a * 255); + this.bitmapDirty = true; + } + + private void ClearBitmap() { + if (this.bitmapDirty) { + for (var i = 0; i < this.bitmap.Length; i++) + this.bitmap[i] = 0; + this.bitmapDirty = false; + } } private static KeyboardNames ConvertKey(KeyboardKeys key) { diff --git a/Illumilib/System/RazerLighting.cs b/Illumilib/System/RazerLighting.cs index 50678d7..78bba3d 100644 --- a/Illumilib/System/RazerLighting.cs +++ b/Illumilib/System/RazerLighting.cs @@ -6,11 +6,13 @@ using Colore.Effects.Keyboard; namespace Illumilib.System { internal class RazerLighting : LightingSystem { - private static IChroma chroma; + private IChroma chroma; + private CustomKeyboardEffect effect = new CustomKeyboardEffect(Color.Black); + private bool effectOutdated; public override async Task Initialize() { try { - chroma = await ColoreProvider.CreateNativeAsync(); + this.chroma = await ColoreProvider.CreateNativeAsync(); return true; } catch { return false; @@ -18,23 +20,49 @@ namespace Illumilib.System { } public override void Dispose() { - chroma?.UninitializeAsync(); + this.chroma.UninitializeAsync(); + this.effectOutdated = true; } public override void SetAllLighting(float r, float g, float b) { - chroma?.SetAllAsync(new Color(r, g, b)); + this.chroma.SetAllAsync(new Color(r, g, b)); + this.effectOutdated = true; } public override void SetKeyboardLighting(float r, float g, float b) { - chroma?.Keyboard.SetAllAsync(new Color(r, g, b)); + this.chroma.Keyboard?.SetAllAsync(new Color(r, g, b)); + this.effectOutdated = true; + } + + public override void SetKeyboardLighting(int x, int y, float r, float g, float b) { + this.chroma.Keyboard?.SetPositionAsync(y, x, new Color(r, g, b)); + this.effectOutdated = true; + } + + public override void SetKeyboardLighting(int x, int y, int width, int height, float r, float g, float b) { + if (this.chroma.Keyboard == null) + return; + if (this.effectOutdated) { + for (var fullX = 0; fullX < KeyboardConstants.MaxColumns; fullX++) { + for (var fullY = 0; fullY < KeyboardConstants.MaxRows; fullY++) + this.effect[fullY, fullX] = this.chroma.Keyboard[fullY, fullX]; + } + this.effectOutdated = false; + } + for (var xAdd = 0; xAdd < width; xAdd++) { + for (var yAdd = 0; yAdd < height; yAdd++) + this.effect[y + yAdd, x + xAdd] = new Color(r, g, b); + } + this.chroma.Keyboard.SetCustomAsync(this.effect); + } + + public override void SetKeyboardLighting(KeyboardKeys key, float r, float g, float b) { + this.chroma.Keyboard?.SetKeyAsync(ConvertKey(key), new Color(r, g, b)); + this.effectOutdated = true; } public override void SetMouseLighting(float r, float g, float b) { - chroma?.Mouse.SetAllAsync(new Color(r, g, b)); - } - - public override void SetKeyLighting(KeyboardKeys key, float r, float g, float b) { - chroma.Keyboard?.SetKeyAsync(ConvertKey(key), new Color(r, g, b)); + this.chroma.Mouse?.SetAllAsync(new Color(r, g, b)); } private static Key ConvertKey(KeyboardKeys key) { diff --git a/README.md b/README.md index c028a3a..640bfa5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # Illumilib A simple keyboard and mouse lighting library with support for Razer and Logitech devices. -- See the [Demo](https://github.com/Ellpeck/Illumilib/blob/main/Demo/Program.cs) for a demonstration. -- See [IllumilibLighting](https://github.com/Ellpeck/Illumilib/blob/main/Illumilib/IllumilibLighting.cs) for the API documentation. \ No newline at end of file + +See the [Demo](https://github.com/Ellpeck/Illumilib/blob/main/Demo/Program.cs) to check out the features, or the [IllumilibLighting](https://github.com/Ellpeck/Illumilib/blob/main/Illumilib/IllumilibLighting.cs) class for API documentation. \ No newline at end of file