initial version of multibow
This commit is contained in:
commit
59850fd49b
|
@ -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()
|
Binary file not shown.
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