Harald Albrecht
5 years ago
commit
59850fd49b
13 changed files with 827 additions and 0 deletions
@ -0,0 +1,19 @@ |
|||||
|
Copyright 2019 Harald Albrecht |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1,59 @@ |
|||||
|
# Multibow |
||||
|
|
||||
|
GitHub: [github.com/thediveo/multibow](https://github.com/thediveo/multibow) |
||||
|
|
||||
|
Multibow turns a [Pimoroni Keybow](https://shop.pimoroni.com/products/keybow) |
||||
|
into a macro keyboard with multiple layouts, switchable at any time. (Keybow is |
||||
|
a solderless DIY 4x3 mechanical USB keyboard, powered by a Raspberry Pi. And |
||||
|
yes, these days even keyboards run Linux...) |
||||
|
|
||||
|
![Multibow on Keybow](multibow.jpg) |
||||
|
|
||||
|
Layouts included: |
||||
|
- Debug Go programs and packages in VisualStudio Code with its Go extension. |
||||
|
- "Empty" keyboard layout what does nothing, very useful when using cycling between different keyboard layouts to have one non-reacting layout. |
||||
|
- permanent layout for cycling between the other layouts and for changing the keybow LED brightness. |
||||
|
|
||||
|
## Licenses |
||||
|
|
||||
|
Multibow is (c) 2019 Harald Albrecht and is licensed under the MIT license, see |
||||
|
the [LICENSE](LICENSE) file. |
||||
|
|
||||
|
The file `keybow.lua` included from |
||||
|
[pimoroni/keybow-firmware](https://github.com/pimoroni/keybow-firmware) for |
||||
|
testing purposes is licensed under the MIT license, as declared by Pimoroni's |
||||
|
keybow-firmware GitHub repository. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
1. Download the [Pibow |
||||
|
firmware](https://github.com/pimoroni/keybow-firmware/releases) and copy all |
||||
|
files inside its `sdcard/` subdirectory onto an empty, FAT32 formatted microSD |
||||
|
card. Copy only the files **inside** `sdcard/`, but do **not** place them into a |
||||
|
~~`sdcard`~~ directory on your microSD card. |
||||
|
|
||||
|
2. Download all files from the `sdcard/` subdirectory of this repository and |
||||
|
then copy them onto the microSD card. This will overwrite but one file |
||||
|
`key.lua`, all other files are new. |
||||
|
|
||||
|
## Multiple Keyboard Layouts |
||||
|
|
||||
|
To enable one or more multibow keyboard layouts, edit `sdcard/keys.lua` |
||||
|
accordingly to require them. The default configuration is as follows: |
||||
|
|
||||
|
```lua |
||||
|
require "layouts/shift" -- for cycling between layouts. |
||||
|
require "layouts/vsc-golang" -- debugging Go programs in VisualStudio Code. |
||||
|
require "layouts/empty" -- empty, do-nothing layout. |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## Developing |
||||
|
|
||||
|
For some basic testing, run `lua test.lua` from the base directory of this |
||||
|
repository. It pulls in `keybow`, then mocks some functionality of it, and |
||||
|
finally starts `sdcard/keys.lua` as usual. |
||||
|
|
||||
|
This helps in detecting syntax and logic erros early, avoiding the |
||||
|
rinse-and-repeat cycle with copying to microSD card, starting the Keybow |
||||
|
hardware, and then wondering what went wrong, without any real clue. |
@ -0,0 +1,210 @@ |
|||||
|
keybow = {} |
||||
|
|
||||
|
local KEYCODES = "abcdefghijklmnopqrstuvwxyz1234567890\n\a\b\t -=[]\\#;'`,./" |
||||
|
local SHIFTED_KEYCODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()\a\a\a\a\a_+{}|~:\"~<>?" |
||||
|
|
||||
|
keybow.LEFT_CTRL = 0 |
||||
|
keybow.LEFT_SHIFT = 1 |
||||
|
keybow.LEFT_ALT = 2 |
||||
|
keybow.LEFT_META = 3 |
||||
|
|
||||
|
keybow.RIGHT_CTRL = 4 |
||||
|
keybow.RIGHT_SHIFT = 5 |
||||
|
keybow.RIGHT_ALT = 6 |
||||
|
keybow.RIGHT_META = 7 |
||||
|
|
||||
|
keybow.ENTER = 0x28 |
||||
|
keybow.ESC = 0x29 |
||||
|
keybow.BACKSPACE = 0x2a |
||||
|
keybow.TAB = 0x2b |
||||
|
keybow.SPACE = 0x2c |
||||
|
keybow.CAPSLOCK = 0x39 |
||||
|
|
||||
|
keybow.LEFT_ARROW = 0x50 |
||||
|
keybow.RIGHT_ARROW = 0x4f |
||||
|
keybow.UP_ARROW = 0x52 |
||||
|
keybow.DOWN_ARROW = 0x51 |
||||
|
|
||||
|
keybow.F1 = 0x3a |
||||
|
keybow.F2 = 0x3b |
||||
|
keybow.F3 = 0x3c |
||||
|
keybow.F4 = 0x3d |
||||
|
keybow.F5 = 0x3e |
||||
|
keybow.F6 = 0x3f |
||||
|
keybow.F7 = 0x40 |
||||
|
keybow.F8 = 0x41 |
||||
|
keybow.F9 = 0x42 |
||||
|
keybow.F10 = 0x43 |
||||
|
keybow.F11 = 0x44 |
||||
|
keybow.F12 = 0x45 |
||||
|
|
||||
|
keybow.KEY_DOWN = true |
||||
|
keybow.KEY_UP = false |
||||
|
|
||||
|
-- Functions exposed from C |
||||
|
|
||||
|
function keybow.set_modifier(key, state) |
||||
|
keybow_set_modifier(key, state) |
||||
|
end |
||||
|
|
||||
|
function keybow.sleep(time) |
||||
|
keybow_sleep(time) |
||||
|
end |
||||
|
|
||||
|
function keybow.usleep(time) |
||||
|
keybow_usleep(time) |
||||
|
end |
||||
|
|
||||
|
function keybow.text(text) |
||||
|
for i = 1, #text do |
||||
|
local c = text:sub(i, i) |
||||
|
keybow.tap_key(c) |
||||
|
end |
||||
|
|
||||
|
keybow.set_modifier(keybow.LEFT_SHIFT, false) |
||||
|
end |
||||
|
|
||||
|
-- Lighting control |
||||
|
|
||||
|
function keybow.set_pixel(x, r, g, b) |
||||
|
keybow_set_pixel(x, r, g, b) |
||||
|
end |
||||
|
|
||||
|
function keybow.auto_lights(state) |
||||
|
keybow_auto_lights(state) |
||||
|
end |
||||
|
|
||||
|
function keybow.clear_lights() |
||||
|
keybow_clear_lights() |
||||
|
end |
||||
|
|
||||
|
function keybow.load_pattern(file) |
||||
|
keybow_load_pattern(file) |
||||
|
end |
||||
|
|
||||
|
-- Meta keys - ctrl, shift, alt and win/apple |
||||
|
|
||||
|
function keybow.tap_left_ctrl() |
||||
|
keybow.set_modifier(keybow.LEFT_CTRL, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.LEFT_CTRL, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_right_ctrl() |
||||
|
keybow.set_modifier(keybow.RIGHT_CTRL, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.RIGHT_CTRL, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_left_shift() |
||||
|
keybow.set_modifier(keybow.LEFT_SHIFT, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.LEFT_SHIFT, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_right_shift() |
||||
|
keybow.set_modifier(keybow.RIGHT_SHIFT, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.RIGHT_SHIFT, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_left_alt() |
||||
|
keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.LEFT_ALT, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_right_alt() |
||||
|
keybow.set_modifier(keybow.RIGHT_ALT, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.RIGHT_ALT, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_left_meta() |
||||
|
keybow.set_modifier(keybow.LEFT_META, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.LEFT_META, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_right_meta() |
||||
|
keybow.set_modifier(keybow.RIGHT_META, keybow.KEY_DOWN) |
||||
|
keybow.set_modifier(keybow.RIGHT_META, keybow.KEY_UP) |
||||
|
end |
||||
|
|
||||
|
-- Function keys |
||||
|
|
||||
|
function keybow.tap_function_key(index) |
||||
|
index = 57 + index -- Offset to 0x39 (F1 is 0x3a) |
||||
|
keybow.set_key(index, true) |
||||
|
keybow.set_key(index, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.ascii_to_shift(key) |
||||
|
if not (type(key) == "string") then |
||||
|
return false |
||||
|
end |
||||
|
|
||||
|
return SHIFTED_KEYCODES.find(key) ~= nil |
||||
|
end |
||||
|
|
||||
|
function keybow.ascii_to_hid(key) |
||||
|
if not (type(key) == "string") then |
||||
|
return key |
||||
|
end |
||||
|
|
||||
|
key = key:lower() |
||||
|
|
||||
|
code = KEYCODES:find(key) |
||||
|
|
||||
|
if code == nil then return nil end |
||||
|
|
||||
|
return code + 3 |
||||
|
end |
||||
|
|
||||
|
function keybow.set_key(key, pressed) |
||||
|
if type(key) == "string" then |
||||
|
local hid_code = nil |
||||
|
local shifted = SHIFTED_KEYCODES:find(key, 1, true) ~= nil |
||||
|
|
||||
|
if shifted then |
||||
|
hid_code = SHIFTED_KEYCODES:find(key, 1, true) |
||||
|
else |
||||
|
hid_code = KEYCODES:find(key, 1, true) |
||||
|
end |
||||
|
|
||||
|
if not (hid_code == nil) then |
||||
|
hid_code = hid_code + 3 |
||||
|
if shifted then keybow.set_modifier(keybow.LEFT_SHIFT, pressed) end |
||||
|
keybow_set_key(hid_code, pressed) |
||||
|
end |
||||
|
|
||||
|
else -- already a key code |
||||
|
keybow_set_key(key, pressed) |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_enter() |
||||
|
keybow.set_key(keybow.ENTER, true) |
||||
|
keybow.set_key(keybow.ENTER, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_space() |
||||
|
keybow.set_key(keybow.SPACE, true) |
||||
|
keybow.set_key(keybow.SPACE, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_shift() |
||||
|
keybow.set_key(keybow.LEFT_SHIFT, true) |
||||
|
keybow.set_key(keybow.LEFT_SHIFT, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_tab() |
||||
|
keybow.set_key(keybow.TAB, true) |
||||
|
keybow.set_key(keybow.TAB, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_key(key) |
||||
|
keybow.set_key(key, true) |
||||
|
keybow.set_key(key, false) |
||||
|
end |
||||
|
|
||||
|
function keybow.press_key(key) |
||||
|
keybow.set_key(key, true) |
||||
|
end |
||||
|
|
||||
|
function keybow.release_key(key) |
||||
|
keybow.set_key(key, false) |
||||
|
end |
@ -0,0 +1,24 @@ |
|||||
|
require "keybow" |
||||
|
|
||||
|
function keybow.set_pixel(pix, r, g, b) |
||||
|
-- print("set_pixel", pix, r, g, b) |
||||
|
end |
||||
|
|
||||
|
function keybow.auto_lights(onoff) |
||||
|
-- print("auto_lights", onoff) |
||||
|
end |
||||
|
|
||||
|
function keybow.clear_lights() |
||||
|
-- print("clear_lights") |
||||
|
end |
||||
|
|
||||
|
function keybow.tap_key(key) |
||||
|
print("tap_key", key) |
||||
|
end |
||||
|
|
||||
|
function keybow.set_modifier(mod, key) |
||||
|
print("set_modifier", mod, key) |
||||
|
end |
||||
|
|
||||
|
require "keys" |
||||
|
setup() |
After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,4 @@ |
|||||
|
require "keybow" |
||||
|
require "layouts/shift" |
||||
|
require "layouts/vsc-golang" |
||||
|
require "layouts/empty" |
@ -0,0 +1,22 @@ |
|||||
|
-- An empty Multibow Keybow layout. Useful for "switching off" the keyboard, |
||||
|
|
||||
|
require "snippets/multibow" |
||||
|
|
||||
|
--[[ |
||||
|
The Keybow layout is as follows when in landscape orientation, with the USB |
||||
|
cable going off "northwards": |
||||
|
|
||||
|
┋┋ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 11 │ │ 8 │ │ 5 │ │ 2 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 10 │ │ 7 │ │ 4 │ │ 1 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 9 │ │ 6 │ │ 3 │ │ 0 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
|
||||
|
]]-- |
||||
|
|
||||
|
mb.register_keymap({}, "empty") |
@ -0,0 +1,38 @@ |
|||||
|
-- A permanent SHIFT layout toggler |
||||
|
|
||||
|
require "snippets/multibow" |
||||
|
|
||||
|
--[[ |
||||
|
The Keybow layout is as follows when in landscape orientation, with the USB |
||||
|
cable going off "northwards": |
||||
|
|
||||
|
┋┋ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 11 │ │ 8 │ │ 5 │ │ 2 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 10 │ │ 7 │ │ 4 │ │ 1 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 9 │ │ 6 │ │ 3 │ │ 0 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
|
||||
|
]]-- |
||||
|
|
||||
|
shift = {} |
||||
|
|
||||
|
function shift.cycle(key) |
||||
|
print("permanent SHIFT") |
||||
|
mb.cycle_keymaps() |
||||
|
end |
||||
|
|
||||
|
function shift.brightness(key) |
||||
|
local b = mb.brightness + 0.3 |
||||
|
if b > 1 then; b = 0.4; end |
||||
|
mb.set_brightness(b) |
||||
|
end |
||||
|
|
||||
|
mb.register_permanent_keymap({ |
||||
|
[11] = {c={r=1, g=1, b=1}, h=shift.cycle}, |
||||
|
[8] = {c={r=0.5, g=0.5, b=0.5}, h=shift.brightness} |
||||
|
}, "shift") |
@ -0,0 +1,74 @@ |
|||||
|
-- VSC Go extension debug Keybow layout |
||||
|
|
||||
|
require "snippets/multibow" |
||||
|
|
||||
|
--[[ |
||||
|
The Keybow layout is as follows when in landscape orientation, with the USB |
||||
|
cable going off "northwards": |
||||
|
|
||||
|
┋┋ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 11 │ │ 8 │ │ 5 │ │ 2 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 10 │ │ 7 │ │ 4 │ │ 1 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
┌────┐ ┌────┐ ┌────┐ ┌────┐ |
||||
|
│ 9 │ │ 6 │ │ 3 │ │ 0 │ |
||||
|
└────┘ └────┘ └────┘ └────┘ |
||||
|
|
||||
|
]]-- |
||||
|
|
||||
|
RED = { r=1, g=0, b=0 } |
||||
|
YELLOW = { r=1, g=0.8, b=0 } |
||||
|
GREEN = { r=0, g=1, b=0 } |
||||
|
BLUE = { r=0, g=0, b=1 } |
||||
|
BLUECYAN = { r=0, g=0.7, b=1 } |
||||
|
BLUEGRAY = { r=0.7, g=0.7, b=1 } |
||||
|
CYAN = { r=0, g=1, b=1 } |
||||
|
|
||||
|
|
||||
|
-- AND NOW FOR SOMETHING DIFFERENT: THE REAL MEAT -- |
||||
|
|
||||
|
function debug_stop(key) |
||||
|
mb.tap(key, keybow.F5, keybow.LEFT_SHIFT) |
||||
|
end |
||||
|
|
||||
|
function debug_restart(key) |
||||
|
mb.tap(key, keybow.F5, keybow.LEFT_SHIFT, keybow.LEFT_CTRL) |
||||
|
end |
||||
|
|
||||
|
function debug_continue(key) |
||||
|
mb.tap(key, keybow.F5) |
||||
|
end |
||||
|
|
||||
|
function debug_stepover(key) |
||||
|
mb.tap(key, keybow.F10) |
||||
|
end |
||||
|
|
||||
|
function debug_stepinto(key) |
||||
|
mb.tap(key, keybow.F11) |
||||
|
end |
||||
|
|
||||
|
function debug_stepout(key) |
||||
|
mb.tap(key, keybow.F11, keybow.LEFT_SHIFT) |
||||
|
end |
||||
|
|
||||
|
function go_test_package(key) |
||||
|
mb.tap(key, "P", keybow.LEFT_SHIFT, keybow.LEFT_CTRL) |
||||
|
keybow.sleep(250) |
||||
|
keybow.text("go test package") |
||||
|
keybow.tap_enter() |
||||
|
end |
||||
|
|
||||
|
|
||||
|
mb.register_keymap({ |
||||
|
[10] = {c=RED, h=debug_stop}, |
||||
|
[7] = {c=YELLOW, h=debug_restart}, |
||||
|
[1] = {c=CYAN, h=go_test_package}, |
||||
|
|
||||
|
[9] = {c=GREEN, h=debug_continue}, |
||||
|
[6] = {c=BLUECYAN, h=debug_stepinto}, |
||||
|
[3] = {c=BLUE, h=debug_stepover}, |
||||
|
[0] = {c=BLUEGRAY, h=debug_stepout}, |
||||
|
}, 'vsc-golang-debug') |
@ -0,0 +1,108 @@ |
|||||
|
--[[ |
||||
|
Provide additional keybow USB HID key definitions. |
||||
|
|
||||
|
For more information about USB HID keyboard scan codes, for instance, |
||||
|
see: https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 |
||||
|
|
||||
|
Copyright 2019 Harald Albrecht |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
||||
|
]]-- |
||||
|
|
||||
|
require("keybow") |
||||
|
|
||||
|
keybow.SYSRQ = 0x46 |
||||
|
keybow.SCROLLLOCK = 0x47 |
||||
|
keybow.PAUSE = 0x48 |
||||
|
keybow.INSERT = 0x49 |
||||
|
keybow.DELETE = 0x4c |
||||
|
keybow.HOME = 0x4a |
||||
|
keybow.END = 0x4d |
||||
|
keybow.PAGEUP = 0x4b |
||||
|
keybow.PAGEOWN = 0x4e |
||||
|
|
||||
|
keybow.F13 = 0x68 |
||||
|
keybow.F14 = 0x69 |
||||
|
keybow.F15 = 0x6a |
||||
|
keybow.F16 = 0x6b |
||||
|
keybow.F17 = 0x6c |
||||
|
keybow.F18 = 0x6d |
||||
|
keybow.F19 = 0x6e |
||||
|
keybow.F20 = 0x6f |
||||
|
keybow.F21 = 0x70 |
||||
|
keybow.F22 = 0x71 |
||||
|
keybow.F23 = 0x72 |
||||
|
keybow.F24 = 0x73 |
||||
|
|
||||
|
keybow.KPSLASH = 0x54 |
||||
|
keybow.KPASTERISK = 0x55 |
||||
|
keybow.KPMINUS = 0x56 |
||||
|
keybow.KPPLUS = 0x57 |
||||
|
keybow.KPENTER = 0x58 |
||||
|
keybow.KP1 = 0x59 |
||||
|
keybow.KP2 = 0x5a |
||||
|
keybow.KP3 = 0x5b |
||||
|
keybow.KP4 = 0x5c |
||||
|
keybow.KP5 = 0x5d |
||||
|
keybow.KP6 = 0x5e |
||||
|
keybow.KP7 = 0x5f |
||||
|
keybow.KP8 = 0x60 |
||||
|
keybow.KP9 = 0x61 |
||||
|
keybow.KP0 = 0x62 |
||||
|
keybow.KPDOT = 0x63 |
||||
|
keybow.KPEQUAL = 0x67 |
||||
|
|
||||
|
keybow.COMPOSE = 0x65 |
||||
|
keybow.POWER = 0x66 |
||||
|
|
||||
|
keybow.OPEN = 0x74 |
||||
|
keybow.HELP = 0x75 |
||||
|
keybow.PROPS = 0x76 |
||||
|
keybow.FRONT = 0x77 |
||||
|
keybow.STOP = 0x78 |
||||
|
keybow.AGAIN = 0x79 |
||||
|
keybow.UNDO = 0x7a |
||||
|
keybow.CUT = 0x7b |
||||
|
keybow.COPY = 0x7c |
||||
|
keybow.PASTE = 0x7d |
||||
|
keybow.FIND = 0x7e |
||||
|
keybow.MUTE = 0x7f |
||||
|
keybow.VOLUMEUP = 0x80 |
||||
|
keybow.VOLUMEDOWN = 0x81 |
||||
|
|
||||
|
keybow.MEDIA_PLAYPAUSE = 0xe8 |
||||
|
keybow.MEDIA_STOPCD = 0xe9 |
||||
|
keybow.MEDIA_PREVIOUSSONG = 0xea |
||||
|
keybow.MEDIA_NEXTSONG = 0xeb |
||||
|
keybow.MEDIA_EJECTCD = 0xec |
||||
|
keybow.MEDIA_VOLUMEUP = 0xed |
||||
|
keybow.MEDIA_VOLUMEDOWN = 0xee |
||||
|
keybow.MEDIA_MUTE = 0xef |
||||
|
keybow.MEDIA_WWW = 0xf0 |
||||
|
keybow.MEDIA_BACK = 0xf1 |
||||
|
keybow.MEDIA_FORWARD = 0xf2 |
||||
|
keybow.MEDIA_STOP = 0xf3 |
||||
|
keybow.MEDIA_FIND = 0xf4 |
||||
|
keybow.MEDIA_SCROLLUP = 0xf5 |
||||
|
keybow.MEDIA_SCROLLDOWN = 0xf6 |
||||
|
keybow.MEDIA_EDIT = 0xf7 |
||||
|
keybow.MEDIA_SLEEP = 0xf8 |
||||
|
keybow.MEDIA_COFFEE = 0xf9 |
||||
|
keybow.MEDIA_REFRESH = 0xfa |
||||
|
keybow.MEDIA_CALC = 0xfb |
@ -0,0 +1,176 @@ |
|||||
|
--[[ |
||||
|
Copyright 2019 Harald Albrecht |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
||||
|
]]-- |
||||
|
|
||||
|
require "keybow" |
||||
|
|
||||
|
mb = {} |
||||
|
mb.path = (...):match("^(.-)[^%/]+$") |
||||
|
|
||||
|
require(mb.path .. "morekeys") |
||||
|
require(mb.path .. "routehandlers") |
||||
|
|
||||
|
|
||||
|
mb.brightness = 0.4 |
||||
|
mb.keymaps = {} |
||||
|
mb.permanent_keymaps = {} |
||||
|
mb.current_keymap_set = nil |
||||
|
mb.current_keymap_idx = 0 |
||||
|
|
||||
|
|
||||
|
-- |
||||
|
function mb.tap(keyno, key, ...) |
||||
|
for modifier_argno = 1, select('#', ...) do |
||||
|
local modifier = select(modifier_argno, ...) |
||||
|
if modifier then; keybow.set_modifier(modifier, keybow.KEY_DOWN); end |
||||
|
end |
||||
|
keybow.tap_key(key) |
||||
|
for modifier_argno = 1, select('#', ...) do |
||||
|
local modifier = select(modifier_argno, ...) |
||||
|
if modifier then; keybow.set_modifier(modifier, keybow.KEY_UP); end |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Registers a keymap by name (and optional) index. The name is simply used |
||||
|
-- for allowing multiple SHIFT (modifier) levels for the same keyboard layout. |
||||
|
-- The indices are then used to differentiate between different SHIFT/modifier |
||||
|
-- levels of the same keyboard layout. |
||||
|
-- |
||||
|
-- Indices start at 0, this is the un-SHIFT-ed keymap for a layout. |
||||
|
-- |
||||
|
-- If this is the first keymap getting registered, then it will also made |
||||
|
-- activated. |
||||
|
function mb.register_keymap(keymap, name, index) |
||||
|
-- register |
||||
|
local kms = mb.keymaps[name] |
||||
|
kms = kms and kms or {} -- if name isn't known yet, create new keymap array for name |
||||
|
local index = index and #kms or 0 -- appends keymap if index is nil |
||||
|
kms[index] = keymap |
||||
|
mb.keymaps[name] = kms |
||||
|
-- ensure that first registered keymap also automatically gets activated |
||||
|
-- (albeit the LEDs will only update later). |
||||
|
if mb.current_keymap_set == nil then |
||||
|
mb.current_keymap_set = kms |
||||
|
mb.current_keymap_idx = index |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Cycles through the available (non-permanent) keymaps. When switching to |
||||
|
-- the next keymap, we will always activate the index 0 layout. |
||||
|
function mb.cycle_keymaps() |
||||
|
local first_kms |
||||
|
local first_name |
||||
|
local next = false |
||||
|
local next_kms |
||||
|
local next_name |
||||
|
for name, kms in pairs(mb.keymaps) do |
||||
|
if first_kms == nil then |
||||
|
first_kms = kms; first_name = name |
||||
|
end |
||||
|
if kms == mb.current_keymap_set then |
||||
|
next = true |
||||
|
elseif next then |
||||
|
next_kms = kms; next_name = name |
||||
|
next = false |
||||
|
end |
||||
|
end |
||||
|
if next_kms == nil then |
||||
|
next_kms = first_kms; next_name = first_name |
||||
|
end |
||||
|
mb.activate_keymap(next_name, 0) |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Activates a specific keymap by name and index. |
||||
|
function mb.activate_keymap(name, index) |
||||
|
print("activate_keymap", name, index) |
||||
|
local kms = mb.keymaps[name] |
||||
|
mb.current_keymap_set = kms |
||||
|
mb.current_keymap_idx = index |
||||
|
keybow.clear_lights() |
||||
|
mb.activate_leds() |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Registers a permanent keymap (as opposed to switchable keymaps). As their |
||||
|
-- name suggest, permanent keymaps are permanently active and have priority |
||||
|
-- over any "standard" activated keymap. As they are permanent, there is no |
||||
|
-- "index" sub-layout mechanism, but instead all permanent keymaps are always |
||||
|
-- active. If permanent keymaps define overlapping keys, then the result is |
||||
|
-- undefined. |
||||
|
function mb.register_permanent_keymap(keymap, name) |
||||
|
-- register |
||||
|
mb.permanent_keymaps[name] = keymap |
||||
|
mb.activate_leds() |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Sets the Keybow key LEDs maximum brightness, in the range [0.1..1]. |
||||
|
function mb.set_brightness(brightness) |
||||
|
if brightness < 0.1 then; brightness = 0.1; end |
||||
|
if brightness > 1 then; brightness = 1; end |
||||
|
mb.brightness = brightness |
||||
|
mb.activate_leds() |
||||
|
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 ~= nil then |
||||
|
keybow.set_pixel(keyno, |
||||
|
color.r * mb.brightness * 255, color.g * mb.brightness * 255, color.b * mb.brightness * 255) |
||||
|
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() |
||||
|
-- current keymap |
||||
|
if mb.current_keymap_set ~= nil then |
||||
|
for keyno, keydef in pairs(mb.current_keymap_set[mb.current_keymap_idx]) do |
||||
|
print("LED", keyno) |
||||
|
mb.led(keyno, keydef.c) |
||||
|
end |
||||
|
end |
||||
|
-- permanent keymap(s) |
||||
|
for name, pkm in pairs(mb.permanent_keymaps) do |
||||
|
for keyno, keydef in pairs(pkm) do |
||||
|
print("pLED", keyno) |
||||
|
mb.led(keyno, keydef.c) |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
|
||||
|
-- Disables the automatic Keybow lightshow and sets the key LED colors. |
||||
|
function setup() |
||||
|
-- Disables the automatic keybow lightshow and switches all key LEDs off |
||||
|
-- because the LEDs might be in a random state after power on. |
||||
|
keybow.auto_lights(false) |
||||
|
keybow.clear_lights() |
||||
|
mb.activate_leds() |
||||
|
end |
@ -0,0 +1,63 @@ |
|||||
|
--[[ |
||||
|
Copyright 2019 Harald Albrecht |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
||||
|
]]-- |
||||
|
|
||||
|
-- This all-key, central key router forwards Keybow key events to |
||||
|
-- their correct handlers, depending on which keyboard layout currently |
||||
|
-- is active. |
||||
|
function mb.route(keyno, pressed) |
||||
|
-- Check key in permanent keymaps |
||||
|
local keydef |
||||
|
for name, pkm in pairs(mb.permanent_keymaps) do |
||||
|
keydef = pkm[keyno] |
||||
|
if keydef ~= nil then; break; end |
||||
|
end |
||||
|
-- Check key in current keymap |
||||
|
if keydef == nil and mb.current_keymap_set ~= nil then |
||||
|
keydef = mb.current_keymap_set[mb.current_keymap_idx][keyno] |
||||
|
end |
||||
|
|
||||
|
if keydef == nil then; return; end |
||||
|
|
||||
|
if pressed then |
||||
|
for led = 0, 11 do |
||||
|
if led ~= keyno then; mb.led(led, {r=0, g=0, b=0}); end |
||||
|
end |
||||
|
print("route to key #", keyno) |
||||
|
keydef.h(keyno) |
||||
|
else |
||||
|
mb.activate_leds() |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
-- Routes all keybow key handling through our central key router |
||||
|
function handle_key_00(pressed); mb.route(0, pressed); end |
||||
|
function handle_key_01(pressed); mb.route(1, pressed); end |
||||
|
function handle_key_02(pressed); mb.route(2, pressed); end |
||||
|
function handle_key_03(pressed); mb.route(3, pressed); end |
||||
|
function handle_key_04(pressed); mb.route(4, pressed); end |
||||
|
function handle_key_05(pressed); mb.route(5, pressed); end |
||||
|
function handle_key_06(pressed); mb.route(6, pressed); end |
||||
|
function handle_key_07(pressed); mb.route(7, pressed); end |
||||
|
function handle_key_08(pressed); mb.route(8, pressed); end |
||||
|
function handle_key_09(pressed); mb.route(9, pressed); end |
||||
|
function handle_key_10(pressed); mb.route(10, pressed); end |
||||
|
function handle_key_11(pressed); mb.route(11, pressed); end |
@ -0,0 +1,30 @@ |
|||||
|
package.path = "./sdcard/?.lua;./mock/?.lua;" .. package.path |
||||
|
|
||||
|
require "mockkeybow" |
||||
|
|
||||
|
print() |
||||
|
print("**** key 00...") |
||||
|
handle_key_00(true) |
||||
|
handle_key_00(false) |
||||
|
print() |
||||
|
print("**** key 03...") |
||||
|
handle_key_03(true) |
||||
|
handle_key_03(false) |
||||
|
print() |
||||
|
|
||||
|
print("**** key 11...") |
||||
|
handle_key_11(true) |
||||
|
handle_key_11(false) |
||||
|
|
||||
|
print("**** key 00...") |
||||
|
handle_key_00(true) |
||||
|
handle_key_00(false) |
||||
|
|
||||
|
print() |
||||
|
print("**** key 11...") |
||||
|
handle_key_11(true) |
||||
|
handle_key_11(false) |
||||
|
|
||||
|
print("**** key 00...") |
||||
|
handle_key_00(true) |
||||
|
handle_key_00(false) |
Loading…
Reference in new issue