From 9756bacaea3130e31dbc5ee16caf2a6fc531a45d Mon Sep 17 00:00:00 2001 From: Harald Albrecht Date: Mon, 7 Jan 2019 18:36:50 +0100 Subject: [PATCH] more refactoring; adds dynamic key coloring --- sdcard/layouts/shift.lua | 23 +++++++----- sdcard/snippets/mb/leds.lua | 65 ++++++++++++++++++++++++++++++++-- sdcard/snippets/multibow.lua | 44 ----------------------- spec/layouts/kdenlive_spec.lua | 2 +- spec/layouts/shift_spec.lua | 19 ++++++++++ spec/snippets/leds_spec.lua | 35 ++++++++++++++++-- 6 files changed, 131 insertions(+), 57 deletions(-) diff --git a/sdcard/layouts/shift.lua b/sdcard/layouts/shift.lua index 2dbc0ae..8c4625a 100644 --- a/sdcard/layouts/shift.lua +++ b/sdcard/layouts/shift.lua @@ -55,12 +55,17 @@ shift.KEY_SHIFT = shift.KEY_SHIFT or 11 shift.KEY_LAYOUT = shift.KEY_LAYOUT or 8 shift.KEY_BRIGHTNESS = shift.KEY_BRIGHTNESS or 5 +shift.BRIGHTNESS_LEVELS = shift.BRIGHTNESS_LEVELS or { 70, 100, 40 } + -- Internal flag for detecting SHIFT press-release sequences without any SHIFTed -- function. local shift_only = false local grabbed_key_count = 0 +-- Activates the first brightness level... +shift.brightnesses = table.pack(table.unpack(shift.BRIGHTNESS_LEVELS)) +mb.cycle_brightness(shift.brightnesses) -- Switches to the next SHIFT layer within the currently active keyboard layout. -- SHIFT layer(s) are wired up as a circular list of keymaps, linked using their @@ -72,7 +77,6 @@ function shift.shift_secondary_keymap() end end - -- Remember how many grabbed keys are pressed, so we won't ungrab later until -- all keys have been released. function shift.any_press(keyno) @@ -94,7 +98,6 @@ function shift.any_release(keyno) end end - -- SHIFT press: switches into grabbed SHIFT mode, activating the in-SHIFT keys -- for brightness change, keymap cycling, et cetera. function shift.shift(key) @@ -106,23 +109,27 @@ function shift.shift(key) mb.grab(shift.keymap_shifted.name) end - -- Cycles to the next primary keyboard layout (keymap) function shift.cycle(key) shift_only = false mb.cycle_primary_keymaps() end - -- Changes the Keybow LED brightness, by cycling through different brightness -- levels function shift.brightness(key) shift_only = false - local b = mb.brightness + 0.3 - if b > 1 then; b = 0.4; end - mb.set_brightness(b) + mb.cycle_brightness(shift.brightnesses) + mb.led(key, shift.next_brightness_color()) end +-- Returns the color for the BRIGHTNESS key LED: this is the next brightness +-- level in the sequence of brightness levels as a color of gray/white. +function shift.next_brightness_color() + local br = shift.brightnesses[1] + br = br > 1.0 and br / 100 or br + return { r=br, g=br, b=br } +end -- define and register our keymaps: the permanent SHIFT key-only keymap, as well -- as a temporary grabbing keymap while the SHIFT key is being pressed and held. @@ -138,7 +145,7 @@ shift.keymap_shifted = { [shift.KEY_SHIFT] = {c={r=1, g=1, b=1}}, [shift.KEY_LAYOUT] = {c={r=0, g=1, b=1}, press=shift.cycle, release=shift.release_other}, - [shift.KEY_BRIGHTNESS] = {c={r=0.5, g=0.5, b=0.5}, press=shift.brightness, release=shift.release_other} + [shift.KEY_BRIGHTNESS] = {c=shift.next_brightness_color, press=shift.brightness, release=shift.release_other} } mb.register_keymap(shift.keymap) mb.register_keymap(shift.keymap_shifted) diff --git a/sdcard/snippets/mb/leds.lua b/sdcard/snippets/mb/leds.lua index 668cb9f..1fedab6 100644 --- a/sdcard/snippets/mb/leds.lua +++ b/sdcard/snippets/mb/leds.lua @@ -23,13 +23,74 @@ SOFTWARE. ]]-- +mb.MIN_BRIGHTNESS = mb.MIN_BRIGHTNESS or 0.1 + -- Default LED brightness in the [0.1..1] range. mb.brightness = 1 --- Sets the Keybow key LEDs maximum brightness, in the range [0.1..1]. +-- Sets the Keybow key LEDs maximum brightness, in the range [0.1..1] or +-- [10..100] (percent). The minim brightness is clamped on purpose to avoid +-- unlit LEDs. The lower clamp defaults to mb.MIN_BRIGHTNESS. function mb.set_brightness(brightness) - if brightness < 0.1 then brightness = 0.1 end + brightness = brightness > 1.0 and brightness / 100 or brightness + if brightness < mb.MIN_BRIGHTNESS then brightness = mb.MIN_BRIGHTNESS end if brightness > 1 then brightness = 1 end mb.brightness = brightness mb.activate_leds() end + +-- Cycles through a list of brightness values, always taking the first +-- value and modifying the list by cycling it. Brightness values can be +-- either [0..1] or [0..100] (that is, percent) +function mb.cycle_brightness(brightnesses) + local brightness = table.remove(brightnesses, 1) + table.insert(brightnesses, brightness) + mb.set_brightness(brightness) + return brightnesses +end + +-- Sets key LED to specific color, taking brightness into consideration. +-- The color is a triple (table) with the elements r, g, and b. Each color +-- component is in the range [0..1]. +function mb.led(keyno, color) + if color then + local b = mb.brightness * 255 + keybow.set_pixel(keyno, color.r * b, color.g * b, color.b * b) + else + keybow.set_pixel(keyno, 0, 0, 0) + end + end + +-- Restores Keybow LEDs according to current keymap and the permanent keymaps. +function mb.activate_leds() + keybow.clear_lights() + -- if a grab is in place then it takes absolute priority + if mb.grab_keymap then + mb.activate_keymap_leds(mb.grab_keymap) + else + -- first update LEDs for the current keymap... + if mb.current_keymap ~= nil then + mb.activate_keymap_leds(mb.current_keymap) + end + -- ...then update LEDs from permanent keymap(s), as this ensures that + -- the permanent keymaps take precedence. + for name, keymap in pairs(mb.keymaps) do + if keymap.permanent then + mb.activate_keymap_leds(keymap) + end + end + end +end + +-- Helper function that iterates over all keymap elements but skipping non-key +-- bindings. +function mb.activate_keymap_leds(keymap) + for keyno, keydef in pairs(keymap) do + -- Only iterates over keys, skipping any other keymap definitions. + if type(keyno) == "number" and keydef.c then + local color = type(keydef.c) == "function" and keydef.c() or keydef.c + mb.led(keyno, color) + end + end +end + diff --git a/sdcard/snippets/multibow.lua b/sdcard/snippets/multibow.lua index fb6f922..d8230a9 100644 --- a/sdcard/snippets/multibow.lua +++ b/sdcard/snippets/multibow.lua @@ -160,50 +160,6 @@ function mb.ungrab() end --- Sets key LED to specific color, taking brightness into consideration. --- The color is a triple (table) with the elements r, g, and b. Each color --- component is in the range [0..1]. -function mb.led(keyno, color) - if color then - local b = mb.brightness * 255 - keybow.set_pixel(keyno, color.r * b, color.g * b, color.b * b) - else - keybow.set_pixel(keyno, 0, 0, 0) - end -end - --- Restores Keybow LEDs according to current keymap and the permanent keymaps. -function mb.activate_leds() - keybow.clear_lights() - -- if a grab is in place then it takes absolute priority - if mb.grab_keymap then - mb.activate_keymap_leds(mb.grab_keymap) - else - -- first update LEDs for the current keymap... - if mb.current_keymap ~= nil then - mb.activate_keymap_leds(mb.current_keymap) - end - -- ...then update LEDs from permanent keymap(s), as this ensures that - -- the permanent keymaps take precedence. - for name, keymap in pairs(mb.keymaps) do - if keymap.permanent then - mb.activate_keymap_leds(keymap) - end - end - end -end - --- Helper function that iterates over all keymap elements but skipping non-key --- bindings. -function mb.activate_keymap_leds(keymap) - for keyno, keydef in pairs(keymap) do - if type(keyno) == "number" and keydef.c then - -- print((keymap.permanent and "permanent LED" or "LED") .. " " .. keyno) - mb.led(keyno, keydef.c) - end - end -end - -- Disables the automatic Keybow lightshow and sets the key LED colors. This -- is a well-known (hook) function that gets called by the Keybow firmware -- after initialization immediately before waiting for key events. diff --git a/spec/layouts/kdenlive_spec.lua b/spec/layouts/kdenlive_spec.lua index 48f6e35..9cea069 100644 --- a/spec/layouts/kdenlive_spec.lua +++ b/spec/layouts/kdenlive_spec.lua @@ -46,7 +46,7 @@ describe("Kdenlive keymap", function() _G.setup() end) - it("colors its keys", function() + inslit("colors its keys", function() for _, keymap in pairs(mb.registered_keymaps()) do if string.sub(keymap.name, 1, #"kdenlive") == "kdenlive" then for keyno = 0, 11 do diff --git a/spec/layouts/shift_spec.lua b/spec/layouts/shift_spec.lua index cccea34..9a5d962 100644 --- a/spec/layouts/shift_spec.lua +++ b/spec/layouts/shift_spec.lua @@ -179,6 +179,25 @@ describe("SHIFT multibow keymap", function() end) end) + inslit("cycles brightness", function() + local s = spy.on(mb, "led") + + local len = #shift.BRIGHTNESS_LEVELS + for i = 1, len do + assert.equals(mb.brightness * 100, shift.BRIGHTNESS_LEVELS[i]) + -- enters SHIFT and check the brightness of brightness key... + s:clear() + hwk.press(shift.KEY_SHIFT) + assert.spy(s).was.called_with( + shift.KEY_BRIGHTNESS, + shift.next_brightness_color()) + -- cycles to next brightness + hwk.tap(shift.KEY_BRIGHTNESS) + hwk.release(shift.KEY_SHIFT) + end + assert.equals(mb.brightness * 100, shift.BRIGHTNESS_LEVELS[1]) + end) + end) end) diff --git a/spec/snippets/leds_spec.lua b/spec/snippets/leds_spec.lua index cac3d81..cf4f219 100644 --- a/spec/snippets/leds_spec.lua +++ b/spec/snippets/leds_spec.lua @@ -29,11 +29,42 @@ describe("multibow LEDs", function() mb.set_brightness(0.5) assert.equals(0.5, mb.brightness) - mb.set_brightness(1.1) + mb.set_brightness(1.0) assert.equals(1.0, mb.brightness) mb.set_brightness(0) - assert.equals(0.1, mb.brightness) + assert.equals(mb.MIN_BRIGHTNESS, mb.brightness) + + mb.set_brightness(20) + assert.equals(0.2, mb.brightness) + end) + + it("cycles brightness", function() + function f(b, scale) + local copy = table.pack(table.unpack(b)) + local len = #b + for i = 1, len do + mb.cycle_brightness(copy) + assert.equals(b[i], mb.brightness * scale) + end + end + + f({ 0.7, 1.0, 0.4 }, 1) + f({ 70, 100, 40 }, 100) + end) + + inslit("accepts LED color functions in keymaps", function() + local s = spy.on(mb, "led") + local km = { + name="test", + [0]={c={r=0, g=1, b=0}}, + [1]={c=function() return {r=1, g=1, b=1} end} + } + + mb.activate_keymap_leds(km) + assert.spy(s).was.called(2) + assert.spy(s).was.called_with(0, {r=0, g=1, b=0}) + assert.spy(s).was.called_with(1, {r=1, g=1, b=1}) end) end)