Compare commits
32 Commits
topic-memo
...
refactor-s
Author | SHA1 | Date | |
---|---|---|---|
|
d7e43f7d0d | ||
|
ec6bb9564b | ||
|
17f2961c0b | ||
|
0fbba96026 | ||
|
2f0134eed3 | ||
|
75f535c977 | ||
|
af3febc469 | ||
|
9431ce5ec1 | ||
|
03c8a61ed0 | ||
|
711114ad30 | ||
|
d965a5c3e8 | ||
|
218c15e322 | ||
|
565ec8353b | ||
|
5efd2688d7 | ||
|
91032e123f | ||
|
54a1d61a06 | ||
|
a97e655d08 | ||
|
f2fb4eecf4 | ||
|
63fd4d9574 | ||
|
002a57b3b1 | ||
|
dd488686c7 | ||
|
891cd1a67f | ||
|
ea3f59340d | ||
|
85b5aba679 | ||
|
44233bbb01 | ||
|
0909c6767d | ||
|
a21bf761ba | ||
|
01cd9d4a7c | ||
|
3ab11179e1 | ||
|
352bf25c0d | ||
|
9c85ea9b6b | ||
|
5f09ba7c0a |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -180,3 +180,5 @@ cython_debug/
|
|||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Mac Finder
|
||||||
|
.DS_Store
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import board
|
import board
|
||||||
|
|
||||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||||
|
from kmk.quickpin.pro_micro.boardsource_blok import pinout as pins
|
||||||
from kmk.scanners import DiodeOrientation
|
from kmk.scanners import DiodeOrientation
|
||||||
|
|
||||||
|
|
||||||
class KMKKeyboard(_KMKKeyboard):
|
class KMKKeyboard(_KMKKeyboard):
|
||||||
row_pins = (board.P1_15, board.P0_02, board.P0_29)
|
row_pins = (pins[16], pins[17], pins[18])
|
||||||
col_pins = (board.P0_09, board.P0_10, board.P1_11, board.P1_13)
|
col_pins = (pins[12], pins[13], pins[14], pins[15])
|
||||||
diode_orientation = DiodeOrientation.COLUMNS
|
diode_orientation = DiodeOrientation.COLUMNS
|
||||||
i2c = board.I2C
|
i2c = board.I2C
|
||||||
powersave_pin = board.P0_13
|
|
||||||
|
@@ -1,30 +1,35 @@
|
|||||||
import board
|
import board
|
||||||
|
|
||||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||||
|
from kmk.quickpin.pro_micro.boardsource_blok import pinout as pins
|
||||||
from kmk.scanners import DiodeOrientation
|
from kmk.scanners import DiodeOrientation
|
||||||
|
|
||||||
|
|
||||||
class KMKKeyboard(_KMKKeyboard):
|
class KMKKeyboard(_KMKKeyboard):
|
||||||
col_pins = (
|
col_pins = (
|
||||||
board.P0_31,
|
pins[19],
|
||||||
board.P0_29,
|
pins[18],
|
||||||
board.P0_02,
|
pins[17],
|
||||||
board.P1_15,
|
pins[16],
|
||||||
board.P1_13,
|
pins[15],
|
||||||
board.P1_11,
|
pins[14],
|
||||||
|
)
|
||||||
|
row_pins = (
|
||||||
|
pins[6],
|
||||||
|
pins[7],
|
||||||
|
pins[8],
|
||||||
|
pins[9],
|
||||||
)
|
)
|
||||||
row_pins = (board.P0_22, board.P0_24, board.P1_00, board.P0_11)
|
|
||||||
diode_orientation = DiodeOrientation.COLUMNS
|
diode_orientation = DiodeOrientation.COLUMNS
|
||||||
data_pin = board.P0_08
|
data_pin = pins[1]
|
||||||
rgb_pixel_pin = board.P0_06
|
rgb_pixel_pin = pins[0]
|
||||||
i2c = board.I2C
|
i2c = board.I2C
|
||||||
powersave_pin = board.P0_13
|
|
||||||
|
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
# fmt: off
|
||||||
coord_mapping = [
|
coord_mapping = [
|
||||||
0, 1, 2, 3, 4, 5, 29, 28, 27, 26, 25, 24,
|
0, 1, 2, 3, 4, 5, 29, 28, 27, 26, 25, 24,
|
||||||
6, 7, 8, 9, 10, 11, 35, 34, 33, 32, 31, 30,
|
6, 7, 8, 9, 10, 11, 35, 34, 33, 32, 31, 30,
|
||||||
12, 13, 14, 15, 16, 17, 41, 40, 39, 38, 37, 36,
|
12, 13, 14, 15, 16, 17, 41, 40, 39, 38, 37, 36,
|
||||||
21, 22, 23, 47, 46, 45,
|
21, 22, 23, 47, 46, 45,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
import board
|
|
||||||
|
|
||||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
|
||||||
from kmk.scanners import DiodeOrientation
|
|
||||||
|
|
||||||
|
|
||||||
class KMKKeyboard(_KMKKeyboard):
|
|
||||||
col_pins = (
|
|
||||||
board.A3,
|
|
||||||
board.A2,
|
|
||||||
board.A1,
|
|
||||||
board.A0,
|
|
||||||
board.SCK,
|
|
||||||
board.MISO,
|
|
||||||
)
|
|
||||||
row_pins = (board.D4, board.D5, board.D6, board.D7)
|
|
||||||
diode_orientation = DiodeOrientation.COLUMNS
|
|
||||||
data_pin = board.RX
|
|
||||||
rgb_pixel_pin = board.D0
|
|
||||||
i2c = board.I2C
|
|
||||||
|
|
||||||
# flake8: noqa
|
|
||||||
coord_mapping = [
|
|
||||||
0, 1, 2, 3, 4, 5, 29, 28, 27, 26, 25, 24,
|
|
||||||
6, 7, 8, 9, 10, 11, 35, 34, 33, 32, 31, 30,
|
|
||||||
12, 13, 14, 15, 16, 17, 41, 40, 39, 38, 37, 36,
|
|
||||||
21, 22, 23, 47, 46, 45,
|
|
||||||
]
|
|
@@ -41,8 +41,9 @@ from kmk.extensions.rgb import RGB, AnimationModes
|
|||||||
from kmk.kmk_keyboard import KMKKeyboard
|
from kmk.kmk_keyboard import KMKKeyboard
|
||||||
from kmk.scanners.keypad import KeysScanner
|
from kmk.scanners.keypad import KeysScanner
|
||||||
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
|
||||||
|
|
||||||
def raspi_pins():
|
def raspi_pins():
|
||||||
return [
|
return [
|
||||||
board.D20, board.D16, board.D26,
|
board.D20, board.D16, board.D26,
|
||||||
@@ -52,6 +53,15 @@ def raspi_pins():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def rp2040_pins():
|
||||||
|
return [
|
||||||
|
board.GP7, board.GP8, board.GP27,
|
||||||
|
board.GP9, board.GP26, board.GP10,
|
||||||
|
board.GP11, board.GP18, board.GP12,
|
||||||
|
board.GP16, board.GP17, board.GP14
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def itsybitsy_pins():
|
def itsybitsy_pins():
|
||||||
return [
|
return [
|
||||||
board.D11, board.D12, board.D2,
|
board.D11, board.D12, board.D2,
|
||||||
@@ -59,6 +69,7 @@ def itsybitsy_pins():
|
|||||||
board.A5, board.A4, board.A3,
|
board.A5, board.A4, board.A3,
|
||||||
board.A2, board.A1, board.A0,
|
board.A2, board.A1, board.A0,
|
||||||
]
|
]
|
||||||
|
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
@@ -66,9 +77,17 @@ def isPi():
|
|||||||
return sys.platform == 'BROADCOM'
|
return sys.platform == 'BROADCOM'
|
||||||
|
|
||||||
|
|
||||||
|
def isRP2040():
|
||||||
|
return sys.platform == 'RP2040'
|
||||||
|
|
||||||
|
|
||||||
if isPi():
|
if isPi():
|
||||||
_KEY_CFG = raspi_pins()
|
_KEY_CFG = raspi_pins()
|
||||||
_LED_PINS = (board.SCK, board.MOSI)
|
_LED_PINS = (board.SCK, board.MOSI)
|
||||||
|
elif isRP2040():
|
||||||
|
_KEY_CFG = rp2040_pins()
|
||||||
|
_LED_PINS = (board.GP2, board.GP3)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_KEY_CFG = itsybitsy_pins()
|
_KEY_CFG = itsybitsy_pins()
|
||||||
_LED_PINS = (board.SCK, board.MOSI)
|
_LED_PINS = (board.SCK, board.MOSI)
|
||||||
|
15
boards/zodiark/README.md
Normal file
15
boards/zodiark/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Zodiark
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A split keyboard with 5x7 including a thumbcluster, encoders on each side, per
|
||||||
|
key RGB, and 2x I2C headers per side, supporting 1.3"/.96" 128x64 OLEDs (the
|
||||||
|
1.3" is an SSH1106 OLED, .91" 128x32 OLEDs.
|
||||||
|
|
||||||
|
Hardware Availability: Pending Group Buy - [Discord Link](https://discord.gg/BCSbXwskVt)
|
||||||
|
|
||||||
|
Extensions enabled by default
|
||||||
|
- [Split](/docs/split.md) Connects halves using a wire.
|
||||||
|
- [Layers](/docs/layers.md) Need more keys than switches? Use layers.
|
||||||
|
- [PEG_RGB](/docs/peg_rgb_matrix.md) Light it up!
|
||||||
|
- [PEG_OLED](/docs/peg_oled_display.md) Screens to see things on of course.
|
68
boards/zodiark/kb.py
Normal file
68
boards/zodiark/kb.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||||
|
from kmk.scanners import DiodeOrientation
|
||||||
|
from kmk.scanners.keypad import MatrixScanner
|
||||||
|
from kmk.quickpin.pro_micro.boardsource_blok import pinout as pins
|
||||||
|
from kmk.quickpin.pro_Micro.avr_promicro import avr
|
||||||
|
|
||||||
|
|
||||||
|
class KMKKeyboard(_KMKKeyboard):
|
||||||
|
def __init__(self):
|
||||||
|
# create and register the scanner
|
||||||
|
self.matrix = [
|
||||||
|
MatrixScanner(
|
||||||
|
# required arguments:
|
||||||
|
column_pins=self.col_pins,
|
||||||
|
row_pins=self.row_pins,
|
||||||
|
# optional arguments with defaults:
|
||||||
|
columns_to_anodes=DiodeOrientation.COL2ROW,
|
||||||
|
interval=0.02,
|
||||||
|
max_events=64,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
col_pins = (
|
||||||
|
pins[avr['F5']],
|
||||||
|
pins[avr['F6']],
|
||||||
|
pins[avr['F7']],
|
||||||
|
pins[avr['B1']],
|
||||||
|
pins[avr['B3']],
|
||||||
|
pins[avr['B2']],
|
||||||
|
pins[avr['B6']],
|
||||||
|
)
|
||||||
|
row_pins = (
|
||||||
|
pins[avr['C6']],
|
||||||
|
pins[avr['D7']],
|
||||||
|
pins[avr['E6']],
|
||||||
|
pins[avr['B4']],
|
||||||
|
pins[avr['F4']],
|
||||||
|
)
|
||||||
|
diode_orientation = DiodeOrientation.COLUMNS
|
||||||
|
rgb_pixel_pin = pins[avr['B5']]
|
||||||
|
data_pin = pins[avr['D3']]
|
||||||
|
i2c = board.I2C
|
||||||
|
SCL = board.SCL
|
||||||
|
SDA = board.SDA
|
||||||
|
# NOQA
|
||||||
|
# flake8: noqa
|
||||||
|
# fmt: off
|
||||||
|
led_key_pos =[
|
||||||
|
5, 4, 3, 2, 01, 00, 34, 35, 36, 37, 38, 39,
|
||||||
|
6, 7, 8, 9, 10, 11, 12, 46, 45, 44, 43, 42, 41, 40,
|
||||||
|
19, 18, 17, 16, 15, 14, 13, 47, 48, 49, 50, 51, 52, 53,
|
||||||
|
20, 21, 22, 23, 24, 25, 26, 60, 59, 58, 57, 56, 55, 54,
|
||||||
|
33, 32, 31, 30, 29, 28, 27, 61, 62, 63, 64, 65, 66, 67
|
||||||
|
]
|
||||||
|
brightness_limit = 0.5
|
||||||
|
num_pixels = 62
|
||||||
|
|
||||||
|
# NOQA
|
||||||
|
# flake8: noqa
|
||||||
|
coord_mapping = [
|
||||||
|
0, 1, 2, 3, 4, 5, 40, 39, 38, 37, 36, 35,
|
||||||
|
7, 8, 9, 10, 11, 12, 06, 41, 47, 46, 45, 44, 43, 42,
|
||||||
|
14, 15, 16, 17, 18, 19, 13, 48, 54, 53, 52, 51, 50, 49,
|
||||||
|
21, 22, 23, 24, 25, 26, 20, 27, 62, 55, 61, 60, 59, 58, 57, 56,
|
||||||
|
28, 29, 30, 31, 32, 33, 34, 69, 68, 67, 66, 65, 64, 63
|
||||||
|
]
|
709
boards/zodiark/main.py
Normal file
709
boards/zodiark/main.py
Normal file
@@ -0,0 +1,709 @@
|
|||||||
|
import supervisor
|
||||||
|
|
||||||
|
from kb import KMKKeyboard
|
||||||
|
|
||||||
|
from kmk.extensions.peg_oled_Display import (
|
||||||
|
Oled,
|
||||||
|
OledData,
|
||||||
|
OledDisplayMode,
|
||||||
|
OledReactionType,
|
||||||
|
)
|
||||||
|
from kmk.extensions.peg_rgb_matrix import Rgb_matrix
|
||||||
|
from kmk.handlers.sequences import send_string
|
||||||
|
from kmk.hid import HIDModes
|
||||||
|
from kmk.keys import KC
|
||||||
|
from kmk.modules.layers import Layers
|
||||||
|
from kmk.modules.modtap import ModTap
|
||||||
|
from kmk.modules.split import Split, SplitSide, SplitType
|
||||||
|
|
||||||
|
keyboard = KMKKeyboard()
|
||||||
|
modtap = ModTap()
|
||||||
|
layers_ext = Layers()
|
||||||
|
keyboard.modules.append(layers_ext)
|
||||||
|
keyboard.modules.append(modtap)
|
||||||
|
|
||||||
|
oled_ext = Oled(
|
||||||
|
OledData(
|
||||||
|
corner_one={0: OledReactionType.STATIC, 1: ['qwertyzzzz']},
|
||||||
|
corner_two={
|
||||||
|
0: OledReactionType.LAYER,
|
||||||
|
1: ['1', '2', '3', '4', '5', '6', '7', '8'],
|
||||||
|
},
|
||||||
|
corner_three={
|
||||||
|
0: OledReactionType.LAYER,
|
||||||
|
1: ['base', 'raise', 'lower', 'adjust', '5', '6', '7', '8'],
|
||||||
|
},
|
||||||
|
corner_four={
|
||||||
|
0: OledReactionType.LAYER,
|
||||||
|
1: ['qwertyzzz', 'nums', 'shifted', 'leds', '5', '6', '7', '8'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
toDisplay=OledDisplayMode.TXT,
|
||||||
|
flip=False,
|
||||||
|
)
|
||||||
|
keyboard.extensions.append(oled_ext)
|
||||||
|
|
||||||
|
# Default RGB matrix colours
|
||||||
|
rgb_ext = Rgb_matrix(
|
||||||
|
ledDisplay=[
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
[80, 0, 80],
|
||||||
|
],
|
||||||
|
split=True,
|
||||||
|
rightSide=False,
|
||||||
|
disable_auto_write=True,
|
||||||
|
)
|
||||||
|
keyboard.extensions.append(rgb_ext)
|
||||||
|
|
||||||
|
# TODO Comment one of these on each side
|
||||||
|
split_side = SplitSide.LEFT
|
||||||
|
# split_side = SplitSide.RIGHT
|
||||||
|
split = Split(data_pin=keyboard.data_pin)
|
||||||
|
keyboard.modules.append(split)
|
||||||
|
|
||||||
|
keyboard.keymap = [
|
||||||
|
[
|
||||||
|
KC.F1,
|
||||||
|
KC.F2,
|
||||||
|
KC.F3,
|
||||||
|
KC.F4,
|
||||||
|
KC.F5,
|
||||||
|
KC.F6,
|
||||||
|
KC.F7,
|
||||||
|
KC.F8,
|
||||||
|
KC.F9,
|
||||||
|
KC.F10,
|
||||||
|
KC.F11,
|
||||||
|
KC.F12,
|
||||||
|
KC.ESC,
|
||||||
|
KC.N1,
|
||||||
|
KC.N2,
|
||||||
|
KC.N3,
|
||||||
|
KC.N4,
|
||||||
|
KC.N5,
|
||||||
|
KC.N6,
|
||||||
|
KC.N7,
|
||||||
|
KC.N8,
|
||||||
|
KC.N9,
|
||||||
|
KC.N0,
|
||||||
|
KC.GRV,
|
||||||
|
KC.TAB,
|
||||||
|
KC.Q,
|
||||||
|
KC.W,
|
||||||
|
KC.E,
|
||||||
|
KC.R,
|
||||||
|
KC.T,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
KC.Y,
|
||||||
|
KC.U,
|
||||||
|
KC.I,
|
||||||
|
KC.O,
|
||||||
|
KC.P,
|
||||||
|
KC.MINS,
|
||||||
|
KC.LCTL,
|
||||||
|
KC.A,
|
||||||
|
KC.S,
|
||||||
|
KC.D,
|
||||||
|
KC.F,
|
||||||
|
KC.G,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
KC.H,
|
||||||
|
KC.J,
|
||||||
|
KC.K,
|
||||||
|
KC.L,
|
||||||
|
KC.SCLN,
|
||||||
|
KC.QUOT,
|
||||||
|
KC.LSFT,
|
||||||
|
KC.Z,
|
||||||
|
KC.X,
|
||||||
|
KC.C,
|
||||||
|
KC.V,
|
||||||
|
KC.B,
|
||||||
|
KC.LBRC,
|
||||||
|
KC.RBRC,
|
||||||
|
KC.N,
|
||||||
|
KC.M,
|
||||||
|
KC.COMMA,
|
||||||
|
KC.DOT,
|
||||||
|
KC.SLSH,
|
||||||
|
KC.RSFT,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
KC.LGUI,
|
||||||
|
KC.MO(1),
|
||||||
|
KC.LCTL,
|
||||||
|
KC.SPC,
|
||||||
|
KC.ENT,
|
||||||
|
KC.MO(2),
|
||||||
|
KC.BSPC,
|
||||||
|
KC.RGUI,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
KC.NO,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.F1,
|
||||||
|
KC.F2,
|
||||||
|
KC.F3,
|
||||||
|
KC.F4,
|
||||||
|
KC.F5,
|
||||||
|
KC.F6,
|
||||||
|
KC.F7,
|
||||||
|
KC.F8,
|
||||||
|
KC.F9,
|
||||||
|
KC.F10,
|
||||||
|
KC.F11,
|
||||||
|
KC.F12,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.UP,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.EQL,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.LEFT,
|
||||||
|
KC.DOWN,
|
||||||
|
KC.RGHT,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.LEFT,
|
||||||
|
KC.DOWN,
|
||||||
|
KC.RGHT,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.DEL,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.N2,
|
||||||
|
KC.EXLM,
|
||||||
|
KC.AT,
|
||||||
|
KC.HASH,
|
||||||
|
KC.DLR,
|
||||||
|
KC.PERC,
|
||||||
|
KC.CIRC,
|
||||||
|
KC.AMPR,
|
||||||
|
KC.ASTR,
|
||||||
|
KC.LPRN,
|
||||||
|
KC.RPRN,
|
||||||
|
KC.TILD,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.PLUS,
|
||||||
|
KC.UNDS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.LCBR,
|
||||||
|
KC.RCBR,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.LABK,
|
||||||
|
KC.RABK,
|
||||||
|
KC.QUES,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
KC.TRNS,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
if __name__ == '__main__':
|
||||||
|
keyboard.go(hid_type=HIDModes.USB)
|
21
docs/boot.md
21
docs/boot.md
@@ -38,18 +38,21 @@ import storage
|
|||||||
import usb_cdc
|
import usb_cdc
|
||||||
import usb_hid
|
import usb_hid
|
||||||
|
|
||||||
# This is from the base kmk boot.py
|
from kb import KMKKeyboard
|
||||||
supervisor.set_next_stack_limit(4096 + 4096)
|
from kmk.scanners import DiodeOrientation
|
||||||
|
|
||||||
|
|
||||||
# If this key is held during boot, don't run the code which hides the storage and disables serial
|
# If this key is held during boot, don't run the code which hides the storage and disables serial
|
||||||
# To use another key just count its row and column and use those pins
|
# This will use the first row/col pin. Feel free to change it if you want it to be another pin
|
||||||
# You can also use any other pins not already used in the matrix and make a button just for accesing your storage
|
col = digitalio.DigitalInOut(KMKKeyboard.col_pins[0])
|
||||||
col = digitalio.DigitalInOut(board.GP2)
|
row = digitalio.DigitalInOut(KMKKeyboard.row_pins[0])
|
||||||
row = digitalio.DigitalInOut(board.GP13)
|
|
||||||
|
|
||||||
# TODO: If your diode orientation is ROW2COL, then make row the output and col the input
|
if KMKKeyboard.diode_orientation == DiodeOrientation.COLUMNS:
|
||||||
col.switch_to_output(value=True)
|
col.switch_to_output(value=True)
|
||||||
row.switch_to_input(pull=digitalio.Pull.DOWN)
|
row.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||||
|
else:
|
||||||
|
col.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||||
|
row.switch_to_output(value=True)
|
||||||
|
|
||||||
if not row.value:
|
if not row.value:
|
||||||
storage.disable_usb_drive()
|
storage.disable_usb_drive()
|
||||||
|
@@ -27,6 +27,7 @@ Optional arguments that customize individual combos:
|
|||||||
* `per_key_timeout`: If True, reset timeout on every key press (default for
|
* `per_key_timeout`: If True, reset timeout on every key press (default for
|
||||||
sequences).
|
sequences).
|
||||||
* `timeout`: Set the time window within which the match has to happen in ms.
|
* `timeout`: Set the time window within which the match has to happen in ms.
|
||||||
|
* `match_coord`: If True, matches key position in the matrix.
|
||||||
|
|
||||||
## Example Code
|
## Example Code
|
||||||
```python
|
```python
|
||||||
@@ -43,6 +44,8 @@ make_key(
|
|||||||
combos.combos = [
|
combos.combos = [
|
||||||
Chord((KC.A, KC.B), KC.LSFT),
|
Chord((KC.A, KC.B), KC.LSFT),
|
||||||
Chord((KC.A, KC.B, KC.C), KC.LALT),
|
Chord((KC.A, KC.B, KC.C), KC.LALT),
|
||||||
|
Chord((0, 1), KC.ESC, match_coord=True),
|
||||||
|
Chord((8, 9, 10), KC.MO(4), match_coord=True),
|
||||||
Sequence((KC.LEADER, KC.A, KC.B), KC.C),
|
Sequence((KC.LEADER, KC.A, KC.B), KC.C),
|
||||||
Sequence((KC.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
|
Sequence((KC.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
|
||||||
]
|
]
|
||||||
|
BIN
docs/img/pro_micro_pinout.png
Normal file
BIN
docs/img/pro_micro_pinout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 400 KiB |
@@ -68,3 +68,26 @@ keyboard.keymap = [
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Advanced Example
|
||||||
|
A common question is: "How do I change RGB background based on my active layer?"
|
||||||
|
Here is _one_ (simple) way of many to go about it.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from kmk.modules.layers import Layers as _Layers
|
||||||
|
from kmk.extensions.rgb import RGB
|
||||||
|
|
||||||
|
rgb = RGB(...) # your RGB configuration goes here
|
||||||
|
keyboard.extensions.append(rgb)
|
||||||
|
|
||||||
|
class Layers(_Layers):
|
||||||
|
last_top_layer = 0
|
||||||
|
hues = (4, 20, 69)
|
||||||
|
|
||||||
|
def after_hid_send(keyboard):
|
||||||
|
if keyboard.active_layers[0] != self.last_top_layer:
|
||||||
|
self.last_top_layer = keyboard.active_layers[0]
|
||||||
|
rgb.set_hsv_fill(self.hues[self.last_top_layer], 255, 255)
|
||||||
|
|
||||||
|
keyboard.modules.append(Layers())
|
||||||
|
```
|
||||||
|
@@ -14,7 +14,7 @@ keyboard.extensions.append(MediaKeys())
|
|||||||
|`KC.AUDIO_MUTE` |`KC.MUTE` |Mute |
|
|`KC.AUDIO_MUTE` |`KC.MUTE` |Mute |
|
||||||
|`KC.AUDIO_VOL_UP` |`KC.VOLU` |Volume Up |
|
|`KC.AUDIO_VOL_UP` |`KC.VOLU` |Volume Up |
|
||||||
|`KC.AUDIO_VOL_DOWN` |`KC.VOLD` |Volume Down |
|
|`KC.AUDIO_VOL_DOWN` |`KC.VOLD` |Volume Down |
|
||||||
|`KC.BRIGHTESS_UP` |`KC.BRIU` |Brightness Up |
|
|`KC.BRIGHTNESS_UP` |`KC.BRIU` |Brightness Up |
|
||||||
|`KC.BRIGHTNESS_DOWN` |`KC.BRID` |Brightness Down |
|
|`KC.BRIGHTNESS_DOWN` |`KC.BRID` |Brightness Down |
|
||||||
|`KC.MEDIA_NEXT_TRACK` |`KC.MNXT` |Next Track (Windows) |
|
|`KC.MEDIA_NEXT_TRACK` |`KC.MNXT` |Next Track (Windows) |
|
||||||
|`KC.MEDIA_PREV_TRACK` |`KC.MPRV` |Previous Track (Windows) |
|
|`KC.MEDIA_PREV_TRACK` |`KC.MPRV` |Previous Track (Windows) |
|
||||||
|
@@ -31,7 +31,7 @@ keyboard.modules.append(modtap)
|
|||||||
## Custom HoldTap Behavior
|
## Custom HoldTap Behavior
|
||||||
The full ModTap signature is as follows:
|
The full ModTap signature is as follows:
|
||||||
```python
|
```python
|
||||||
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None)
|
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=False)
|
||||||
```
|
```
|
||||||
* `prefer_hold`: decides which keycode the ModTap key resolves to when another
|
* `prefer_hold`: decides which keycode the ModTap key resolves to when another
|
||||||
key is pressed before the timeout finishes. When `True` the hold keycode is
|
key is pressed before the timeout finishes. When `True` the hold keycode is
|
||||||
@@ -40,5 +40,11 @@ KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None)
|
|||||||
key press/down, or after the first other key up/release. Set to `True` for
|
key press/down, or after the first other key up/release. Set to `True` for
|
||||||
interrupt on release.
|
interrupt on release.
|
||||||
* `tap_time`: length of the tap timeout in milliseconds.
|
* `tap_time`: length of the tap timeout in milliseconds.
|
||||||
|
* `repeat`: decides how to interpret repeated presses if they happen within
|
||||||
|
`tap_time` after a release.
|
||||||
|
When `True` the repeated press sends the previous keycode, no matter
|
||||||
|
how long the key remains pressed the second time.
|
||||||
|
This allows to hold the tap keycode, or repeatedly tap the hold keycode.
|
||||||
|
When `False` repeated presses are independent.
|
||||||
|
|
||||||
Each of these parameters can be set for every ModTap key individually.
|
Each of these parameters can be set for every ModTap key individually.
|
||||||
|
@@ -1,44 +1,55 @@
|
|||||||
# Peg RGB Matrix
|
# Peg RGB Matrix
|
||||||
|
|
||||||
### What you can and cannot do with this extension:
|
## What you can and cannot do with this extension:
|
||||||
|
|
||||||
|
### Can Do
|
||||||
|
|
||||||
#### Can Do
|
|
||||||
* Set any key's LED to be any color in a syntax very similar to your keymap
|
* Set any key's LED to be any color in a syntax very similar to your keymap
|
||||||
* Allows specific keys to be set to OFF
|
* Allows specific keys to be set to OFF
|
||||||
* Allows underglow LEDs to be a different color than per-key LEDs
|
* Allows underglow LEDs to be a different color than per-key LEDs
|
||||||
* Allows modifier keys to be set to a different color than alpha keys
|
* Allows modifier keys to be set to a different color than alpha keys
|
||||||
* Full split keyboard support
|
* Full split keyboard support
|
||||||
|
* Change brightness of LEDs from code or using keycodes
|
||||||
|
|
||||||
|
### Cannot Do (currently in progress)
|
||||||
|
|
||||||
#### Cannot Do (currently in progress)
|
|
||||||
* Adjust color at runtime. Currently the extension requires changes to main.py in order to make changes to your LEDs.
|
* Adjust color at runtime. Currently the extension requires changes to main.py in order to make changes to your LEDs.
|
||||||
* Animations
|
* Animations
|
||||||
* Change LED color based on current layer
|
* Change LED color based on current layer
|
||||||
|
|
||||||
### Keycodes
|
## Keycodes
|
||||||
Currently this extension does not support changing LEDs at runtime, as a result there is only a single keycode available to interact with this extension and that is KC.RGB_TOG. This keycode simply toggles all your LEDs on and off.
|
|
||||||
|
Currently this extension does not support changing LEDs at runtime, as a result there are only three keycodes available to interact with this extension,those are:
|
||||||
|
|
||||||
|
* `KC.RGB_TOG`. This keycode simply toggles all your LEDs on and off.
|
||||||
|
* `KC.RGB_BRI`. This keycode increases the brightness of the LEDs.
|
||||||
|
* `KC.RGB_BRD`. This keycode decreases the brightness of the LEDs.
|
||||||
|
|
||||||
## Required Libraries
|
## Required Libraries
|
||||||
|
|
||||||
The following libraries must be frozen in your CircuitPython distribution or in a 'lib' folder at the root of your drive.
|
The following libraries must be frozen in your CircuitPython distribution or in a 'lib' folder at the root of your drive.
|
||||||
|
|
||||||
* [Adafruit_CircuitPython_NeoPixel](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel)
|
* [Adafruit_CircuitPython_NeoPixel](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel)
|
||||||
* [Download .mpy versions from here](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20220415/adafruit-circuitpython-bundle-7.x-mpy-20220415.zip)
|
* [Download .mpy versions from here](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20220415/adafruit-circuitpython-bundle-7.x-mpy-20220415.zip)
|
||||||
|
|
||||||
# Required Changes to main.py and kb.py
|
## Required Changes to main.py and kb.py
|
||||||
In order to use this extension the user must make changes to both their kb.py and main.py files. Below you will find a more comprehensive list of changes required in order to use this extension.
|
|
||||||
|
In order to use this extension the user must make changes to both their kb.py and main.py files. Below you will find a more comprehensive list of changes required in order to use this extension.
|
||||||
|
|
||||||
|
### kb.py
|
||||||
|
|
||||||
## kb.py
|
|
||||||
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
||||||
|
|
||||||
The board's kb.py needs 3 fields:
|
The board's kb.py needs 3 fields:
|
||||||
* LED Key Position `led_key_pos`
|
|
||||||
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
|
* LED Key Position `led_key_pos`
|
||||||
|
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
|
||||||
* Brightness Limit `brightness_limit`
|
* Brightness Limit `brightness_limit`
|
||||||
* Limits your brightness and may be required in order to stabilize performance.
|
* Limits your brightness and may be required in order to stabilize performance.
|
||||||
* Number of LEDs `num_pixels`
|
* Number of LEDs `num_pixels`
|
||||||
* Used for calculations in order to ensure the LEDs map to the correct keys.
|
* Used for calculations in order to ensure the LEDs map to the correct keys.
|
||||||
|
|
||||||
|
#### Non-split Example:
|
||||||
### Non-split Example:
|
|
||||||
|
|
||||||
Below shows a simple non-split example for a board containing 48 LEDs total and 38 keys with per-key LEDs.
|
Below shows a simple non-split example for a board containing 48 LEDs total and 38 keys with per-key LEDs.
|
||||||
This means we will have 10 underglow LEDs and 38 per-key LEDs.
|
This means we will have 10 underglow LEDs and 38 per-key LEDs.
|
||||||
@@ -62,11 +73,10 @@ Underglow LEDs always appear at the end of the `led_key_pos` array, because the
|
|||||||
num_pixels = 48
|
num_pixels = 48
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Split Example:
|
||||||
|
|
||||||
### Split Example:
|
Below shows a 58 key split keyboard's `led_key_pos` array for a board containing 70 LEDs in total.
|
||||||
|
The board has 58 keys, meaning we are left with 12 underglow LEDs total.
|
||||||
Below shows a 58 key split keyboard's `led_key_pos` array for a board containing 70 LEDs in total.
|
|
||||||
The board has 58 keys, meaning we are left with 12 underglow LEDs total.
|
|
||||||
Since the board is a split and we can assume the LEDs are mirrored, that means each half has 29 per-key LEDs and 6 underglow LEDs.
|
Since the board is a split and we can assume the LEDs are mirrored, that means each half has 29 per-key LEDs and 6 underglow LEDs.
|
||||||
|
|
||||||
Let's first focus on the left half of the board.
|
Let's first focus on the left half of the board.
|
||||||
@@ -95,8 +105,8 @@ Underglow LEDs always appear at the end of the `led_key_pos` array, because the
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### main.py
|
||||||
|
|
||||||
## main.py
|
|
||||||
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -111,38 +121,41 @@ Rgb_matrix extension requires one argument (`Rgb_matrix_data`), although additio
|
|||||||
Rgb_matrix:
|
Rgb_matrix:
|
||||||
|
|
||||||
* LED Display `ledDisplay`
|
* LED Display `ledDisplay`
|
||||||
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
|
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
|
||||||
* Rgb_matrix_data only takes two fields:
|
* Rgb_matrix_data only takes two fields:
|
||||||
* Keys: an array of colors with a length equal to the number of keys on your keyboard
|
* Keys: an array of colors with a length equal to the number of keys on your keyboard
|
||||||
* Underglow: an array of colors with a length equal to the number of underglow leds on your keyboard
|
* Underglow: an array of colors with a length equal to the number of underglow leds on your keyboard
|
||||||
* Split `split`
|
* Split `split`
|
||||||
* This is an optional boolean and only to be used if the keyboard is a split.
|
* This is an optional boolean and only to be used if the keyboard is a split.
|
||||||
* Right Side `rightSide`
|
* Right Side `rightSide`
|
||||||
* This is optional boolean only to be used if the keyboard is split. This signals that this configuration is targetting the right side (off side).
|
* This is optional boolean only to be used if the keyboard is split. This signals that this configuration is targetting the right side (off side).
|
||||||
* RGB Order `rgb_order`
|
* RGB Order `rgb_order`
|
||||||
* This is optional and only needs to be set if you are not using a WS2812 based LED.
|
* This is optional and only needs to be set if you are not using a WS2812 based LED.
|
||||||
* Disable Auto Write `disable_auto_write`
|
* Disable Auto Write `disable_auto_write`
|
||||||
* This is optional and only serves to make all your LEDs turn on at once instead of animate to their on state.
|
* This is optional and only serves to make all your LEDs turn on at once instead of animate to their on state.
|
||||||
|
|
||||||
### Colors
|
### Colors
|
||||||
Colors are RGB and can be provided in one of two ways.
|
|
||||||
|
Colors are RGB and can be provided in one of two ways.
|
||||||
Colors can be defined as an array of three numbers (0-255) or you can use the `Color` class with its default colors, see example below.
|
Colors can be defined as an array of three numbers (0-255) or you can use the `Color` class with its default colors, see example below.
|
||||||
|
|
||||||
### Passing RGB Codes
|
#### Passing RGB Codes
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Rgb_matrix_data(
|
Rgb_matrix_data(
|
||||||
keys=[[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],"""... rest of colors""" ],
|
keys=[[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],"""... rest of colors""" ],
|
||||||
underglow=[[0,0,55],[0,0,55],"""... rest of colors""" ]
|
underglow=[[0,0,55],[0,0,55],"""... rest of colors""" ]
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Using `Color` Class
|
||||||
|
|
||||||
### Using `Color` Class
|
|
||||||
```python
|
```python
|
||||||
Rgb_matrix_data(
|
Rgb_matrix_data(
|
||||||
keys=[Color.RED, Color.GREEN, Color.BLUE, Color.WHITE, Color.YELLOW, Color.ORANGE,"""... rest of colors""" ],
|
keys=[Color.RED, Color.GREEN, Color.BLUE, Color.WHITE, Color.YELLOW, Color.ORANGE,"""... rest of colors""" ],
|
||||||
underglow=[Color.PURPLE, Color.TEAL, Color.PINK, Color.OFF,"""... rest of colors""" ]
|
underglow=[Color.PURPLE, Color.TEAL, Color.PINK, Color.OFF,"""... rest of colors""" ]
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Full Examples
|
### Full Examples
|
||||||
|
|
||||||
@@ -163,7 +176,8 @@ rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_data(
|
|||||||
disable_auto_write=True)
|
disable_auto_write=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Bonus
|
#### Bonus
|
||||||
|
|
||||||
Because creating `ledDisplay` can be time consuming, there is a utility avaiable that will generate a basic framework for you.
|
Because creating `ledDisplay` can be time consuming, there is a utility avaiable that will generate a basic framework for you.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -173,7 +187,8 @@ Rgb_matrix_data.generate_led_map(58,10,Color.WHITE,Color.BLUE)
|
|||||||
Call `Rgb_matrix_data.generate_led_map` before you do any configuration beyond imports and it will print an `Rgb_matrix_data` class to your CircuitPython REPL which you can view by using a tool like "screen" or "PUTTY".
|
Call `Rgb_matrix_data.generate_led_map` before you do any configuration beyond imports and it will print an `Rgb_matrix_data` class to your CircuitPython REPL which you can view by using a tool like "screen" or "PUTTY".
|
||||||
|
|
||||||
Generate LED Map Arguments:
|
Generate LED Map Arguments:
|
||||||
* Number of Keys
|
|
||||||
|
* Number of Keys
|
||||||
* Number of Underglow
|
* Number of Underglow
|
||||||
* Key Color
|
* Key Color
|
||||||
* Underglow Color
|
* Underglow Color
|
||||||
@@ -184,6 +199,5 @@ Example Using Above Arguments:
|
|||||||
Rgb_matrix_data(keys=[[249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249]],
|
Rgb_matrix_data(keys=[[249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249]],
|
||||||
underglow=[[0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255]])
|
underglow=[[0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255]])
|
||||||
```
|
```
|
||||||
|
|
||||||
[Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console)
|
[Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console)
|
||||||
|
|
||||||
|
|
||||||
|
103
docs/quickpin.md
Normal file
103
docs/quickpin.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Quickpin
|
||||||
|
|
||||||
|
Quickpin helps devs quickly translate pinouts between boards of similar footprint.
|
||||||
|
This lets you write a single `kb.py` that can be swapped between
|
||||||
|
microcontrollers with only a single line change and less mistakes.
|
||||||
|
|
||||||
|
## Supported footprints/boards
|
||||||
|
|
||||||
|
- Pro micro footprint
|
||||||
|
- Sparkfun Pro micro RP2040
|
||||||
|
- Boardsource Blok
|
||||||
|
- Nice!nano
|
||||||
|
|
||||||
|
## Pro micro footprint pinout
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
In this example, we are converting a Boardsource 3x4 from a hard pinned
|
||||||
|
nice!nano to a controller agnostic pinout.
|
||||||
|
|
||||||
|
```python
|
||||||
|
row_pins = (board.P1_15, board.P0_02, board.P0_29)
|
||||||
|
col_pins = (board.P0_09, board.P0_10, board.P1_11, board.P1_13)
|
||||||
|
```
|
||||||
|
|
||||||
|
Converts to the following. Notice that `nice_nano` can be subbed for
|
||||||
|
`boardsource_blok` or `sparkfun_promicro_rp2040`, or any other board sharing
|
||||||
|
this pinout.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from kmk.quickpin.pro_micro.nice_nano import pinout as pins
|
||||||
|
|
||||||
|
row_pins = (pins[16], pins[17], pins[18])
|
||||||
|
col_pins = (pins[12], pins[13], pins[14], pins[15])
|
||||||
|
```
|
||||||
|
|
||||||
|
## Porting from AVR pro micro
|
||||||
|
|
||||||
|
An additional added convenience for translating from other firmwares with AVR
|
||||||
|
pro micros has also been added to speed up porting.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from kmk.quickpin.pro_micro.nice_nano import pinout as pins
|
||||||
|
from kmk.quickpin.pro_Micro.avr_promicro import avr
|
||||||
|
|
||||||
|
row_pins = (
|
||||||
|
pins[avr['F7']],
|
||||||
|
pins[avr['F6']],
|
||||||
|
pins[avr['F5']],
|
||||||
|
)
|
||||||
|
col_pins = (
|
||||||
|
pins[avr['B6']],
|
||||||
|
pins[avr['B2']],
|
||||||
|
pins[avr['B3']],
|
||||||
|
pins[avr['B1']],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Adding boards to quickpin support
|
||||||
|
|
||||||
|
Quickpin format is simply a list of pins in order of all through hole pins,
|
||||||
|
going anticlockwise starting at the top left. The orientation should be with the
|
||||||
|
chips facing toward you, with USB facing the top. If this isn't appliable, or
|
||||||
|
otherwise is not true, it should be stated in a comment in the file. Any pin
|
||||||
|
that is not addressable in software should be left as `None` to fill the space,
|
||||||
|
and align pins correctly for all boards. All boards should be stored in
|
||||||
|
`kmk/quickpin/<footprint>/boardname.py`.
|
||||||
|
|
||||||
|
Pro Micro RP2040 shown as an example:
|
||||||
|
```python
|
||||||
|
import board
|
||||||
|
|
||||||
|
pinout = [
|
||||||
|
board.TX,
|
||||||
|
board.RX,
|
||||||
|
None, # GND
|
||||||
|
None, # GND
|
||||||
|
board.D2,
|
||||||
|
board.D3,
|
||||||
|
board.D4,
|
||||||
|
board.D5,
|
||||||
|
board.D6,
|
||||||
|
board.D7,
|
||||||
|
board.D8,
|
||||||
|
board.D9,
|
||||||
|
board.D21,
|
||||||
|
board.MOSI,
|
||||||
|
board.MISO,
|
||||||
|
board.SCK,
|
||||||
|
board.D26,
|
||||||
|
board.D27,
|
||||||
|
board.D28,
|
||||||
|
board.D29,
|
||||||
|
None, # 3.3v
|
||||||
|
None, # RST
|
||||||
|
None, # GND
|
||||||
|
None, # RAW
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
87
docs/rgb.md
87
docs/rgb.md
@@ -4,7 +4,7 @@ Want your keyboard to shine? Add some lights!
|
|||||||
## CircuitPython
|
## CircuitPython
|
||||||
This does require the [NeoPixel library from Adafruit](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel/blob/main/neopixel.py).
|
This does require the [NeoPixel library from Adafruit](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel/blob/main/neopixel.py).
|
||||||
It is part of the [Adafruit CircuitPython Bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle).
|
It is part of the [Adafruit CircuitPython Bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle).
|
||||||
Simply put this in the "root" of your CircuitPython device. If unsure, it's the folder with main.py in it, and should be the first folder you see when you open the device.
|
Simply put this in the "root" of your CircuitPython device. If unsure, it's the folder with `main.py` in it, and should be the first folder you see when you open the device.
|
||||||
|
|
||||||
Currently we support the following addressable LEDs:
|
Currently we support the following addressable LEDs:
|
||||||
|
|
||||||
@@ -20,13 +20,13 @@ Changing the **Saturation** moves between the inner and outer sections of the wh
|
|||||||
Changing the **Value** sets the overall brightness.
|
Changing the **Value** sets the overall brightness.
|
||||||
|
|
||||||
## Enabling the extension
|
## Enabling the extension
|
||||||
The only required values that you need to give the RGB extension would be the pixel pin, and the number of pixels/LED's. If using a split keyboard, this number is per side, and not the total of both sides.
|
The only required values that you need to give the RGB extension would be the board pin for the data line, and the number of pixels/LED's. If using a split keyboard, this number is per side, and not the total of both sides.
|
||||||
```python
|
```python
|
||||||
|
import board
|
||||||
from kmk.extensions.RGB import RGB
|
from kmk.extensions.RGB import RGB
|
||||||
from kb import rgb_pixel_pin # This can be imported or defined manually
|
|
||||||
|
|
||||||
rgb_ext = RGB(pixel_pin=rgb_pixel_pin, num_pixels=27)
|
rgb = RGB(pixel_pin=board.GP14, num_pixels=27)
|
||||||
keyboard.extensions.append(rgb_ext)
|
keyboard.extensions.append(rgb)
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Keycodes]
|
## [Keycodes]
|
||||||
@@ -52,22 +52,21 @@ keyboard.extensions.append(rgb_ext)
|
|||||||
## Configuration
|
## Configuration
|
||||||
|Define |Default |Description |
|
|Define |Default |Description |
|
||||||
|-------------------------------------|-------------|-----------------------------------------------------------------------------|
|
|-------------------------------------|-------------|-----------------------------------------------------------------------------|
|
||||||
|`keyboard.pixel_pin` | |The pin connected to the data pin of the LEDs|
|
|`rgb.num_pixels`| |The number of LEDs connected |
|
||||||
|`keyboard.num_pixels`| |The number of LEDs connected |
|
|`rgb.rgb_order` |`(1, 0, 2)` |The order of the pixels R G B, and optionally white. Example(1, 0, 2, 3) |
|
||||||
|`keyboard.rgb_config['rgb_order']` |`(1, 0, 2)` |The order of the pixels R G B, and optionally white. Example(1, 0, 2, 3) |
|
|`rgb.hue_step` |`10` |The number of steps to cycle through the hue by |
|
||||||
|`keyboard.rgb_config['hue_step']` |`10` |The number of steps to cycle through the hue by |
|
|`rgb.sat_step` |`17` |The number of steps to change the saturation by |
|
||||||
|`keyboard.rgb_config['sat_step']` |`17` |The number of steps to change the saturation by |
|
|`rgb.val_step` |`17` |The number of steps to change the brightness by |
|
||||||
|`keyboard.rgb_config['val_step']` |`17` |The number of steps to change the brightness by |
|
|`rgb.hue_default` |`0` |The default hue when the keyboard boots |
|
||||||
|`keyboard.rgb_config['hue_default']` |`0` |The default hue when the keyboard boots |
|
|`rgb.sat_default` |`255` |The default saturation when the keyboard boots |
|
||||||
|`keyboard.rgb_config['sat_default']` |`100` |The default saturation when the keyboard boots |
|
|`rgb.val_default` |`255` |The default value (brightness) when the keyboard boots |
|
||||||
|`keyboard.rgb_config['val_default']` |`100` |The default value (brightness) when the keyboard boots |
|
|`rgb.val_limit` |`255` |The maximum brightness level |
|
||||||
|`keyboard.rgb_config['val_limit']` |`255` |The maximum brightness level |
|
|
||||||
|
|
||||||
## Built-in Animation Configuration
|
## Built-in Animation Configuration
|
||||||
|Define |Default |Description |
|
|Define |Default |Description |
|
||||||
|----------------------------------------------|-------------|-------------------------------------------------------------------------------------|
|
|----------------------------------------------|-------------|-------------------------------------------------------------------------------------|
|
||||||
|`keyboard.rgb_config['breathe_center']` |`1.5` |Used to calculate the curve for the breathing animation. Anywhere from 1.0 - 2.7 is valid|
|
|`rgb.breathe_center` |`1.5` |Used to calculate the curve for the breathing animation. Anywhere from 1.0 - 2.7 is valid|
|
||||||
|`keyboard.rgb_config['knight_effect_length']` |`4` |The number of LEDs to light up for the "Knight" animation |
|
|`rgb.knight_effect_length` |`4` |The number of LEDs to light up for the "Knight" animation |
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
@@ -75,38 +74,36 @@ If you want to create your own animations, or for example, change the lighting i
|
|||||||
|
|
||||||
|Function |Description |
|
|Function |Description |
|
||||||
|--------------------------------------------------|--------------------------------------------------------------------------------------------|
|
|--------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||||
|`keyboard.pixels.set_hsv_fill(hue, sat, val)` |Fills all LED's with HSV values |
|
|`rgb.set_hsv_fill(hue, sat, val)` |Fills all LED's with HSV values |
|
||||||
|`keyboard.pixels.set_hsv(hue, sat, val, index)` |Sets a single LED with HSV value |
|
|`rgb.set_hsv(hue, sat, val, index)` |Sets a single LED with HSV value |
|
||||||
|`keyboard.pixels.set_rgb_fill((r, g, b))` |Fills all LED's with RGB(W) values |
|
|`rgb.set_rgb_fill((r, g, b))` |Fills all LED's with RGB(W) values |
|
||||||
|`keyboard.pixels.set_rgb((r, g, b), index)` |Set's a single LED with RGB(W) values |
|
|`rgb.set_rgb((r, g, b), index)` |Set's a single LED with RGB(W) values |
|
||||||
|`keyboard.pixels.disable_auto_write(bool)` |When True, disables showing changes. Good for setting multiple LED's before a visible update|
|
|`rgb.disable_auto_write(bool)` |When True, disables showing changes. Good for setting multiple LED's before a visible update|
|
||||||
|`keyboard.pixels.increase_hue(step)` |Increases hue by a given step |
|
|`rgb.increase_hue(step)` |Increases hue by a given step |
|
||||||
|`keyboard.pixels.decrease_hue(step)` |Decreases hue by a given step |
|
|`rgb.decrease_hue(step)` |Decreases hue by a given step |
|
||||||
|`keyboard.pixels.increase_sat(step)` |Increases saturation by a given step |
|
|`rgb.increase_sat(step)` |Increases saturation by a given step |
|
||||||
|`keyboard.pixels.decrease_sat(step)` |Decreases saturation by a given step |
|
|`rgb.decrease_sat(step)` |Decreases saturation by a given step |
|
||||||
|`keyboard.pixels.increase_val(step)` |Increases value (brightness) by a given step |
|
|`rgb.increase_val(step)` |Increases value (brightness) by a given step |
|
||||||
|`keyboard.pixels.decrease_val(step)` |Decreases value (brightness) by a given step |
|
|`rgb.decrease_val(step)` |Decreases value (brightness) by a given step |
|
||||||
|`keyboard.pixels.increase_ani()` |Increases animation speed by 1. Maximum 10 |
|
|`rgb.increase_ani()` |Increases animation speed by 1. Maximum 10 |
|
||||||
|`keyboard.pixels.decrease_ani()` |Decreases animation speed by 1. Minimum 10 |
|
|`rgb.decrease_ani()` |Decreases animation speed by 1. Minimum 10 |
|
||||||
|`keyboard.pixels.off()` |Turns all LED's off |
|
|`rgb.off()` |Turns all LED's off |
|
||||||
|`keyboard.pixels.show()` |Displays all stored configuration for LED's. Useful with disable_auto_write explained above |
|
|`rgb.show()` |Displays all stored configuration for LED's. Useful with disable_auto_write explained above |
|
||||||
|`keyboard.pixels.time_ms()` |Returns a time in ms since the board has booted. Useful for start/stop timers |
|
|
||||||
|
|
||||||
## Direct variable access
|
## Direct variable access
|
||||||
|Define |Default |Description |
|
|Define |Default |Description |
|
||||||
|-----------------------------------|-----------|-----------------------------------------------------------------------------------------------------------|
|
|-----------------------------------|-----------|-----------------------------------------------------------------------------------------------------------|
|
||||||
|`keyboard.pixels.hue` |`0` |Sets the hue from 0-360 |
|
|`rgb.hue` |`0` |Sets the hue from 0-255 |
|
||||||
|`keyboard.pixels.sat` |`100` |Sets the saturation from 0-100 |
|
|`rgb.sat` |`255` |Sets the saturation from 0-255 |
|
||||||
|`keyboard.pixels.val` |`80` |Sets the brightness from 1-255 |
|
|`rgb.val` |`255` |Sets the brightness from 0-255 |
|
||||||
|`keyboard.pixels.reverse_animation`|`False` |If true, some animations will run in reverse. Can be safely used in user animations |
|
|`rgb.reverse_animation`|`False` |If true, some animations will run in reverse. Can be safely used in user animations |
|
||||||
|`keyboard.pixels.animation_mode` |`static` |This can be changed to any modes included, or to something custom for user animations. Any string is valid |
|
|`rgb.animation_mode` |`static` |This can be changed to any modes included, or to something custom for user animations. Any string is valid |
|
||||||
|`keyboard.pixels.animation_speed` |`1` |Increases animation speed of most animations. Recommended 1-5, Maximum 10. |
|
|`rgb.animation_speed` |`1` |Increases animation speed of most animations. Recommended 1-5, Maximum 10. |
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from kmk.extensions.rgb import AnimationModes
|
from kmk.extensions.rgb import AnimationModes
|
||||||
rgb_ext = RGB(pixel_pin=rgb_pixel_pin,
|
rgb = RGB(pixel_pin=rgb_pixel_pin,
|
||||||
num_pixels=27
|
num_pixels=27
|
||||||
num_pixels=0,
|
|
||||||
val_limit=100,
|
val_limit=100,
|
||||||
hue_default=0,
|
hue_default=0,
|
||||||
sat_default=100,
|
sat_default=100,
|
||||||
@@ -169,8 +166,8 @@ from kb import rgb_pixel_pin # This can be imported or defined manually
|
|||||||
_LED_COUNT=12
|
_LED_COUNT=12
|
||||||
pixels = adafruit_dotstar.DotStar(board.SCK, board.MOSI, _LED_COUNT)
|
pixels = adafruit_dotstar.DotStar(board.SCK, board.MOSI, _LED_COUNT)
|
||||||
|
|
||||||
rgb_ext = RGB(pixel_pin=None, pixels=pixels)
|
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||||
keyboard.extensions.append(rgb_ext)
|
keyboard.extensions.append(rgb)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple PixelBuffer
|
### Multiple PixelBuffer
|
||||||
@@ -185,6 +182,6 @@ pixels = (
|
|||||||
CustomPixelBuf(...)
|
CustomPixelBuf(...)
|
||||||
)
|
)
|
||||||
|
|
||||||
rgb_ext = RGB(pixel_pin=None, pixels=pixels)
|
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||||
keyboard.extensions.append(rgb_ext)
|
keyboard.extensions.append(rgb)
|
||||||
```
|
```
|
||||||
|
@@ -29,23 +29,23 @@ use it to add things like copying/pasting, tabbing between fields, etc.
|
|||||||
from kmk.handlers.sequences import simple_key_sequence
|
from kmk.handlers.sequences import simple_key_sequence
|
||||||
|
|
||||||
PASTE_WITH_COMMENTARY = simple_key_sequence(
|
PASTE_WITH_COMMENTARY = simple_key_sequence(
|
||||||
(
|
(
|
||||||
KC.L,
|
KC.L,
|
||||||
KC.O,
|
KC.O,
|
||||||
KC.O,
|
KC.O,
|
||||||
KC.K,
|
KC.K,
|
||||||
KC.SPC,
|
KC.SPC,
|
||||||
KC.A,
|
KC.A,
|
||||||
KC.T,
|
KC.T,
|
||||||
KC.SPC,
|
KC.SPC,
|
||||||
KC.T,
|
KC.T,
|
||||||
KC.H,
|
KC.H,
|
||||||
KC.I,
|
KC.I,
|
||||||
KC.S,
|
KC.S,
|
||||||
KC.COLN,
|
KC.COLN,
|
||||||
KC.SPC,
|
KC.SPC,
|
||||||
KC.LCTL(KC.V),
|
KC.LCTL(KC.V),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.keymap = [<other keycodes>, PASTE_WITH_COMMENTARY, <other keycodes>]
|
keyboard.keymap = [<other keycodes>, PASTE_WITH_COMMENTARY, <other keycodes>]
|
||||||
@@ -64,23 +64,35 @@ length of time, in milliseconds.
|
|||||||
from kmk.handlers.sequences import simple_key_sequence
|
from kmk.handlers.sequences import simple_key_sequence
|
||||||
|
|
||||||
COUNTDOWN_TO_PASTE = simple_key_sequence(
|
COUNTDOWN_TO_PASTE = simple_key_sequence(
|
||||||
(
|
(
|
||||||
KC.N3,
|
KC.N3,
|
||||||
KC.ENTER,
|
KC.ENTER,
|
||||||
KC.MACRO_SLEEP_MS(1000),
|
KC.MACRO_SLEEP_MS(1000),
|
||||||
KC.N2,
|
KC.N2,
|
||||||
KC.ENTER,
|
KC.ENTER,
|
||||||
KC.MACRO_SLEEP_MS(1000),
|
KC.MACRO_SLEEP_MS(1000),
|
||||||
KC.N1,
|
KC.N1,
|
||||||
KC.ENTER,
|
KC.ENTER,
|
||||||
KC.MACRO_SLEEP(1000),
|
KC.MACRO_SLEEP(1000),
|
||||||
KC.LCTL(KC.V),
|
KC.LCTL(KC.V),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.keymap = [<other keycodes>, COUNTDOWN_TO_PASTE, <other keycodes>]
|
keyboard.keymap = [<other keycodes>, COUNTDOWN_TO_PASTE, <other keycodes>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
from kmk.handlers.sequences import simple_key_sequence
|
||||||
|
|
||||||
|
NEXT = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.LALT(no_release=True),
|
||||||
|
KC.MACRO_SLEEP_MS(30),
|
||||||
|
KC.TAB,
|
||||||
|
KC.MACRO_SLEEP_MS(30),
|
||||||
|
KC.LALT(no_press=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
This example will type out the following, waiting one second (1000 ms) between numbers:
|
This example will type out the following, waiting one second (1000 ms) between numbers:
|
||||||
|
|
||||||
3
|
3
|
||||||
@@ -89,6 +101,24 @@ This example will type out the following, waiting one second (1000 ms) between n
|
|||||||
|
|
||||||
and then paste the contents of your clipboard.
|
and then paste the contents of your clipboard.
|
||||||
|
|
||||||
|
### Alt Tab with delay
|
||||||
|
|
||||||
|
If alt tab isn't working because it requires a delay, adding a delay and triggering
|
||||||
|
down and up on ALT manually may fix the issue.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
from kmk.handlers.sequences import simple_key_sequence
|
||||||
|
|
||||||
|
NEXT = simple_key_sequence(
|
||||||
|
(
|
||||||
|
KC.LALT(no_release=True),
|
||||||
|
KC.MACRO_SLEEP_MS(30),
|
||||||
|
KC.TAB,
|
||||||
|
KC.MACRO_SLEEP_MS(30),
|
||||||
|
KC.LALT(no_press=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Unicode
|
## Unicode
|
||||||
Before trying to send Unicode sequences, make sure you set your `UnicodeMode`.
|
Before trying to send Unicode sequences, make sure you set your `UnicodeMode`.
|
||||||
@@ -128,8 +158,8 @@ keys), that's supported too, through an obnoxiously long-winded method:
|
|||||||
from kmk.handlers.sequences import compile_unicode_string_sequences as cuss
|
from kmk.handlers.sequences import compile_unicode_string_sequences as cuss
|
||||||
|
|
||||||
emoticons = cuss({
|
emoticons = cuss({
|
||||||
'BEER': r'🍺',
|
'BEER': r'🍺',
|
||||||
'HAND_WAVE': r'👋',
|
'HAND_WAVE': r'👋',
|
||||||
})
|
})
|
||||||
|
|
||||||
keymap = [<other keycodes>, emoticons.BEER, emoticons.HAND_WAVE, <other keycodes>]
|
keymap = [<other keycodes>, emoticons.BEER, emoticons.HAND_WAVE, <other keycodes>]
|
||||||
@@ -147,8 +177,8 @@ supported too, through `unicode_codepoint_sequence`.
|
|||||||
from kmk.handlers.sequences import unicode_codepoint_sequence
|
from kmk.handlers.sequences import unicode_codepoint_sequence
|
||||||
|
|
||||||
TABLE_FLIP = unicode_codepoint_sequence([
|
TABLE_FLIP = unicode_codepoint_sequence([
|
||||||
"28", "30ce", "ca0", "75ca","ca0", "29",
|
"28", "30ce", "ca0", "75ca","ca0", "29",
|
||||||
"30ce", "5f61", "253b", "2501", "253b",
|
"30ce", "5f61", "253b", "2501", "253b",
|
||||||
])
|
])
|
||||||
|
|
||||||
keyboard.keymap = [<other keycodes>, TABLE_FLIP, <other keycodes>]
|
keyboard.keymap = [<other keycodes>, TABLE_FLIP, <other keycodes>]
|
||||||
|
BIN
kmk/.DS_Store
vendored
BIN
kmk/.DS_Store
vendored
Binary file not shown.
@@ -61,7 +61,6 @@ class Oled(Extension):
|
|||||||
|
|
||||||
def renderOledTextLayer(self, layer):
|
def renderOledTextLayer(self, layer):
|
||||||
splash = displayio.Group()
|
splash = displayio.Group()
|
||||||
self._display.show(splash)
|
|
||||||
splash.append(
|
splash.append(
|
||||||
label.Label(
|
label.Label(
|
||||||
terminalio.FONT,
|
terminalio.FONT,
|
||||||
@@ -98,16 +97,17 @@ class Oled(Extension):
|
|||||||
y=25,
|
y=25,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self._display.show(splash)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
def renderOledImgLayer(self, layer):
|
def renderOledImgLayer(self, layer):
|
||||||
splash = displayio.Group()
|
splash = displayio.Group()
|
||||||
self._display.show(splash)
|
|
||||||
odb = displayio.OnDiskBitmap(
|
odb = displayio.OnDiskBitmap(
|
||||||
'/' + self.returnCurrectRenderText(layer, self._views[0])
|
'/' + self.returnCurrectRenderText(layer, self._views[0])
|
||||||
)
|
)
|
||||||
image = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
|
image = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
|
||||||
splash.append(image)
|
splash.append(image)
|
||||||
|
self._display.show(splash)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
def updateOLED(self, sandbox):
|
def updateOLED(self, sandbox):
|
||||||
|
@@ -9,11 +9,15 @@ from kmk.keys import make_key
|
|||||||
|
|
||||||
class Color:
|
class Color:
|
||||||
OFF = [0, 0, 0]
|
OFF = [0, 0, 0]
|
||||||
|
BLACK = OFF
|
||||||
WHITE = [249, 249, 249]
|
WHITE = [249, 249, 249]
|
||||||
RED = [255, 0, 0]
|
RED = [255, 0, 0]
|
||||||
|
AZURE = [153, 245, 255]
|
||||||
BLUE = [0, 0, 255]
|
BLUE = [0, 0, 255]
|
||||||
|
CYAN = [0, 255, 255]
|
||||||
GREEN = [0, 255, 0]
|
GREEN = [0, 255, 0]
|
||||||
YELLOW = [255, 247, 0]
|
YELLOW = [255, 247, 0]
|
||||||
|
MAGENTA = [255, 0, 255]
|
||||||
ORANGE = [255, 77, 0]
|
ORANGE = [255, 77, 0]
|
||||||
PURPLE = [255, 0, 242]
|
PURPLE = [255, 0, 242]
|
||||||
TEAL = [0, 128, 128]
|
TEAL = [0, 128, 128]
|
||||||
@@ -53,6 +57,9 @@ class Rgb_matrix(Extension):
|
|||||||
self.disable_auto_write = disable_auto_write
|
self.disable_auto_write = disable_auto_write
|
||||||
self.split = split
|
self.split = split
|
||||||
self.rightSide = rightSide
|
self.rightSide = rightSide
|
||||||
|
self.brightness_step = 0.1
|
||||||
|
self.brightness = 0
|
||||||
|
|
||||||
if name.endswith('L'):
|
if name.endswith('L'):
|
||||||
self.rightSide = False
|
self.rightSide = False
|
||||||
elif name.endswith('R'):
|
elif name.endswith('R'):
|
||||||
@@ -65,6 +72,12 @@ class Rgb_matrix(Extension):
|
|||||||
make_key(
|
make_key(
|
||||||
names=('RGB_TOG',), on_press=self._rgb_tog, on_release=handler_passthrough
|
names=('RGB_TOG',), on_press=self._rgb_tog, on_release=handler_passthrough
|
||||||
)
|
)
|
||||||
|
make_key(
|
||||||
|
names=('RGB_BRI',), on_press=self._rgb_bri, on_release=handler_passthrough
|
||||||
|
)
|
||||||
|
make_key(
|
||||||
|
names=('RGB_BRD',), on_press=self._rgb_brd, on_release=handler_passthrough
|
||||||
|
)
|
||||||
|
|
||||||
def _rgb_tog(self, *args, **kwargs):
|
def _rgb_tog(self, *args, **kwargs):
|
||||||
if self.enable:
|
if self.enable:
|
||||||
@@ -73,6 +86,12 @@ class Rgb_matrix(Extension):
|
|||||||
self.on()
|
self.on()
|
||||||
self.enable = not self.enable
|
self.enable = not self.enable
|
||||||
|
|
||||||
|
def _rgb_bri(self, *args, **kwargs):
|
||||||
|
self.increase_brightness()
|
||||||
|
|
||||||
|
def _rgb_brd(self, *args, **kwargs):
|
||||||
|
self.decrease_brightness()
|
||||||
|
|
||||||
def on(self):
|
def on(self):
|
||||||
if self.neopixel:
|
if self.neopixel:
|
||||||
self.setBasedOffDisplay()
|
self.setBasedOffDisplay()
|
||||||
@@ -88,6 +107,34 @@ class Rgb_matrix(Extension):
|
|||||||
if self.disable_auto_write:
|
if self.disable_auto_write:
|
||||||
self.neopixel.show()
|
self.neopixel.show()
|
||||||
|
|
||||||
|
def set_brightness(self, brightness=None):
|
||||||
|
if brightness is None:
|
||||||
|
brightness = self.brightness
|
||||||
|
|
||||||
|
if self.neopixel:
|
||||||
|
self.neopixel.brightness = brightness
|
||||||
|
if self.disable_auto_write:
|
||||||
|
self.neopixel.show()
|
||||||
|
|
||||||
|
def increase_brightness(self, step=None):
|
||||||
|
if step is None:
|
||||||
|
step = self.brightness_step
|
||||||
|
|
||||||
|
self.brightness = (
|
||||||
|
self.brightness + step if self.brightness + step <= 1.0 else 1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
self.set_brightness(self.brightness)
|
||||||
|
|
||||||
|
def decrease_brightness(self, step=None):
|
||||||
|
if step is None:
|
||||||
|
step = self.brightness_step
|
||||||
|
|
||||||
|
self.brightness = (
|
||||||
|
self.brightness - step if self.brightness - step >= 0.0 else 0.0
|
||||||
|
)
|
||||||
|
self.set_brightness(self.brightness)
|
||||||
|
|
||||||
def setBasedOffDisplay(self):
|
def setBasedOffDisplay(self):
|
||||||
if self.split:
|
if self.split:
|
||||||
for i, val in enumerate(self.ledDisplay):
|
for i, val in enumerate(self.ledDisplay):
|
||||||
@@ -121,6 +168,7 @@ class Rgb_matrix(Extension):
|
|||||||
)
|
)
|
||||||
self.num_pixels = board.num_pixels
|
self.num_pixels = board.num_pixels
|
||||||
self.keyPos = board.led_key_pos
|
self.keyPos = board.led_key_pos
|
||||||
|
self.brightness = board.brightness_limit
|
||||||
self.on()
|
self.on()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -137,7 +185,17 @@ class Rgb_matrix(Extension):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def on_powersave_enable(self, sandbox):
|
def on_powersave_enable(self, sandbox):
|
||||||
return
|
if self.neopixel:
|
||||||
|
self.neopixel.brightness = (
|
||||||
|
self.neopixel.brightness / 2
|
||||||
|
if self.neopixel.brightness / 2 > 0
|
||||||
|
else 0.1
|
||||||
|
)
|
||||||
|
if self.disable_auto_write:
|
||||||
|
self.neopixel.show()
|
||||||
|
|
||||||
def on_powersave_disable(self, sandbox):
|
def on_powersave_disable(self, sandbox):
|
||||||
return
|
if self.neopixel:
|
||||||
|
self.neopixel.brightness = self.brightness
|
||||||
|
if self.disable_auto_write:
|
||||||
|
self.neopixel.show()
|
||||||
|
@@ -5,7 +5,9 @@ from kmk.extensions import Extension
|
|||||||
from kmk.handlers.stock import passthrough as handler_passthrough
|
from kmk.handlers.stock import passthrough as handler_passthrough
|
||||||
from kmk.keys import make_key
|
from kmk.keys import make_key
|
||||||
from kmk.kmktime import PeriodicTimer
|
from kmk.kmktime import PeriodicTimer
|
||||||
from kmk.utils import clamp
|
from kmk.utils import Debug, clamp
|
||||||
|
|
||||||
|
debug = Debug(__name__)
|
||||||
|
|
||||||
rgb_config = {}
|
rgb_config = {}
|
||||||
|
|
||||||
@@ -127,6 +129,10 @@ class RGB(Extension):
|
|||||||
for pixels in self.pixels:
|
for pixels in self.pixels:
|
||||||
self.num_pixels += len(pixels)
|
self.num_pixels += len(pixels)
|
||||||
|
|
||||||
|
if debug.enabled:
|
||||||
|
for n, pixels in enumerate(self.pixels):
|
||||||
|
debug(f'pixels[{n}] = {pixels.__class__}[{len(pixels)}]')
|
||||||
|
|
||||||
self.rgbw = bool(len(rgb_order) == 4)
|
self.rgbw = bool(len(rgb_order) == 4)
|
||||||
|
|
||||||
self.hue_step = hue_step
|
self.hue_step = hue_step
|
||||||
@@ -249,6 +255,9 @@ class RGB(Extension):
|
|||||||
:param val:
|
:param val:
|
||||||
:param index: Index of LED/Pixel
|
:param index: Index of LED/Pixel
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
val = clamp(val, 0, self.val_limit)
|
||||||
|
|
||||||
if self.rgbw:
|
if self.rgbw:
|
||||||
self.set_rgb(hsv_to_rgbw(hue, sat, val), index)
|
self.set_rgb(hsv_to_rgbw(hue, sat, val), index)
|
||||||
else:
|
else:
|
||||||
@@ -261,6 +270,9 @@ class RGB(Extension):
|
|||||||
:param sat:
|
:param sat:
|
||||||
:param val:
|
:param val:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
val = clamp(val, 0, self.val_limit)
|
||||||
|
|
||||||
if self.rgbw:
|
if self.rgbw:
|
||||||
self.set_rgb_fill(hsv_to_rgbw(hue, sat, val))
|
self.set_rgb_fill(hsv_to_rgbw(hue, sat, val))
|
||||||
else:
|
else:
|
||||||
|
165
kmk/keys.py
165
kmk/keys.py
@@ -1,4 +1,8 @@
|
|||||||
from gc import collect
|
try:
|
||||||
|
from typing import Callable, Optional, Tuple
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
import kmk.handlers.stock as handlers
|
import kmk.handlers.stock as handlers
|
||||||
@@ -7,13 +11,20 @@ from kmk.key_validators import key_seq_sleep_validator, unicode_mode_key_validat
|
|||||||
from kmk.types import UnicodeModeKeyMeta
|
from kmk.types import UnicodeModeKeyMeta
|
||||||
from kmk.utils import Debug
|
from kmk.utils import Debug
|
||||||
|
|
||||||
|
# Type aliases / forward declaration; can't use the proper types because of circular imports.
|
||||||
|
Keyboard = object
|
||||||
|
Key = object
|
||||||
|
|
||||||
|
|
||||||
|
class KeyType:
|
||||||
|
SIMPLE = const(0)
|
||||||
|
MODIFIER = const(1)
|
||||||
|
CONSUMER = const(2)
|
||||||
|
|
||||||
|
|
||||||
FIRST_KMK_INTERNAL_KEY = const(1000)
|
FIRST_KMK_INTERNAL_KEY = const(1000)
|
||||||
NEXT_AVAILABLE_KEY = 1000
|
NEXT_AVAILABLE_KEY = 1000
|
||||||
|
|
||||||
KEY_SIMPLE = const(0)
|
|
||||||
KEY_MODIFIER = const(1)
|
|
||||||
KEY_CONSUMER = const(2)
|
|
||||||
|
|
||||||
ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
ALL_NUMBERS = '1234567890'
|
ALL_NUMBERS = '1234567890'
|
||||||
# since KC.1 isn't valid Python, alias to KC.N1
|
# since KC.1 isn't valid Python, alias to KC.N1
|
||||||
@@ -22,7 +33,12 @@ ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS)
|
|||||||
debug = Debug(__name__)
|
debug = Debug(__name__)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_key(code, names, *args, **kwargs):
|
def maybe_make_key(
|
||||||
|
code: Optional[int],
|
||||||
|
names: Tuple[str, ...],
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> Callable[[str], Key]:
|
||||||
def closure(candidate):
|
def closure(candidate):
|
||||||
if candidate in names:
|
if candidate in names:
|
||||||
return make_key(code=code, names=names, *args, **kwargs)
|
return make_key(code=code, names=names, *args, **kwargs)
|
||||||
@@ -32,10 +48,10 @@ def maybe_make_key(code, names, *args, **kwargs):
|
|||||||
|
|
||||||
def maybe_make_argumented_key(
|
def maybe_make_argumented_key(
|
||||||
validator=lambda *validator_args, **validator_kwargs: object(),
|
validator=lambda *validator_args, **validator_kwargs: object(),
|
||||||
names=tuple(), # NOQA
|
names: Tuple[str, ...] = tuple(), # NOQA
|
||||||
*constructor_args,
|
*constructor_args,
|
||||||
**constructor_kwargs,
|
**constructor_kwargs,
|
||||||
):
|
) -> Callable[[str], Key]:
|
||||||
def closure(candidate):
|
def closure(candidate):
|
||||||
if candidate in names:
|
if candidate in names:
|
||||||
return make_argumented_key(
|
return make_argumented_key(
|
||||||
@@ -45,7 +61,7 @@ def maybe_make_argumented_key(
|
|||||||
return closure
|
return closure
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_no_key(candidate):
|
def maybe_make_no_key(candidate: str) -> Optional[Key]:
|
||||||
# NO and TRNS are functionally identical in how they (don't) mutate
|
# NO and TRNS are functionally identical in how they (don't) mutate
|
||||||
# the state, but are tracked semantically separately, so create
|
# the state, but are tracked semantically separately, so create
|
||||||
# two keys with the exact same functionality
|
# two keys with the exact same functionality
|
||||||
@@ -63,7 +79,7 @@ def maybe_make_no_key(candidate):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_alpha_key(candidate):
|
def maybe_make_alpha_key(candidate: str) -> Optional[Key]:
|
||||||
if len(candidate) != 1:
|
if len(candidate) != 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -75,7 +91,7 @@ def maybe_make_alpha_key(candidate):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_numeric_key(candidate):
|
def maybe_make_numeric_key(candidate: str) -> Optional[Key]:
|
||||||
if candidate in ALL_NUMBERS or candidate in ALL_NUMBER_ALIASES:
|
if candidate in ALL_NUMBERS or candidate in ALL_NUMBER_ALIASES:
|
||||||
try:
|
try:
|
||||||
offset = ALL_NUMBERS.index(candidate)
|
offset = ALL_NUMBERS.index(candidate)
|
||||||
@@ -88,7 +104,7 @@ def maybe_make_numeric_key(candidate):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_mod_key(candidate):
|
def maybe_make_mod_key(candidate: str) -> Optional[Key]:
|
||||||
# MEH = LCTL | LALT | LSFT
|
# MEH = LCTL | LALT | LSFT
|
||||||
# HYPR = LCTL | LALT | LSFT | LGUI
|
# HYPR = LCTL | LALT | LSFT | LGUI
|
||||||
mods = (
|
mods = (
|
||||||
@@ -106,10 +122,10 @@ def maybe_make_mod_key(candidate):
|
|||||||
|
|
||||||
for code, names in mods:
|
for code, names in mods:
|
||||||
if candidate in names:
|
if candidate in names:
|
||||||
return make_key(code=code, names=names, type=KEY_MODIFIER)
|
return make_key(code=code, names=names, type=KeyType.MODIFIER)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_more_ascii(candidate):
|
def maybe_make_more_ascii(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(40, ('ENTER', 'ENT', '\n')),
|
(40, ('ENTER', 'ENT', '\n')),
|
||||||
(41, ('ESCAPE', 'ESC')),
|
(41, ('ESCAPE', 'ESC')),
|
||||||
@@ -134,7 +150,7 @@ def maybe_make_more_ascii(candidate):
|
|||||||
return make_key(code=code, names=names)
|
return make_key(code=code, names=names)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_fn_key(candidate):
|
def maybe_make_fn_key(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(58, ('F1',)),
|
(58, ('F1',)),
|
||||||
(59, ('F2',)),
|
(59, ('F2',)),
|
||||||
@@ -167,7 +183,7 @@ def maybe_make_fn_key(candidate):
|
|||||||
return make_key(code=code, names=names)
|
return make_key(code=code, names=names)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_navlock_key(candidate):
|
def maybe_make_navlock_key(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(57, ('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS')),
|
(57, ('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS')),
|
||||||
# FIXME: Investigate whether this key actually works, and
|
# FIXME: Investigate whether this key actually works, and
|
||||||
@@ -196,7 +212,7 @@ def maybe_make_navlock_key(candidate):
|
|||||||
return make_key(code=code, names=names)
|
return make_key(code=code, names=names)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_numpad_key(candidate):
|
def maybe_make_numpad_key(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(83, ('NUM_LOCK', 'NUMLOCK', 'NLCK')),
|
(83, ('NUM_LOCK', 'NUMLOCK', 'NLCK')),
|
||||||
(84, ('KP_SLASH', 'NUMPAD_SLASH', 'PSLS')),
|
(84, ('KP_SLASH', 'NUMPAD_SLASH', 'PSLS')),
|
||||||
@@ -225,7 +241,7 @@ def maybe_make_numpad_key(candidate):
|
|||||||
return make_key(code=code, names=names)
|
return make_key(code=code, names=names)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_shifted_key(candidate):
|
def maybe_make_shifted_key(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(30, ('EXCLAIM', 'EXLM', '!')),
|
(30, ('EXCLAIM', 'EXLM', '!')),
|
||||||
(31, ('AT', '@')),
|
(31, ('AT', '@')),
|
||||||
@@ -255,7 +271,7 @@ def maybe_make_shifted_key(candidate):
|
|||||||
return make_key(code=code, names=names, has_modifiers={KC.LSFT.code})
|
return make_key(code=code, names=names, has_modifiers={KC.LSFT.code})
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_international_key(candidate):
|
def maybe_make_international_key(candidate: str) -> Optional[Key]:
|
||||||
codes = (
|
codes = (
|
||||||
(50, ('NONUS_HASH', 'NUHS')),
|
(50, ('NONUS_HASH', 'NUHS')),
|
||||||
(100, ('NONUS_BSLASH', 'NUBS')),
|
(100, ('NONUS_BSLASH', 'NUBS')),
|
||||||
@@ -285,7 +301,7 @@ def maybe_make_international_key(candidate):
|
|||||||
return make_key(code=code, names=names)
|
return make_key(code=code, names=names)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_unicode_key(candidate):
|
def maybe_make_unicode_key(candidate: str) -> Optional[Key]:
|
||||||
keys = (
|
keys = (
|
||||||
(
|
(
|
||||||
('UC_MODE_NOOP', 'UC_DISABLE'),
|
('UC_MODE_NOOP', 'UC_DISABLE'),
|
||||||
@@ -321,7 +337,7 @@ def maybe_make_unicode_key(candidate):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def maybe_make_firmware_key(candidate):
|
def maybe_make_firmware_key(candidate: str) -> Optional[Key]:
|
||||||
keys = (
|
keys = (
|
||||||
((('BLE_REFRESH',), handlers.ble_refresh)),
|
((('BLE_REFRESH',), handlers.ble_refresh)),
|
||||||
((('BOOTLOADER',), handlers.bootloader)),
|
((('BOOTLOADER',), handlers.bootloader)),
|
||||||
@@ -389,13 +405,13 @@ class KeyAttrDict:
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self.__cache.__iter__()
|
return self.__cache.__iter__()
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key: str, value: Key):
|
||||||
self.__cache.__setitem__(key, value)
|
self.__cache.__setitem__(key, value)
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key: Key):
|
||||||
return self.__getitem__(key)
|
return self.__getitem__(key)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key: Key, default: Optional[Key] = None):
|
||||||
try:
|
try:
|
||||||
return self.__getitem__(key)
|
return self.__getitem__(key)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -404,7 +420,7 @@ class KeyAttrDict:
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
self.__cache.clear()
|
self.__cache.clear()
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key: Key):
|
||||||
try:
|
try:
|
||||||
return self.__cache[key]
|
return self.__cache[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -431,13 +447,17 @@ KC = KeyAttrDict()
|
|||||||
class Key:
|
class Key:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
code,
|
code: int,
|
||||||
has_modifiers=None,
|
has_modifiers: Optional[list[Key, ...]] = None,
|
||||||
no_press=False,
|
no_press: bool = False,
|
||||||
no_release=False,
|
no_release: bool = False,
|
||||||
on_press=handlers.default_pressed,
|
on_press: Callable[
|
||||||
on_release=handlers.default_released,
|
[object, Key, Keyboard, ...], None
|
||||||
meta=object(),
|
] = handlers.default_pressed,
|
||||||
|
on_release: Callable[
|
||||||
|
[object, Key, Keyboard, ...], None
|
||||||
|
] = handlers.default_released,
|
||||||
|
meta: object = object(),
|
||||||
):
|
):
|
||||||
self.code = code
|
self.code = code
|
||||||
self.has_modifiers = has_modifiers
|
self.has_modifiers = has_modifiers
|
||||||
@@ -449,7 +469,9 @@ class Key:
|
|||||||
self._handle_release = on_release
|
self._handle_release = on_release
|
||||||
self.meta = meta
|
self.meta = meta
|
||||||
|
|
||||||
def __call__(self, no_press=None, no_release=None):
|
def __call__(
|
||||||
|
self, no_press: Optional[bool] = None, no_release: Optional[bool] = None
|
||||||
|
) -> Key:
|
||||||
if no_press is None and no_release is None:
|
if no_press is None and no_release is None:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -466,35 +488,31 @@ class Key:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'Key(code={self.code}, has_modifiers={self.has_modifiers})'
|
return f'Key(code={self.code}, has_modifiers={self.has_modifiers})'
|
||||||
|
|
||||||
def on_press(self, state, coord_int=None):
|
def on_press(self, keyboard: Keyboard, coord_int: Optional[int] = None) -> None:
|
||||||
if hasattr(self, '_pre_press_handlers'):
|
if hasattr(self, '_pre_press_handlers'):
|
||||||
for fn in self._pre_press_handlers:
|
for fn in self._pre_press_handlers:
|
||||||
if not fn(self, state, KC, coord_int):
|
if not fn(self, keyboard, KC, coord_int):
|
||||||
return None
|
return
|
||||||
|
|
||||||
ret = self._handle_press(self, state, KC, coord_int)
|
self._handle_press(self, keyboard, KC, coord_int)
|
||||||
|
|
||||||
if hasattr(self, '_post_press_handlers'):
|
if hasattr(self, '_post_press_handlers'):
|
||||||
for fn in self._post_press_handlers:
|
for fn in self._post_press_handlers:
|
||||||
fn(self, state, KC, coord_int)
|
fn(self, keyboard, KC, coord_int)
|
||||||
|
|
||||||
return ret
|
def on_release(self, keyboard: Keyboard, coord_int: Optional[int] = None) -> None:
|
||||||
|
|
||||||
def on_release(self, state, coord_int=None):
|
|
||||||
if hasattr(self, '_pre_release_handlers'):
|
if hasattr(self, '_pre_release_handlers'):
|
||||||
for fn in self._pre_release_handlers:
|
for fn in self._pre_release_handlers:
|
||||||
if not fn(self, state, KC, coord_int):
|
if not fn(self, keyboard, KC, coord_int):
|
||||||
return None
|
return
|
||||||
|
|
||||||
ret = self._handle_release(self, state, KC, coord_int)
|
self._handle_release(self, keyboard, KC, coord_int)
|
||||||
|
|
||||||
if hasattr(self, '_post_release_handlers'):
|
if hasattr(self, '_post_release_handlers'):
|
||||||
for fn in self._post_release_handlers:
|
for fn in self._post_release_handlers:
|
||||||
fn(self, state, KC, coord_int)
|
fn(self, keyboard, KC, coord_int)
|
||||||
|
|
||||||
return ret
|
def clone(self) -> Key:
|
||||||
|
|
||||||
def clone(self):
|
|
||||||
'''
|
'''
|
||||||
Return a shallow clone of the current key without any pre/post press/release
|
Return a shallow clone of the current key without any pre/post press/release
|
||||||
handlers attached. Almost exclusively useful for creating non-colliding keys
|
handlers attached. Almost exclusively useful for creating non-colliding keys
|
||||||
@@ -511,7 +529,7 @@ class Key:
|
|||||||
meta=self.meta,
|
meta=self.meta,
|
||||||
)
|
)
|
||||||
|
|
||||||
def before_press_handler(self, fn):
|
def before_press_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||||
'''
|
'''
|
||||||
Attach a callback to be run prior to the on_press handler for this key.
|
Attach a callback to be run prior to the on_press handler for this key.
|
||||||
Receives the following:
|
Receives the following:
|
||||||
@@ -534,9 +552,8 @@ class Key:
|
|||||||
if not hasattr(self, '_pre_press_handlers'):
|
if not hasattr(self, '_pre_press_handlers'):
|
||||||
self._pre_press_handlers = []
|
self._pre_press_handlers = []
|
||||||
self._pre_press_handlers.append(fn)
|
self._pre_press_handlers.append(fn)
|
||||||
return self
|
|
||||||
|
|
||||||
def after_press_handler(self, fn):
|
def after_press_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||||
'''
|
'''
|
||||||
Attach a callback to be run after the on_release handler for this key.
|
Attach a callback to be run after the on_release handler for this key.
|
||||||
Receives the following:
|
Receives the following:
|
||||||
@@ -558,9 +575,8 @@ class Key:
|
|||||||
if not hasattr(self, '_post_press_handlers'):
|
if not hasattr(self, '_post_press_handlers'):
|
||||||
self._post_press_handlers = []
|
self._post_press_handlers = []
|
||||||
self._post_press_handlers.append(fn)
|
self._post_press_handlers.append(fn)
|
||||||
return self
|
|
||||||
|
|
||||||
def before_release_handler(self, fn):
|
def before_release_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||||
'''
|
'''
|
||||||
Attach a callback to be run prior to the on_release handler for this
|
Attach a callback to be run prior to the on_release handler for this
|
||||||
key. Receives the following:
|
key. Receives the following:
|
||||||
@@ -583,9 +599,8 @@ class Key:
|
|||||||
if not hasattr(self, '_pre_release_handlers'):
|
if not hasattr(self, '_pre_release_handlers'):
|
||||||
self._pre_release_handlers = []
|
self._pre_release_handlers = []
|
||||||
self._pre_release_handlers.append(fn)
|
self._pre_release_handlers.append(fn)
|
||||||
return self
|
|
||||||
|
|
||||||
def after_release_handler(self, fn):
|
def after_release_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||||
'''
|
'''
|
||||||
Attach a callback to be run after the on_release handler for this key.
|
Attach a callback to be run after the on_release handler for this key.
|
||||||
Receives the following:
|
Receives the following:
|
||||||
@@ -607,13 +622,17 @@ class Key:
|
|||||||
if not hasattr(self, '_post_release_handlers'):
|
if not hasattr(self, '_post_release_handlers'):
|
||||||
self._post_release_handlers = []
|
self._post_release_handlers = []
|
||||||
self._post_release_handlers.append(fn)
|
self._post_release_handlers.append(fn)
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
class ModifierKey(Key):
|
class ModifierKey(Key):
|
||||||
FAKE_CODE = const(-1)
|
FAKE_CODE = const(-1)
|
||||||
|
|
||||||
def __call__(self, modified_key=None, no_press=None, no_release=None):
|
def __call__(
|
||||||
|
self,
|
||||||
|
modified_key: Optional[Key] = None,
|
||||||
|
no_press: Optional[bool] = None,
|
||||||
|
no_release: Optional[bool] = None,
|
||||||
|
) -> Key:
|
||||||
if modified_key is None:
|
if modified_key is None:
|
||||||
return super().__call__(no_press=no_press, no_release=no_release)
|
return super().__call__(no_press=no_press, no_release=no_release)
|
||||||
|
|
||||||
@@ -650,7 +669,12 @@ class ConsumerKey(Key):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
|
def make_key(
|
||||||
|
code: Optional[int] = None,
|
||||||
|
names: Tuple[str, ...] = tuple(), # NOQA
|
||||||
|
type: KeyType = KeyType.SIMPLE,
|
||||||
|
**kwargs,
|
||||||
|
) -> Key:
|
||||||
'''
|
'''
|
||||||
Create a new key, aliased by `names` in the KC lookup table.
|
Create a new key, aliased by `names` in the KC lookup table.
|
||||||
|
|
||||||
@@ -667,14 +691,13 @@ def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
|
|||||||
All **kwargs are passed to the Key constructor
|
All **kwargs are passed to the Key constructor
|
||||||
'''
|
'''
|
||||||
|
|
||||||
collect()
|
|
||||||
global NEXT_AVAILABLE_KEY
|
global NEXT_AVAILABLE_KEY
|
||||||
|
|
||||||
if type == KEY_SIMPLE:
|
if type == KeyType.SIMPLE:
|
||||||
constructor = Key
|
constructor = Key
|
||||||
elif type == KEY_MODIFIER:
|
elif type == KeyType.MODIFIER:
|
||||||
constructor = ModifierKey
|
constructor = ModifierKey
|
||||||
elif type == KEY_CONSUMER:
|
elif type == KeyType.CONSUMER:
|
||||||
constructor = ConsumerKey
|
constructor = ConsumerKey
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unrecognized key type')
|
raise ValueError('Unrecognized key type')
|
||||||
@@ -696,29 +719,29 @@ def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
|
|||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
def make_mod_key(code, names, *args, **kwargs):
|
def make_mod_key(code: int, names: Tuple[str, ...], *args, **kwargs) -> Key:
|
||||||
return make_key(code, names, *args, **kwargs, type=KEY_MODIFIER)
|
return make_key(code, names, *args, **kwargs, type=KeyType.MODIFIER)
|
||||||
|
|
||||||
|
|
||||||
def make_shifted_key(code, names):
|
def make_shifted_key(code: int, names: Tuple[str, ...]) -> Key:
|
||||||
return make_key(code, names, has_modifiers={KC.LSFT.code})
|
return make_key(code, names, has_modifiers={KC.LSFT.code})
|
||||||
|
|
||||||
|
|
||||||
def make_consumer_key(*args, **kwargs):
|
def make_consumer_key(*args, **kwargs) -> Key:
|
||||||
return make_key(*args, **kwargs, type=KEY_CONSUMER)
|
return make_key(*args, **kwargs, type=KeyType.CONSUMER)
|
||||||
|
|
||||||
|
|
||||||
# Argumented keys are implicitly internal, so auto-gen of code
|
# Argumented keys are implicitly internal, so auto-gen of code
|
||||||
# is almost certainly the best plan here
|
# is almost certainly the best plan here
|
||||||
def make_argumented_key(
|
def make_argumented_key(
|
||||||
validator=lambda *validator_args, **validator_kwargs: object(),
|
validator: object = lambda *validator_args, **validator_kwargs: object(),
|
||||||
names=tuple(), # NOQA
|
names: Tuple[str, ...] = tuple(), # NOQA
|
||||||
*constructor_args,
|
*constructor_args,
|
||||||
**constructor_kwargs,
|
**constructor_kwargs,
|
||||||
):
|
) -> Key:
|
||||||
global NEXT_AVAILABLE_KEY
|
global NEXT_AVAILABLE_KEY
|
||||||
|
|
||||||
def _argumented_key(*user_args, **user_kwargs):
|
def _argumented_key(*user_args, **user_kwargs) -> Key:
|
||||||
global NEXT_AVAILABLE_KEY
|
global NEXT_AVAILABLE_KEY
|
||||||
|
|
||||||
meta = validator(*user_args, **user_kwargs)
|
meta = validator(*user_args, **user_kwargs)
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
try:
|
try:
|
||||||
from typing import Optional
|
from typing import Callable, Optional, Tuple
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from gc import collect
|
|
||||||
from supervisor import ticks_ms
|
from supervisor import ticks_ms
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
from keypad import Event as KeyEvent
|
||||||
|
|
||||||
from kmk.consts import UnicodeMode
|
from kmk.consts import UnicodeMode
|
||||||
from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes
|
from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes
|
||||||
from kmk.keys import KC, Key
|
from kmk.keys import KC, Key
|
||||||
@@ -16,6 +18,10 @@ from kmk.utils import Debug
|
|||||||
|
|
||||||
debug = Debug(__name__)
|
debug = Debug(__name__)
|
||||||
|
|
||||||
|
KeyBufferFrame = namedtuple(
|
||||||
|
'KeyBufferFrame', ('key', 'is_pressed', 'int_coord', 'index')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Sandbox:
|
class Sandbox:
|
||||||
matrix_update = None
|
matrix_update = None
|
||||||
@@ -58,6 +64,8 @@ class KMKKeyboard:
|
|||||||
i2c_deinit_count = 0
|
i2c_deinit_count = 0
|
||||||
_go_args = None
|
_go_args = None
|
||||||
_processing_timeouts = False
|
_processing_timeouts = False
|
||||||
|
_resume_buffer = []
|
||||||
|
_resume_buffer_x = []
|
||||||
|
|
||||||
# this should almost always be PREpended to, replaces
|
# this should almost always be PREpended to, replaces
|
||||||
# former use of reversed_active_layers which had pointless
|
# former use of reversed_active_layers which had pointless
|
||||||
@@ -70,7 +78,7 @@ class KMKKeyboard:
|
|||||||
# 6.0rc1) this runs out of RAM every cycle and takes down the board. no
|
# 6.0rc1) this runs out of RAM every cycle and takes down the board. no
|
||||||
# real known fix yet other than turning off debug, but M4s have always been
|
# real known fix yet other than turning off debug, but M4s have always been
|
||||||
# tight on RAM so....
|
# tight on RAM so....
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return ''.join(
|
return ''.join(
|
||||||
[
|
[
|
||||||
'KMKKeyboard(\n',
|
'KMKKeyboard(\n',
|
||||||
@@ -88,12 +96,12 @@ class KMKKeyboard:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def _print_debug_cycle(self, init=False):
|
def _print_debug_cycle(self, init: bool = False) -> None:
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'coordkeys_pressed={self._coordkeys_pressed}')
|
debug(f'coordkeys_pressed={self._coordkeys_pressed}')
|
||||||
debug(f'keys_pressed={self.keys_pressed}')
|
debug(f'keys_pressed={self.keys_pressed}')
|
||||||
|
|
||||||
def _send_hid(self):
|
def _send_hid(self) -> None:
|
||||||
if self._hid_send_enabled:
|
if self._hid_send_enabled:
|
||||||
hid_report = self._hid_helper.create_report(self.keys_pressed)
|
hid_report = self._hid_helper.create_report(self.keys_pressed)
|
||||||
try:
|
try:
|
||||||
@@ -103,12 +111,12 @@ class KMKKeyboard:
|
|||||||
debug(f'HidNotFound(HIDReportType={e})')
|
debug(f'HidNotFound(HIDReportType={e})')
|
||||||
self.hid_pending = False
|
self.hid_pending = False
|
||||||
|
|
||||||
def _handle_matrix_report(self, update=None):
|
def _handle_matrix_report(self, kevent: KeyEvent) -> None:
|
||||||
if update is not None:
|
if kevent is not None:
|
||||||
self._on_matrix_changed(update)
|
self._on_matrix_changed(kevent)
|
||||||
self.state_changed = True
|
self.state_changed = True
|
||||||
|
|
||||||
def _find_key_in_map(self, int_coord):
|
def _find_key_in_map(self, int_coord: int) -> Key:
|
||||||
try:
|
try:
|
||||||
idx = self.coord_mapping.index(int_coord)
|
idx = self.coord_mapping.index(int_coord)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@@ -130,7 +138,7 @@ class KMKKeyboard:
|
|||||||
|
|
||||||
return layer_key
|
return layer_key
|
||||||
|
|
||||||
def _on_matrix_changed(self, kevent):
|
def _on_matrix_changed(self, kevent: KeyEvent) -> None:
|
||||||
int_coord = kevent.key_number
|
int_coord = kevent.key_number
|
||||||
is_pressed = kevent.pressed
|
is_pressed = kevent.pressed
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
@@ -157,15 +165,62 @@ class KMKKeyboard:
|
|||||||
|
|
||||||
self.pre_process_key(key, is_pressed, int_coord)
|
self.pre_process_key(key, is_pressed, int_coord)
|
||||||
|
|
||||||
|
def _process_resume_buffer(self):
|
||||||
|
'''
|
||||||
|
Resume the processing of buffered, delayed, deferred, etc. key events
|
||||||
|
emitted by modules.
|
||||||
|
|
||||||
|
We use a copy of the `_resume_buffer` as a working buffer. The working
|
||||||
|
buffer holds all key events in the correct order for processing. If
|
||||||
|
during processing new events are pushed to the `_resume_buffer`, they
|
||||||
|
are prepended to the working buffer (which may not be emptied), in
|
||||||
|
order to preserve key event order.
|
||||||
|
We also double-buffer `_resume_buffer` with `_resume_buffer_x`, only
|
||||||
|
copying the reference to hopefully safe some time on allocations.
|
||||||
|
'''
|
||||||
|
|
||||||
|
buffer, self._resume_buffer = self._resume_buffer, self._resume_buffer_x
|
||||||
|
|
||||||
|
while buffer:
|
||||||
|
ksf = buffer.pop(0)
|
||||||
|
key = ksf.key
|
||||||
|
|
||||||
|
# Handle any unaccounted-for layer shifts by looking up the key resolution again.
|
||||||
|
if ksf.int_coord in self._coordkeys_pressed.keys():
|
||||||
|
key = self._find_key_in_map(ksf.int_coord)
|
||||||
|
|
||||||
|
# Resume the processing of the key event and update the HID report
|
||||||
|
# when applicable.
|
||||||
|
self.pre_process_key(key, ksf.is_pressed, ksf.int_coord, ksf.index)
|
||||||
|
|
||||||
|
if self.hid_pending:
|
||||||
|
self._send_hid()
|
||||||
|
self.hid_pending = False
|
||||||
|
|
||||||
|
# Any newly buffered key events must be prepended to the working
|
||||||
|
# buffer.
|
||||||
|
if self._resume_buffer:
|
||||||
|
self._resume_buffer.extend(buffer)
|
||||||
|
buffer.clear()
|
||||||
|
buffer, self._resume_buffer = self._resume_buffer, buffer
|
||||||
|
|
||||||
|
self._resume_buffer_x = buffer
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debug_enabled(self):
|
def debug_enabled(self) -> bool:
|
||||||
return debug.enabled
|
return debug.enabled
|
||||||
|
|
||||||
@debug_enabled.setter
|
@debug_enabled.setter
|
||||||
def debug_enabled(self, enabled):
|
def debug_enabled(self, enabled: bool):
|
||||||
debug.enabled = enabled
|
debug.enabled = enabled
|
||||||
|
|
||||||
def pre_process_key(self, key, is_pressed, int_coord=None, index=0):
|
def pre_process_key(
|
||||||
|
self,
|
||||||
|
key: Key,
|
||||||
|
is_pressed: bool,
|
||||||
|
int_coord: Optional[int] = None,
|
||||||
|
index: int = 0,
|
||||||
|
) -> None:
|
||||||
for module in self.modules[index:]:
|
for module in self.modules[index:]:
|
||||||
try:
|
try:
|
||||||
key = module.process_key(self, key, is_pressed, int_coord)
|
key = module.process_key(self, key, is_pressed, int_coord)
|
||||||
@@ -188,16 +243,14 @@ class KMKKeyboard:
|
|||||||
if key:
|
if key:
|
||||||
self.process_key(key, is_pressed, int_coord)
|
self.process_key(key, is_pressed, int_coord)
|
||||||
|
|
||||||
return self
|
def process_key(
|
||||||
|
self, key: Key, is_pressed: bool, coord_int: Optional[int] = None
|
||||||
def process_key(self, key, is_pressed, coord_int=None):
|
) -> None:
|
||||||
if is_pressed:
|
if is_pressed:
|
||||||
key.on_press(self, coord_int)
|
key.on_press(self, coord_int)
|
||||||
else:
|
else:
|
||||||
key.on_release(self, coord_int)
|
key.on_release(self, coord_int)
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
def resume_process_key(
|
def resume_process_key(
|
||||||
self,
|
self,
|
||||||
module: Module,
|
module: Module,
|
||||||
@@ -206,24 +259,27 @@ class KMKKeyboard:
|
|||||||
int_coord: Optional[int] = None,
|
int_coord: Optional[int] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
index = self.modules.index(module) + 1
|
index = self.modules.index(module) + 1
|
||||||
self.pre_process_key(key, is_pressed, int_coord, index)
|
ksf = KeyBufferFrame(
|
||||||
|
key=key, is_pressed=is_pressed, int_coord=int_coord, index=index
|
||||||
|
)
|
||||||
|
self._resume_buffer.append(ksf)
|
||||||
|
|
||||||
def remove_key(self, keycode):
|
def remove_key(self, keycode: Key) -> None:
|
||||||
self.keys_pressed.discard(keycode)
|
self.keys_pressed.discard(keycode)
|
||||||
return self.process_key(keycode, False)
|
self.process_key(keycode, False)
|
||||||
|
|
||||||
def add_key(self, keycode):
|
def add_key(self, keycode: Key) -> None:
|
||||||
self.keys_pressed.add(keycode)
|
self.keys_pressed.add(keycode)
|
||||||
return self.process_key(keycode, True)
|
self.process_key(keycode, True)
|
||||||
|
|
||||||
def tap_key(self, keycode):
|
def tap_key(self, keycode: Key) -> None:
|
||||||
self.add_key(keycode)
|
self.add_key(keycode)
|
||||||
# On the next cycle, we'll remove the key.
|
# On the next cycle, we'll remove the key.
|
||||||
self.set_timeout(False, lambda: self.remove_key(keycode))
|
self.set_timeout(False, lambda: self.remove_key(keycode))
|
||||||
|
|
||||||
return self
|
def set_timeout(
|
||||||
|
self, after_ticks: int, callback: Callable[[None], None]
|
||||||
def set_timeout(self, after_ticks, callback):
|
) -> Tuple[int, int]:
|
||||||
# We allow passing False as an implicit "run this on the next process timeouts cycle"
|
# We allow passing False as an implicit "run this on the next process timeouts cycle"
|
||||||
if after_ticks is False:
|
if after_ticks is False:
|
||||||
after_ticks = 0
|
after_ticks = 0
|
||||||
@@ -241,16 +297,16 @@ class KMKKeyboard:
|
|||||||
|
|
||||||
return (timeout_key, idx)
|
return (timeout_key, idx)
|
||||||
|
|
||||||
def cancel_timeout(self, timeout_key):
|
def cancel_timeout(self, timeout_key: int) -> None:
|
||||||
try:
|
try:
|
||||||
self._timeouts[timeout_key[0]][timeout_key[1]] = None
|
self._timeouts[timeout_key[0]][timeout_key[1]] = None
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'no such timeout: {timeout_key}')
|
debug(f'no such timeout: {timeout_key}')
|
||||||
|
|
||||||
def _process_timeouts(self):
|
def _process_timeouts(self) -> None:
|
||||||
if not self._timeouts:
|
if not self._timeouts:
|
||||||
return self
|
return
|
||||||
|
|
||||||
# Copy timeout keys to a temporary list to allow sorting.
|
# Copy timeout keys to a temporary list to allow sorting.
|
||||||
# Prevent net timeouts set during handling from running on the current
|
# Prevent net timeouts set during handling from running on the current
|
||||||
@@ -274,9 +330,7 @@ class KMKKeyboard:
|
|||||||
|
|
||||||
self._processing_timeouts = False
|
self._processing_timeouts = False
|
||||||
|
|
||||||
return self
|
def _init_sanity_check(self) -> None:
|
||||||
|
|
||||||
def _init_sanity_check(self):
|
|
||||||
'''
|
'''
|
||||||
Ensure the provided configuration is *probably* bootable
|
Ensure the provided configuration is *probably* bootable
|
||||||
'''
|
'''
|
||||||
@@ -291,9 +345,7 @@ class KMKKeyboard:
|
|||||||
self.diode_orientation is not None
|
self.diode_orientation is not None
|
||||||
), 'diode orientation must be defined'
|
), 'diode orientation must be defined'
|
||||||
|
|
||||||
return self
|
def _init_coord_mapping(self) -> None:
|
||||||
|
|
||||||
def _init_coord_mapping(self):
|
|
||||||
'''
|
'''
|
||||||
Attempt to sanely guess a coord_mapping if one is not provided. No-op
|
Attempt to sanely guess a coord_mapping if one is not provided. No-op
|
||||||
if `kmk.extensions.split.Split` is used, it provides equivalent
|
if `kmk.extensions.split.Split` is used, it provides equivalent
|
||||||
@@ -311,7 +363,7 @@ class KMKKeyboard:
|
|||||||
cm.extend(m.coord_mapping)
|
cm.extend(m.coord_mapping)
|
||||||
self.coord_mapping = tuple(cm)
|
self.coord_mapping = tuple(cm)
|
||||||
|
|
||||||
def _init_hid(self):
|
def _init_hid(self) -> None:
|
||||||
if self.hid_type == HIDModes.NOOP:
|
if self.hid_type == HIDModes.NOOP:
|
||||||
self._hid_helper = AbstractHID
|
self._hid_helper = AbstractHID
|
||||||
elif self.hid_type == HIDModes.USB:
|
elif self.hid_type == HIDModes.USB:
|
||||||
@@ -323,7 +375,7 @@ class KMKKeyboard:
|
|||||||
self._hid_helper = self._hid_helper(**self._go_args)
|
self._hid_helper = self._hid_helper(**self._go_args)
|
||||||
self._hid_send_enabled = True
|
self._hid_send_enabled = True
|
||||||
|
|
||||||
def _init_matrix(self):
|
def _init_matrix(self) -> None:
|
||||||
if self.matrix is None:
|
if self.matrix is None:
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug('Initialising default matrix scanner.')
|
debug('Initialising default matrix scanner.')
|
||||||
@@ -342,9 +394,7 @@ class KMKKeyboard:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
self.matrix = (self.matrix,)
|
self.matrix = (self.matrix,)
|
||||||
|
|
||||||
return self
|
def before_matrix_scan(self) -> None:
|
||||||
|
|
||||||
def before_matrix_scan(self):
|
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.before_matrix_scan(self)
|
module.before_matrix_scan(self)
|
||||||
@@ -359,7 +409,7 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Error in {ext}.before_matrix_scan: {err}')
|
debug(f'Error in {ext}.before_matrix_scan: {err}')
|
||||||
|
|
||||||
def after_matrix_scan(self):
|
def after_matrix_scan(self) -> None:
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.after_matrix_scan(self)
|
module.after_matrix_scan(self)
|
||||||
@@ -374,7 +424,7 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Error in {ext}.after_matrix_scan: {err}')
|
debug(f'Error in {ext}.after_matrix_scan: {err}')
|
||||||
|
|
||||||
def before_hid_send(self):
|
def before_hid_send(self) -> None:
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.before_hid_send(self)
|
module.before_hid_send(self)
|
||||||
@@ -391,7 +441,7 @@ class KMKKeyboard:
|
|||||||
f'Error in {ext}.before_hid_send: {err}',
|
f'Error in {ext}.before_hid_send: {err}',
|
||||||
)
|
)
|
||||||
|
|
||||||
def after_hid_send(self):
|
def after_hid_send(self) -> None:
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.after_hid_send(self)
|
module.after_hid_send(self)
|
||||||
@@ -406,7 +456,7 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Error in {ext}.after_hid_send: {err}')
|
debug(f'Error in {ext}.after_hid_send: {err}')
|
||||||
|
|
||||||
def powersave_enable(self):
|
def powersave_enable(self) -> None:
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.on_powersave_enable(self)
|
module.on_powersave_enable(self)
|
||||||
@@ -421,7 +471,7 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Error in {ext}.powersave_enable: {err}')
|
debug(f'Error in {ext}.powersave_enable: {err}')
|
||||||
|
|
||||||
def powersave_disable(self):
|
def powersave_disable(self) -> None:
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
try:
|
try:
|
||||||
module.on_powersave_disable(self)
|
module.on_powersave_disable(self)
|
||||||
@@ -435,35 +485,33 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Error in {ext}.powersave_disable: {err}')
|
debug(f'Error in {ext}.powersave_disable: {err}')
|
||||||
|
|
||||||
def go(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs):
|
def go(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs) -> None:
|
||||||
self._init(hid_type=hid_type, secondary_hid_type=secondary_hid_type, **kwargs)
|
self._init(hid_type=hid_type, secondary_hid_type=secondary_hid_type, **kwargs)
|
||||||
while True:
|
while True:
|
||||||
self._main_loop()
|
self._main_loop()
|
||||||
|
|
||||||
def _init(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs):
|
def _init(
|
||||||
|
self,
|
||||||
|
hid_type: HIDModes = HIDModes.USB,
|
||||||
|
secondary_hid_type: Optional[HIDModes] = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
self._go_args = kwargs
|
self._go_args = kwargs
|
||||||
self.hid_type = hid_type
|
self.hid_type = hid_type
|
||||||
self.secondary_hid_type = secondary_hid_type
|
self.secondary_hid_type = secondary_hid_type
|
||||||
|
|
||||||
# Collect is run to keep memory fragmentation down.
|
|
||||||
collect()
|
|
||||||
self._init_sanity_check()
|
self._init_sanity_check()
|
||||||
collect()
|
|
||||||
self._init_hid()
|
self._init_hid()
|
||||||
collect()
|
|
||||||
self._init_matrix()
|
self._init_matrix()
|
||||||
collect()
|
|
||||||
self._init_coord_mapping()
|
self._init_coord_mapping()
|
||||||
|
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
collect()
|
|
||||||
try:
|
try:
|
||||||
module.during_bootup(self)
|
module.during_bootup(self)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'Failed to load module {module}: {err}')
|
debug(f'Failed to load module {module}: {err}')
|
||||||
for ext in self.extensions:
|
for ext in self.extensions:
|
||||||
collect()
|
|
||||||
try:
|
try:
|
||||||
ext.during_bootup(self)
|
ext.during_bootup(self)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@@ -473,28 +521,31 @@ class KMKKeyboard:
|
|||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug(f'init: {self}')
|
debug(f'init: {self}')
|
||||||
|
|
||||||
def _main_loop(self):
|
def _main_loop(self) -> None:
|
||||||
self.state_changed = False
|
self.state_changed = False
|
||||||
self.sandbox.active_layers = self.active_layers.copy()
|
self.sandbox.active_layers = self.active_layers.copy()
|
||||||
|
|
||||||
self.before_matrix_scan()
|
self.before_matrix_scan()
|
||||||
|
|
||||||
|
self._process_resume_buffer()
|
||||||
|
|
||||||
for matrix in self.matrix:
|
for matrix in self.matrix:
|
||||||
update = matrix.scan_for_changes()
|
update = matrix.scan_for_changes()
|
||||||
if update:
|
if update:
|
||||||
self.matrix_update = update
|
self.matrix_update = update
|
||||||
break
|
break
|
||||||
|
self.sandbox.matrix_update = self.matrix_update
|
||||||
self.sandbox.secondary_matrix_update = self.secondary_matrix_update
|
self.sandbox.secondary_matrix_update = self.secondary_matrix_update
|
||||||
|
|
||||||
self.after_matrix_scan()
|
self.after_matrix_scan()
|
||||||
|
|
||||||
if self.secondary_matrix_update:
|
if self.secondary_matrix_update:
|
||||||
self.matrix_update_queue.append(self.secondary_matrix_update)
|
self.matrix_update_queue.append(self.secondary_matrix_update)
|
||||||
del self.secondary_matrix_update
|
self.secondary_matrix_update = None
|
||||||
|
|
||||||
if self.matrix_update:
|
if self.matrix_update:
|
||||||
self.matrix_update_queue.append(self.matrix_update)
|
self.matrix_update_queue.append(self.matrix_update)
|
||||||
del self.matrix_update
|
self.matrix_update = None
|
||||||
|
|
||||||
# only handle one key per cycle.
|
# only handle one key per cycle.
|
||||||
if self.matrix_update_queue:
|
if self.matrix_update_queue:
|
||||||
@@ -521,5 +572,3 @@ class KMKKeyboard:
|
|||||||
|
|
||||||
if self.state_changed:
|
if self.state_changed:
|
||||||
self._print_debug_cycle()
|
self._print_debug_cycle()
|
||||||
|
|
||||||
collect()
|
|
||||||
|
@@ -6,26 +6,26 @@ _TICKS_MAX = const(_TICKS_PERIOD - 1)
|
|||||||
_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2)
|
_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2)
|
||||||
|
|
||||||
|
|
||||||
def ticks_diff(new, start):
|
def ticks_diff(new: int, start: int) -> int:
|
||||||
diff = (new - start) & _TICKS_MAX
|
diff = (new - start) & _TICKS_MAX
|
||||||
diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
|
diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
|
||||||
return diff
|
return diff
|
||||||
|
|
||||||
|
|
||||||
def ticks_add(ticks, delta):
|
def ticks_add(ticks: int, delta: int) -> int:
|
||||||
return (ticks + delta) % _TICKS_PERIOD
|
return (ticks + delta) % _TICKS_PERIOD
|
||||||
|
|
||||||
|
|
||||||
def check_deadline(new, start, ms):
|
def check_deadline(new: int, start: int, ms: int) -> int:
|
||||||
return ticks_diff(new, start) < ms
|
return ticks_diff(new, start) < ms
|
||||||
|
|
||||||
|
|
||||||
class PeriodicTimer:
|
class PeriodicTimer:
|
||||||
def __init__(self, period):
|
def __init__(self, period: int):
|
||||||
self.period = period
|
self.period = period
|
||||||
self.last_tick = ticks_ms()
|
self.last_tick = ticks_ms()
|
||||||
|
|
||||||
def tick(self):
|
def tick(self) -> bool:
|
||||||
now = ticks_ms()
|
now = ticks_ms()
|
||||||
if ticks_diff(now, self.last_tick) >= self.period:
|
if ticks_diff(now, self.last_tick) >= self.period:
|
||||||
self.last_tick = now
|
self.last_tick = now
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
try:
|
try:
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple, Union
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
from micropython import const
|
from micropython import const
|
||||||
@@ -24,14 +24,16 @@ class Combo:
|
|||||||
_remaining = []
|
_remaining = []
|
||||||
_timeout = None
|
_timeout = None
|
||||||
_state = _ComboState.IDLE
|
_state = _ComboState.IDLE
|
||||||
|
_match_coord = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
match: Tuple[Key, ...],
|
match: Tuple[Union[Key, int], ...],
|
||||||
result: Key,
|
result: Key,
|
||||||
fast_reset=None,
|
fast_reset=None,
|
||||||
per_key_timeout=None,
|
per_key_timeout=None,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
|
match_coord=None,
|
||||||
):
|
):
|
||||||
'''
|
'''
|
||||||
match: tuple of keys (KC.A, KC.B)
|
match: tuple of keys (KC.A, KC.B)
|
||||||
@@ -45,22 +47,36 @@ class Combo:
|
|||||||
self.per_key_timeout = per_key_timeout
|
self.per_key_timeout = per_key_timeout
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
if match_coord is not None:
|
||||||
|
self._match_coord = match_coord
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'{self.__class__.__name__}({[k.code for k in self.match]})'
|
return f'{self.__class__.__name__}({[k.code for k in self.match]})'
|
||||||
|
|
||||||
def matches(self, key):
|
def matches(self, key: Key, int_coord: int):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def has_match(self, key: Key, int_coord: int):
|
||||||
|
return self._match_coord and int_coord in self.match or key in self.match
|
||||||
|
|
||||||
|
def insert(self, key: Key, int_coord: int):
|
||||||
|
if self._match_coord:
|
||||||
|
self._remaining.insert(0, int_coord)
|
||||||
|
else:
|
||||||
|
self._remaining.insert(0, key)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._remaining = list(self.match)
|
self._remaining = list(self.match)
|
||||||
|
|
||||||
|
|
||||||
class Chord(Combo):
|
class Chord(Combo):
|
||||||
def matches(self, key):
|
def matches(self, key: Key, int_coord: int):
|
||||||
if key in self._remaining:
|
if not self._match_coord and key in self._remaining:
|
||||||
self._remaining.remove(key)
|
self._remaining.remove(key)
|
||||||
return True
|
return True
|
||||||
|
elif self._match_coord and int_coord in self._remaining:
|
||||||
|
self._remaining.remove(int_coord)
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -70,8 +86,12 @@ class Sequence(Combo):
|
|||||||
per_key_timeout = True
|
per_key_timeout = True
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
|
|
||||||
def matches(self, key):
|
def matches(self, key: Key, int_coord: int):
|
||||||
if self._remaining and self._remaining[0] == key:
|
if (
|
||||||
|
not self._match_coord and self._remaining and self._remaining[0] == key
|
||||||
|
) or (
|
||||||
|
self._match_coord and self._remaining and self._remaining[0] == int_coord
|
||||||
|
):
|
||||||
self._remaining.pop(0)
|
self._remaining.pop(0)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -127,7 +147,7 @@ class Combos(Module):
|
|||||||
for combo in self.combos:
|
for combo in self.combos:
|
||||||
if combo._state != _ComboState.MATCHING:
|
if combo._state != _ComboState.MATCHING:
|
||||||
continue
|
continue
|
||||||
if combo.matches(key):
|
if combo.matches(key, int_coord):
|
||||||
continue
|
continue
|
||||||
combo._state = _ComboState.IDLE
|
combo._state = _ComboState.IDLE
|
||||||
if combo._timeout:
|
if combo._timeout:
|
||||||
@@ -171,10 +191,11 @@ class Combos(Module):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# There's no matching combo: send and reset key buffer
|
# There's no matching combo: send and reset key buffer
|
||||||
self.send_key_buffer(keyboard)
|
if self._key_buffer:
|
||||||
self._key_buffer = []
|
self._key_buffer.append((int_coord, key, True))
|
||||||
if int_coord is not None:
|
self.send_key_buffer(keyboard)
|
||||||
key = keyboard._find_key_in_map(int_coord)
|
self._key_buffer = []
|
||||||
|
key = None
|
||||||
|
|
||||||
return key
|
return key
|
||||||
|
|
||||||
@@ -182,7 +203,7 @@ class Combos(Module):
|
|||||||
for combo in self.combos:
|
for combo in self.combos:
|
||||||
if combo._state != _ComboState.ACTIVE:
|
if combo._state != _ComboState.ACTIVE:
|
||||||
continue
|
continue
|
||||||
if key in combo.match:
|
if combo.has_match(key, int_coord):
|
||||||
# Deactivate combo if it matches current key.
|
# Deactivate combo if it matches current key.
|
||||||
self.deactivate(keyboard, combo)
|
self.deactivate(keyboard, combo)
|
||||||
|
|
||||||
@@ -190,7 +211,7 @@ class Combos(Module):
|
|||||||
self.reset_combo(keyboard, combo)
|
self.reset_combo(keyboard, combo)
|
||||||
self._key_buffer = []
|
self._key_buffer = []
|
||||||
else:
|
else:
|
||||||
combo._remaining.insert(0, key)
|
combo.insert(key, int_coord)
|
||||||
combo._state = _ComboState.MATCHING
|
combo._state = _ComboState.MATCHING
|
||||||
|
|
||||||
key = combo.result
|
key = combo.result
|
||||||
@@ -203,7 +224,7 @@ class Combos(Module):
|
|||||||
for combo in self.combos:
|
for combo in self.combos:
|
||||||
if combo._state != _ComboState.MATCHING:
|
if combo._state != _ComboState.MATCHING:
|
||||||
continue
|
continue
|
||||||
if key not in combo.match:
|
if not combo.has_match(key, int_coord):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Combo matches, but first key released before timeout.
|
# Combo matches, but first key released before timeout.
|
||||||
@@ -216,7 +237,7 @@ class Combos(Module):
|
|||||||
if combo.fast_reset:
|
if combo.fast_reset:
|
||||||
self.reset_combo(keyboard, combo)
|
self.reset_combo(keyboard, combo)
|
||||||
else:
|
else:
|
||||||
combo._remaining.insert(0, key)
|
combo.insert(key, int_coord)
|
||||||
combo._state = _ComboState.MATCHING
|
combo._state = _ComboState.MATCHING
|
||||||
self.reset(keyboard)
|
self.reset(keyboard)
|
||||||
|
|
||||||
@@ -231,12 +252,14 @@ class Combos(Module):
|
|||||||
elif len(combo._remaining) == len(combo.match) - 1:
|
elif len(combo._remaining) == len(combo.match) - 1:
|
||||||
self.reset_combo(keyboard, combo)
|
self.reset_combo(keyboard, combo)
|
||||||
if not self.count_matching():
|
if not self.count_matching():
|
||||||
|
self._key_buffer.append((int_coord, key, False))
|
||||||
self.send_key_buffer(keyboard)
|
self.send_key_buffer(keyboard)
|
||||||
self._key_buffer = []
|
self._key_buffer = []
|
||||||
|
key = None
|
||||||
|
|
||||||
# Anything between first and last key released.
|
# Anything between first and last key released.
|
||||||
else:
|
else:
|
||||||
combo._remaining.insert(0, key)
|
combo.insert(key, int_coord)
|
||||||
|
|
||||||
# Don't propagate key-release events for keys that have been
|
# Don't propagate key-release events for keys that have been
|
||||||
# buffered. Append release events only if corresponding press is in
|
# buffered. Append release events only if corresponding press is in
|
||||||
@@ -275,17 +298,7 @@ class Combos(Module):
|
|||||||
|
|
||||||
def send_key_buffer(self, keyboard):
|
def send_key_buffer(self, keyboard):
|
||||||
for (int_coord, key, is_pressed) in self._key_buffer:
|
for (int_coord, key, is_pressed) in self._key_buffer:
|
||||||
new_key = None
|
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||||
if not is_pressed:
|
|
||||||
try:
|
|
||||||
new_key = keyboard._coordkeys_pressed[int_coord]
|
|
||||||
except KeyError:
|
|
||||||
new_key = None
|
|
||||||
if new_key is None:
|
|
||||||
new_key = keyboard._find_key_in_map(int_coord)
|
|
||||||
|
|
||||||
keyboard.resume_process_key(self, new_key, is_pressed, int_coord)
|
|
||||||
keyboard._send_hid()
|
|
||||||
|
|
||||||
def activate(self, keyboard, combo):
|
def activate(self, keyboard, combo):
|
||||||
combo.result.on_press(keyboard)
|
combo.result.on_press(keyboard)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
from kmk.keys import make_argumented_key
|
from kmk.keys import KC, make_argumented_key
|
||||||
from kmk.modules import Module
|
from kmk.modules import Module
|
||||||
from kmk.utils import Debug
|
from kmk.utils import Debug
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ class ActivationType:
|
|||||||
RELEASED = const(1)
|
RELEASED = const(1)
|
||||||
HOLD_TIMEOUT = const(2)
|
HOLD_TIMEOUT = const(2)
|
||||||
INTERRUPTED = const(3)
|
INTERRUPTED = const(3)
|
||||||
|
REPEAT = const(4)
|
||||||
|
|
||||||
|
|
||||||
class HoldTapKeyState:
|
class HoldTapKeyState:
|
||||||
@@ -30,12 +31,14 @@ class HoldTapKeyMeta:
|
|||||||
prefer_hold=True,
|
prefer_hold=True,
|
||||||
tap_interrupted=False,
|
tap_interrupted=False,
|
||||||
tap_time=None,
|
tap_time=None,
|
||||||
|
repeat=False,
|
||||||
):
|
):
|
||||||
self.tap = tap
|
self.tap = tap
|
||||||
self.hold = hold
|
self.hold = hold
|
||||||
self.prefer_hold = prefer_hold
|
self.prefer_hold = prefer_hold
|
||||||
self.tap_interrupted = tap_interrupted
|
self.tap_interrupted = tap_interrupted
|
||||||
self.tap_time = tap_time
|
self.tap_time = tap_time
|
||||||
|
self.repeat = repeat
|
||||||
|
|
||||||
|
|
||||||
class HoldTap(Module):
|
class HoldTap(Module):
|
||||||
@@ -44,12 +47,13 @@ class HoldTap(Module):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.key_buffer = []
|
self.key_buffer = []
|
||||||
self.key_states = {}
|
self.key_states = {}
|
||||||
make_argumented_key(
|
if not KC.get('HT'):
|
||||||
validator=HoldTapKeyMeta,
|
make_argumented_key(
|
||||||
names=('HT',),
|
validator=HoldTapKeyMeta,
|
||||||
on_press=self.ht_pressed,
|
names=('HT',),
|
||||||
on_release=self.ht_released,
|
on_press=self.ht_pressed,
|
||||||
)
|
on_release=self.ht_released,
|
||||||
|
)
|
||||||
|
|
||||||
def during_bootup(self, keyboard):
|
def during_bootup(self, keyboard):
|
||||||
return
|
return
|
||||||
@@ -82,12 +86,8 @@ class HoldTap(Module):
|
|||||||
self.ht_activate_on_interrupt(
|
self.ht_activate_on_interrupt(
|
||||||
key, keyboard, *state.args, **state.kwargs
|
key, keyboard, *state.args, **state.kwargs
|
||||||
)
|
)
|
||||||
keyboard._send_hid()
|
|
||||||
send_buffer = True
|
send_buffer = True
|
||||||
|
|
||||||
if state.activated == ActivationType.INTERRUPTED:
|
|
||||||
current_key = keyboard._find_key_in_map(int_coord)
|
|
||||||
|
|
||||||
# if interrupt on release: store interrupting keys until one of them
|
# if interrupt on release: store interrupting keys until one of them
|
||||||
# is released.
|
# is released.
|
||||||
if (
|
if (
|
||||||
@@ -100,10 +100,13 @@ class HoldTap(Module):
|
|||||||
# apply changes with 'side-effects' on key_states or the loop behaviour
|
# apply changes with 'side-effects' on key_states or the loop behaviour
|
||||||
# outside the loop.
|
# outside the loop.
|
||||||
if append_buffer:
|
if append_buffer:
|
||||||
self.key_buffer.append((int_coord, current_key))
|
self.key_buffer.append((int_coord, current_key, is_pressed))
|
||||||
current_key = None
|
current_key = None
|
||||||
|
|
||||||
elif send_buffer:
|
elif send_buffer:
|
||||||
self.send_key_buffer(keyboard)
|
self.send_key_buffer(keyboard)
|
||||||
|
keyboard.resume_process_key(self, current_key, is_pressed, int_coord)
|
||||||
|
current_key = None
|
||||||
|
|
||||||
return current_key
|
return current_key
|
||||||
|
|
||||||
@@ -120,7 +123,20 @@ class HoldTap(Module):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def ht_pressed(self, key, keyboard, *args, **kwargs):
|
def ht_pressed(self, key, keyboard, *args, **kwargs):
|
||||||
'''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.'''
|
'''Unless in repeat mode, do nothing yet, action resolves when key is released, timer expires or other key is pressed.'''
|
||||||
|
if key in self.key_states:
|
||||||
|
state = self.key_states[key]
|
||||||
|
keyboard.cancel_timeout(self.key_states[key].timeout_key)
|
||||||
|
|
||||||
|
if state.activated == ActivationType.RELEASED:
|
||||||
|
state.activated = ActivationType.REPEAT
|
||||||
|
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||||
|
elif state.activated == ActivationType.HOLD_TIMEOUT:
|
||||||
|
self.ht_activate_hold(key, keyboard, *args, **kwargs)
|
||||||
|
elif state.activated == ActivationType.INTERRUPTED:
|
||||||
|
self.ht_activate_on_interrupt(key, keyboard, *args, **kwargs)
|
||||||
|
return
|
||||||
|
|
||||||
if key.meta.tap_time is None:
|
if key.meta.tap_time is None:
|
||||||
tap_time = self.tap_time
|
tap_time = self.tap_time
|
||||||
else:
|
else:
|
||||||
@@ -149,10 +165,24 @@ class HoldTap(Module):
|
|||||||
elif state.activated == ActivationType.PRESSED:
|
elif state.activated == ActivationType.PRESSED:
|
||||||
# press and release tap because key released within tap time
|
# press and release tap because key released within tap time
|
||||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||||
|
self.send_key_buffer(keyboard)
|
||||||
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
||||||
state.activated = ActivationType.RELEASED
|
state.activated = ActivationType.RELEASED
|
||||||
self.send_key_buffer(keyboard)
|
self.send_key_buffer(keyboard)
|
||||||
del self.key_states[key]
|
elif state.activated == ActivationType.REPEAT:
|
||||||
|
state.activated = ActivationType.RELEASED
|
||||||
|
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
||||||
|
|
||||||
|
# don't delete the key state right now in this case
|
||||||
|
tap_time = 0
|
||||||
|
if key.meta.repeat:
|
||||||
|
if key.meta.tap_time is None:
|
||||||
|
tap_time = self.tap_time
|
||||||
|
else:
|
||||||
|
tap_time = key.meta.tap_time
|
||||||
|
state.timeout_key = keyboard.set_timeout(
|
||||||
|
tap_time, lambda: self.key_states.pop(key)
|
||||||
|
)
|
||||||
|
|
||||||
return keyboard
|
return keyboard
|
||||||
|
|
||||||
@@ -176,11 +206,12 @@ class HoldTap(Module):
|
|||||||
del self.key_states[key]
|
del self.key_states[key]
|
||||||
|
|
||||||
def send_key_buffer(self, keyboard):
|
def send_key_buffer(self, keyboard):
|
||||||
for (int_coord, key) in self.key_buffer:
|
if not self.key_buffer:
|
||||||
new_key = keyboard._find_key_in_map(int_coord)
|
return
|
||||||
keyboard.resume_process_key(self, new_key, True, int_coord)
|
|
||||||
|
for (int_coord, key, is_pressed) in self.key_buffer:
|
||||||
|
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||||
|
|
||||||
keyboard._send_hid()
|
|
||||||
self.key_buffer.clear()
|
self.key_buffer.clear()
|
||||||
|
|
||||||
def ht_activate_hold(self, key, keyboard, *args, **kwargs):
|
def ht_activate_hold(self, key, keyboard, *args, **kwargs):
|
||||||
@@ -191,24 +222,17 @@ class HoldTap(Module):
|
|||||||
def ht_deactivate_hold(self, key, keyboard, *args, **kwargs):
|
def ht_deactivate_hold(self, key, keyboard, *args, **kwargs):
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug('ht_deactivate_hold')
|
debug('ht_deactivate_hold')
|
||||||
keyboard.set_timeout(
|
keyboard.resume_process_key(self, key.meta.hold, False)
|
||||||
False, lambda: keyboard.resume_process_key(self, key.meta.hold, False)
|
|
||||||
)
|
|
||||||
|
|
||||||
def ht_activate_tap(self, key, keyboard, *args, **kwargs):
|
def ht_activate_tap(self, key, keyboard, *args, **kwargs):
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug('ht_activate_tap')
|
debug('ht_activate_tap')
|
||||||
keyboard.resume_process_key(self, key.meta.tap, True)
|
keyboard.resume_process_key(self, key.meta.tap, True)
|
||||||
|
|
||||||
def ht_deactivate_tap(self, key, keyboard, *args, delayed=True, **kwargs):
|
def ht_deactivate_tap(self, key, keyboard, *args, **kwargs):
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
debug('ht_deactivate_tap')
|
debug('ht_deactivate_tap')
|
||||||
if delayed:
|
keyboard.resume_process_key(self, key.meta.tap, False)
|
||||||
keyboard.set_timeout(
|
|
||||||
False, lambda: keyboard.resume_process_key(self, key.meta.tap, False)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
keyboard.resume_process_key(self, key.meta.tap, False)
|
|
||||||
|
|
||||||
def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs):
|
def ht_activate_on_interrupt(self, key, keyboard, *args, **kwargs):
|
||||||
if debug.enabled:
|
if debug.enabled:
|
||||||
@@ -224,4 +248,4 @@ class HoldTap(Module):
|
|||||||
if key.meta.prefer_hold:
|
if key.meta.prefer_hold:
|
||||||
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
|
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
self.ht_deactivate_tap(key, keyboard, *args, delayed=False, **kwargs)
|
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
'''One layer isn't enough. Adds keys to get to more of them'''
|
'''One layer isn't enough. Adds keys to get to more of them'''
|
||||||
from kmk.keys import KC, make_argumented_key
|
from kmk.keys import KC, make_argumented_key
|
||||||
from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKeyMeta
|
from kmk.modules.holdtap import HoldTap, HoldTapKeyMeta
|
||||||
from kmk.utils import Debug
|
from kmk.utils import Debug
|
||||||
|
|
||||||
debug = Debug(__name__)
|
debug = Debug(__name__)
|
||||||
@@ -73,20 +73,6 @@ class Layers(HoldTap):
|
|||||||
on_release=self.ht_released,
|
on_release=self.ht_released,
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_key(self, keyboard, key, is_pressed, int_coord):
|
|
||||||
current_key = super().process_key(keyboard, key, is_pressed, int_coord)
|
|
||||||
|
|
||||||
for key, state in self.key_states.items():
|
|
||||||
if key == current_key:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# on interrupt: key must be translated here, because it was asigned
|
|
||||||
# before the layer shift happend.
|
|
||||||
if state.activated == ActivationType.INTERRUPTED:
|
|
||||||
current_key = keyboard._find_key_in_map(int_coord)
|
|
||||||
|
|
||||||
return current_key
|
|
||||||
|
|
||||||
def _df_pressed(self, key, keyboard, *args, **kwargs):
|
def _df_pressed(self, key, keyboard, *args, **kwargs):
|
||||||
'''
|
'''
|
||||||
Switches the default layer
|
Switches the default layer
|
||||||
|
@@ -29,7 +29,13 @@ class OneShot(HoldTap):
|
|||||||
elif state.activated == ActivationType.RELEASED and is_pressed:
|
elif state.activated == ActivationType.RELEASED and is_pressed:
|
||||||
state.activated = ActivationType.INTERRUPTED
|
state.activated = ActivationType.INTERRUPTED
|
||||||
elif state.activated == ActivationType.INTERRUPTED:
|
elif state.activated == ActivationType.INTERRUPTED:
|
||||||
self.ht_released(key, keyboard)
|
if is_pressed:
|
||||||
|
keyboard.remove_key(key.meta.tap)
|
||||||
|
self.key_buffer.append((int_coord, current_key, is_pressed))
|
||||||
|
keyboard.set_timeout(False, lambda: self.send_key_buffer(keyboard))
|
||||||
|
current_key = None
|
||||||
|
else:
|
||||||
|
self.ht_released(key, keyboard)
|
||||||
|
|
||||||
return current_key
|
return current_key
|
||||||
|
|
||||||
@@ -37,6 +43,7 @@ class OneShot(HoldTap):
|
|||||||
'''Register HoldTap mechanism and activate os key.'''
|
'''Register HoldTap mechanism and activate os key.'''
|
||||||
self.ht_pressed(key, keyboard, *args, **kwargs)
|
self.ht_pressed(key, keyboard, *args, **kwargs)
|
||||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||||
|
self.send_key_buffer(keyboard)
|
||||||
return keyboard
|
return keyboard
|
||||||
|
|
||||||
def osk_released(self, key, keyboard, *args, **kwargs):
|
def osk_released(self, key, keyboard, *args, **kwargs):
|
||||||
|
@@ -1,13 +1,10 @@
|
|||||||
'''Enables splitting keyboards wirelessly or wired'''
|
'''Enables splitting keyboards wirelessly or wired'''
|
||||||
import busio
|
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from supervisor import runtime, ticks_ms
|
from supervisor import runtime
|
||||||
|
|
||||||
from keypad import Event as KeyEvent
|
from keypad import Event as KeyEvent
|
||||||
from storage import getmount
|
from storage import getmount
|
||||||
|
|
||||||
from kmk.hid import HIDModes
|
|
||||||
from kmk.kmktime import check_deadline
|
|
||||||
from kmk.modules import Module
|
from kmk.modules import Module
|
||||||
|
|
||||||
|
|
||||||
@@ -21,6 +18,7 @@ class SplitType:
|
|||||||
I2C = const(2) # unused
|
I2C = const(2) # unused
|
||||||
ONEWIRE = const(3) # unused
|
ONEWIRE = const(3) # unused
|
||||||
BLE = const(4)
|
BLE = const(4)
|
||||||
|
PIO_UART = const(5)
|
||||||
|
|
||||||
|
|
||||||
class Split(Module):
|
class Split(Module):
|
||||||
@@ -50,52 +48,79 @@ class Split(Module):
|
|||||||
self.data_pin2 = data_pin2
|
self.data_pin2 = data_pin2
|
||||||
self.uart_flip = uart_flip
|
self.uart_flip = uart_flip
|
||||||
self._use_pio = use_pio
|
self._use_pio = use_pio
|
||||||
self._uart = None
|
self._transport = None
|
||||||
self._uart_interval = uart_interval
|
self._uart_interval = uart_interval
|
||||||
self._debug_enabled = debug_enabled
|
self._debug_enabled = debug_enabled
|
||||||
self.uart_header = bytearray([0xB2]) # Any non-zero byte should work
|
self.uart_header = bytearray([0xB2]) # Any non-zero byte should work
|
||||||
|
|
||||||
if self.split_type == SplitType.BLE:
|
if split_type == SplitType.UART and use_pio:
|
||||||
try:
|
split_type = SplitType.PIO_UART
|
||||||
from adafruit_ble import BLERadio
|
|
||||||
from adafruit_ble.advertising.standard import (
|
|
||||||
ProvideServicesAdvertisement,
|
|
||||||
)
|
|
||||||
from adafruit_ble.services.nordic import UARTService
|
|
||||||
|
|
||||||
self.BLERadio = BLERadio
|
|
||||||
self.ProvideServicesAdvertisement = ProvideServicesAdvertisement
|
|
||||||
self.UARTService = UARTService
|
|
||||||
except ImportError:
|
|
||||||
print('BLE Import error')
|
|
||||||
return # BLE isn't supported on this platform
|
|
||||||
self._ble_last_scan = ticks_ms() - 5000
|
|
||||||
self._connection_count = 0
|
|
||||||
self._split_connected = False
|
|
||||||
self._uart_connection = None
|
|
||||||
self._advertisment = None # Seems to not be used anywhere
|
|
||||||
self._advertising = False
|
|
||||||
self._psave_enable = False
|
|
||||||
|
|
||||||
if self._use_pio:
|
|
||||||
from kmk.transports.pio_uart import PIO_UART
|
|
||||||
|
|
||||||
self.PIO_UART = PIO_UART
|
|
||||||
|
|
||||||
def during_bootup(self, keyboard):
|
def during_bootup(self, keyboard):
|
||||||
# Set up name for target side detection and BLE advertisment
|
# Set up name for target side detection and BLE advertisment
|
||||||
name = str(getmount('/').label)
|
if not self.data_pin:
|
||||||
if self.split_type == SplitType.BLE:
|
self.data_pin = keyboard.data_pin
|
||||||
if keyboard.hid_type == HIDModes.BLE:
|
|
||||||
self._ble = keyboard._hid_helper.ble
|
|
||||||
else:
|
|
||||||
self._ble = self.BLERadio()
|
|
||||||
self._ble.name = name
|
|
||||||
else:
|
|
||||||
# Try to guess data pins if not supplied
|
|
||||||
if not self.data_pin:
|
|
||||||
self.data_pin = keyboard.data_pin
|
|
||||||
|
|
||||||
|
self._get_side()
|
||||||
|
|
||||||
|
if not self._is_target:
|
||||||
|
keyboard._hid_send_enabled = False
|
||||||
|
|
||||||
|
if self.split_offset is None:
|
||||||
|
self.split_offset = keyboard.matrix[-1].coord_mapping[-1] + 1
|
||||||
|
|
||||||
|
self._init_transport(keyboard)
|
||||||
|
|
||||||
|
# Attempt to sanely guess a coord_mapping if one is not provided.
|
||||||
|
if not keyboard.coord_mapping:
|
||||||
|
self._guess_coord_mapping()
|
||||||
|
|
||||||
|
if self.split_side == SplitSide.RIGHT:
|
||||||
|
offset = self.split_offset
|
||||||
|
for matrix in keyboard.matrix:
|
||||||
|
matrix.offset = offset
|
||||||
|
offset += matrix.key_count
|
||||||
|
|
||||||
|
def before_matrix_scan(self, keyboard):
|
||||||
|
if self._can_receive(keyboard):
|
||||||
|
keyboard.secondary_matrix_update = self._transport.receive(keyboard)
|
||||||
|
|
||||||
|
def after_matrix_scan(self, keyboard):
|
||||||
|
if keyboard.matrix_update:
|
||||||
|
self._transport.write(keyboard, keyboard.matrix_update)
|
||||||
|
|
||||||
|
def before_hid_send(self, keyboard):
|
||||||
|
if not self._is_target:
|
||||||
|
keyboard.hid_pending = False
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def after_hid_send(self, keyboard):
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_powersave_enable(self, keyboard):
|
||||||
|
self._transport.powersave(True)
|
||||||
|
|
||||||
|
def on_powersave_disable(self, keyboard):
|
||||||
|
self._transport.powersave(False)
|
||||||
|
|
||||||
|
def _serialize_update(self, update):
|
||||||
|
buffer = bytearray(2)
|
||||||
|
buffer[0] = update.key_number
|
||||||
|
buffer[1] = update.pressed
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
def _deserialize_update(self, update):
|
||||||
|
kevent = KeyEvent(key_number=update[0], pressed=update[1])
|
||||||
|
return kevent
|
||||||
|
|
||||||
|
def _checksum(self, update):
|
||||||
|
checksum = bytes([sum(update) & 0xFF])
|
||||||
|
|
||||||
|
return checksum
|
||||||
|
|
||||||
|
def _get_side(self):
|
||||||
|
name = str(getmount('/').label)
|
||||||
# if split side was given, find target from split_side.
|
# if split side was given, find target from split_side.
|
||||||
if self.split_side == SplitSide.LEFT:
|
if self.split_side == SplitSide.LEFT:
|
||||||
self._is_target = bool(self.split_target_left)
|
self._is_target = bool(self.split_target_left)
|
||||||
@@ -108,277 +133,69 @@ class Split(Module):
|
|||||||
or self.split_type == SplitType.ONEWIRE
|
or self.split_type == SplitType.ONEWIRE
|
||||||
):
|
):
|
||||||
self._is_target = runtime.usb_connected
|
self._is_target = runtime.usb_connected
|
||||||
elif self.split_type == SplitType.BLE:
|
|
||||||
self._is_target = name.endswith('L') == self.split_target_left
|
|
||||||
|
|
||||||
if name.endswith('L'):
|
if name.endswith('L'):
|
||||||
self.split_side = SplitSide.LEFT
|
self.split_side = SplitSide.LEFT
|
||||||
elif name.endswith('R'):
|
elif name.endswith('R'):
|
||||||
self.split_side = SplitSide.RIGHT
|
self.split_side = SplitSide.RIGHT
|
||||||
|
|
||||||
if not self._is_target:
|
def _init_transport(self, keyboard):
|
||||||
keyboard._hid_send_enabled = False
|
if self.split_type == SplitType.UART:
|
||||||
|
from kmk.transports.uart import UART
|
||||||
|
elif self.split_type == SplitType.PIO_UART:
|
||||||
|
from kmk.transports.pio_uart import PIO_UART
|
||||||
|
elif self.split_type == SplitType.BLE:
|
||||||
|
from kmk.transports.ble import BLE_UART
|
||||||
|
|
||||||
if self.split_offset is None:
|
if self._is_target or not self.uart_flip:
|
||||||
self.split_offset = keyboard.matrix[-1].coord_mapping[-1] + 1
|
tx_pin = self.data_pin2
|
||||||
|
rx_pin = self.data_pin
|
||||||
|
else:
|
||||||
|
tx_pin = self.data_pin
|
||||||
|
rx_pin = self.data_pin2
|
||||||
|
|
||||||
if self.split_type == SplitType.UART and self.data_pin is not None:
|
if self.split_type == SplitType.UART:
|
||||||
if self._is_target or not self.uart_flip:
|
self._transport = UART(
|
||||||
if self._use_pio:
|
tx=tx_pin,
|
||||||
self._uart = self.PIO_UART(tx=self.data_pin2, rx=self.data_pin)
|
rx=rx_pin,
|
||||||
else:
|
target=self.is_target,
|
||||||
self._uart = busio.UART(
|
uart_interval=self._uart_interval,
|
||||||
tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval
|
)
|
||||||
)
|
elif self.split_type == SplitType.PIO_UART:
|
||||||
else:
|
self._transport = PIO_UART(tx=tx_pin, rx=rx_pin)
|
||||||
if self._use_pio:
|
elif self.split_type == SplitType.BLE:
|
||||||
self._uart = self.PIO_UART(tx=self.data_pin, rx=self.data_pin2)
|
self._transport = BLE_UART(
|
||||||
else:
|
target=self._is_target, uart_interval=self.uart_interval
|
||||||
self._uart = busio.UART(
|
)
|
||||||
tx=self.data_pin, rx=self.data_pin2, timeout=self._uart_interval
|
else:
|
||||||
)
|
raise NotImplementedError
|
||||||
|
|
||||||
# Attempt to sanely guess a coord_mapping if one is not provided.
|
def _guess_coord_mapping(self, keyboard):
|
||||||
if not keyboard.coord_mapping:
|
cm = []
|
||||||
cm = []
|
|
||||||
|
|
||||||
rows_to_calc = len(keyboard.row_pins)
|
rows_to_calc = len(keyboard.row_pins)
|
||||||
cols_to_calc = len(keyboard.col_pins)
|
cols_to_calc = len(keyboard.col_pins)
|
||||||
|
|
||||||
# Flips the col order if PCB is the same but flipped on right
|
# Flips the col order if PCB is the same but flipped on right
|
||||||
cols_rhs = list(range(cols_to_calc))
|
cols_rhs = list(range(cols_to_calc))
|
||||||
if self.split_flip:
|
if self.split_flip:
|
||||||
cols_rhs = list(reversed(cols_rhs))
|
cols_rhs = list(reversed(cols_rhs))
|
||||||
|
|
||||||
for ridx in range(rows_to_calc):
|
for ridx in range(rows_to_calc):
|
||||||
for cidx in range(cols_to_calc):
|
for cidx in range(cols_to_calc):
|
||||||
cm.append(cols_to_calc * ridx + cidx)
|
cm.append(cols_to_calc * ridx + cidx)
|
||||||
for cidx in cols_rhs:
|
for cidx in cols_rhs:
|
||||||
cm.append(cols_to_calc * (rows_to_calc + ridx) + cidx)
|
cm.append(cols_to_calc * (rows_to_calc + ridx) + cidx)
|
||||||
|
|
||||||
keyboard.coord_mapping = tuple(cm)
|
keyboard.coord_mapping = tuple(cm)
|
||||||
|
|
||||||
if self.split_side == SplitSide.RIGHT:
|
def _can_receive(self, keyboard) -> bool:
|
||||||
offset = self.split_offset
|
|
||||||
for matrix in keyboard.matrix:
|
|
||||||
matrix.offset = offset
|
|
||||||
offset += matrix.key_count
|
|
||||||
|
|
||||||
def before_matrix_scan(self, keyboard):
|
|
||||||
if self.split_type == SplitType.BLE:
|
if self.split_type == SplitType.BLE:
|
||||||
self._check_all_connections(keyboard)
|
self._transport.check_connection(keyboard)
|
||||||
self._receive_ble(keyboard)
|
|
||||||
elif self.split_type == SplitType.UART:
|
|
||||||
if self._is_target or self.data_pin2:
|
|
||||||
self._receive_uart(keyboard)
|
|
||||||
elif self.split_type == SplitType.ONEWIRE:
|
|
||||||
pass # Protocol needs written
|
|
||||||
return
|
|
||||||
|
|
||||||
def after_matrix_scan(self, keyboard):
|
|
||||||
if keyboard.matrix_update:
|
|
||||||
if self.split_type == SplitType.UART:
|
|
||||||
if not self._is_target or self.data_pin2:
|
|
||||||
self._send_uart(keyboard.matrix_update)
|
|
||||||
else:
|
|
||||||
pass # explicit pass just for dev sanity...
|
|
||||||
elif self.split_type == SplitType.BLE:
|
|
||||||
self._send_ble(keyboard.matrix_update)
|
|
||||||
elif self.split_type == SplitType.ONEWIRE:
|
|
||||||
pass # Protocol needs written
|
|
||||||
else:
|
|
||||||
print('Unexpected case in after_matrix_scan')
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def before_hid_send(self, keyboard):
|
|
||||||
if not self._is_target:
|
|
||||||
keyboard.hid_pending = False
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def after_hid_send(self, keyboard):
|
|
||||||
return
|
|
||||||
|
|
||||||
def on_powersave_enable(self, keyboard):
|
|
||||||
if self.split_type == SplitType.BLE:
|
|
||||||
if self._uart_connection and not self._psave_enable:
|
|
||||||
self._uart_connection.connection_interval = self._uart_interval
|
|
||||||
self._psave_enable = True
|
|
||||||
|
|
||||||
def on_powersave_disable(self, keyboard):
|
|
||||||
if self.split_type == SplitType.BLE:
|
|
||||||
if self._uart_connection and self._psave_enable:
|
|
||||||
self._uart_connection.connection_interval = 11.25
|
|
||||||
self._psave_enable = False
|
|
||||||
|
|
||||||
def _check_all_connections(self, keyboard):
|
|
||||||
'''Validates the correct number of BLE connections'''
|
|
||||||
self._previous_connection_count = self._connection_count
|
|
||||||
self._connection_count = len(self._ble.connections)
|
|
||||||
if self._is_target:
|
|
||||||
if self._advertising or not self._check_if_split_connected():
|
|
||||||
self._target_advertise()
|
|
||||||
elif self._connection_count < 2 and keyboard.hid_type == HIDModes.BLE:
|
|
||||||
keyboard._hid_helper.start_advertising()
|
|
||||||
|
|
||||||
elif not self._is_target and self._connection_count < 1:
|
|
||||||
self._initiator_scan()
|
|
||||||
|
|
||||||
def _check_if_split_connected(self):
|
|
||||||
# I'm looking for a way how to recognize which connection is on and which one off
|
|
||||||
# For now, I found that service name relation to having other CP device
|
|
||||||
if self._connection_count == 0:
|
|
||||||
return False
|
|
||||||
if self._connection_count == 2:
|
|
||||||
self._split_connected = True
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Polling this takes some time so I check only if connection_count changed
|
elif self.split_type == SplitType.UART:
|
||||||
if self._previous_connection_count == self._connection_count:
|
if self._is_target or self.data_pin2:
|
||||||
return self._split_connected
|
|
||||||
|
|
||||||
bleio_connection = self._ble.connections[0]._bleio_connection
|
|
||||||
connection_services = bleio_connection.discover_remote_services()
|
|
||||||
for service in connection_services:
|
|
||||||
if str(service.uuid).startswith("UUID('adaf0001"):
|
|
||||||
self._split_connected = True
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _initiator_scan(self):
|
|
||||||
'''Scans for target device'''
|
|
||||||
self._uart = None
|
|
||||||
self._uart_connection = None
|
|
||||||
# See if any existing connections are providing UARTService.
|
|
||||||
self._connection_count = len(self._ble.connections)
|
|
||||||
if self._connection_count > 0 and not self._uart:
|
|
||||||
for connection in self._ble.connections:
|
|
||||||
if self.UARTService in connection:
|
|
||||||
self._uart_connection = connection
|
|
||||||
self._uart_connection.connection_interval = 11.25
|
|
||||||
self._uart = self._uart_connection[self.UARTService]
|
|
||||||
break
|
|
||||||
|
|
||||||
if not self._uart:
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Scanning')
|
|
||||||
self._ble.stop_scan()
|
|
||||||
for adv in self._ble.start_scan(
|
|
||||||
self.ProvideServicesAdvertisement, timeout=20
|
|
||||||
):
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Scanning')
|
|
||||||
if self.UARTService in adv.services and adv.rssi > -70:
|
|
||||||
self._uart_connection = self._ble.connect(adv)
|
|
||||||
self._uart_connection.connection_interval = 11.25
|
|
||||||
self._uart = self._uart_connection[self.UARTService]
|
|
||||||
self._ble.stop_scan()
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Scan complete')
|
|
||||||
break
|
|
||||||
self._ble.stop_scan()
|
|
||||||
|
|
||||||
def _target_advertise(self):
|
|
||||||
'''Advertises the target for the initiator to find'''
|
|
||||||
# Give previous advertising some time to complete
|
|
||||||
if self._advertising:
|
|
||||||
if self._check_if_split_connected():
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Advertising complete')
|
|
||||||
self._ble.stop_advertising()
|
|
||||||
self._advertising = False
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.ble_rescan_timer():
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Advertising not answered')
|
|
||||||
|
|
||||||
self._ble.stop_advertising()
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Advertising')
|
|
||||||
# Uart must not change on this connection if reconnecting
|
|
||||||
if not self._uart:
|
|
||||||
self._uart = self.UARTService()
|
|
||||||
advertisement = self.ProvideServicesAdvertisement(self._uart)
|
|
||||||
|
|
||||||
self._ble.start_advertising(advertisement)
|
|
||||||
self._advertising = True
|
|
||||||
self.ble_time_reset()
|
|
||||||
|
|
||||||
def ble_rescan_timer(self):
|
|
||||||
'''If true, the rescan timer is up'''
|
|
||||||
return not bool(check_deadline(ticks_ms(), self._ble_last_scan, 5000))
|
|
||||||
|
|
||||||
def ble_time_reset(self):
|
|
||||||
'''Resets the rescan timer'''
|
|
||||||
self._ble_last_scan = ticks_ms()
|
|
||||||
|
|
||||||
def _serialize_update(self, update):
|
|
||||||
buffer = bytearray(2)
|
|
||||||
buffer[0] = update.key_number
|
|
||||||
buffer[1] = update.pressed
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
def _deserialize_update(self, update):
|
|
||||||
kevent = KeyEvent(key_number=update[0], pressed=update[1])
|
|
||||||
return kevent
|
|
||||||
|
|
||||||
def _send_ble(self, update):
|
|
||||||
if self._uart:
|
|
||||||
try:
|
|
||||||
self._uart.write(self._serialize_update(update))
|
|
||||||
except OSError:
|
|
||||||
try:
|
|
||||||
self._uart.disconnect()
|
|
||||||
except: # noqa: E722
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('UART disconnect failed')
|
|
||||||
|
|
||||||
if self._debug_enabled:
|
|
||||||
print('Connection error')
|
|
||||||
self._uart_connection = None
|
|
||||||
self._uart = None
|
|
||||||
|
|
||||||
def _receive_ble(self, keyboard):
|
|
||||||
if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer:
|
|
||||||
while self._uart.in_waiting >= 2:
|
|
||||||
update = self._deserialize_update(self._uart.read(2))
|
|
||||||
self._uart_buffer.append(update)
|
|
||||||
if self._uart_buffer:
|
|
||||||
keyboard.secondary_matrix_update = self._uart_buffer.pop(0)
|
|
||||||
|
|
||||||
def _checksum(self, update):
|
|
||||||
checksum = bytes([sum(update) & 0xFF])
|
|
||||||
|
|
||||||
return checksum
|
|
||||||
|
|
||||||
def _send_uart(self, update):
|
|
||||||
# Change offsets depending on where the data is going to match the correct
|
|
||||||
# matrix location of the receiever
|
|
||||||
if self._uart is not None:
|
|
||||||
update = self._serialize_update(update)
|
|
||||||
self._uart.write(self.uart_header)
|
|
||||||
self._uart.write(update)
|
|
||||||
self._uart.write(self._checksum(update))
|
|
||||||
|
|
||||||
def _receive_uart(self, keyboard):
|
|
||||||
if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer:
|
|
||||||
if self._uart.in_waiting >= 60:
|
|
||||||
# This is a dirty hack to prevent crashes in unrealistic cases
|
|
||||||
import microcontroller
|
|
||||||
|
|
||||||
microcontroller.reset()
|
|
||||||
|
|
||||||
while self._uart.in_waiting >= 4:
|
|
||||||
# Check the header
|
|
||||||
if self._uart.read(1) == self.uart_header:
|
|
||||||
update = self._uart.read(2)
|
|
||||||
|
|
||||||
# check the checksum
|
|
||||||
if self._checksum(update) == self._uart.read(1):
|
|
||||||
self._uart_buffer.append(self._deserialize_update(update))
|
|
||||||
if self._uart_buffer:
|
|
||||||
keyboard.secondary_matrix_update = self._uart_buffer.pop(0)
|
|
||||||
|
@@ -48,8 +48,11 @@ class TapDance(HoldTap):
|
|||||||
if state.activated == ActivationType.RELEASED:
|
if state.activated == ActivationType.RELEASED:
|
||||||
keyboard.cancel_timeout(state.timeout_key)
|
keyboard.cancel_timeout(state.timeout_key)
|
||||||
self.ht_activate_tap(_key, keyboard)
|
self.ht_activate_tap(_key, keyboard)
|
||||||
keyboard._send_hid()
|
self.send_key_buffer(keyboard)
|
||||||
self.ht_deactivate_tap(_key, keyboard, delayed=False)
|
self.ht_deactivate_tap(_key, keyboard)
|
||||||
|
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||||
|
key = None
|
||||||
|
|
||||||
del self.key_states[_key]
|
del self.key_states[_key]
|
||||||
del self.td_counts[state.tap_dance]
|
del self.td_counts[state.tap_dance]
|
||||||
|
|
||||||
@@ -114,6 +117,6 @@ class TapDance(HoldTap):
|
|||||||
state = self.key_states[key]
|
state = self.key_states[key]
|
||||||
if state.activated == ActivationType.RELEASED:
|
if state.activated == ActivationType.RELEASED:
|
||||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||||
keyboard._send_hid()
|
self.send_key_buffer(keyboard)
|
||||||
del self.td_counts[state.tap_dance]
|
del self.td_counts[state.tap_dance]
|
||||||
super().on_tap_time_expired(key, keyboard, *args, **kwargs)
|
super().on_tap_time_expired(key, keyboard, *args, **kwargs)
|
||||||
|
20
kmk/quickpin/pro_micro/avr_promicro.py
Normal file
20
kmk/quickpin/pro_micro/avr_promicro.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
translate = {
|
||||||
|
'D3': 0,
|
||||||
|
'D2': 1,
|
||||||
|
'D1': 4,
|
||||||
|
'D0': 5,
|
||||||
|
'D4': 6,
|
||||||
|
'C6': 7,
|
||||||
|
'D7': 8,
|
||||||
|
'E6': 9,
|
||||||
|
'B4': 10,
|
||||||
|
'B5': 11,
|
||||||
|
'B6': 12,
|
||||||
|
'B2': 13,
|
||||||
|
'B3': 14,
|
||||||
|
'B1': 15,
|
||||||
|
'F7': 16,
|
||||||
|
'F6': 17,
|
||||||
|
'F5': 18,
|
||||||
|
'F4': 19,
|
||||||
|
}
|
28
kmk/quickpin/pro_micro/boardsource_blok.py
Normal file
28
kmk/quickpin/pro_micro/boardsource_blok.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
pinout = [
|
||||||
|
board.TX,
|
||||||
|
board.RX,
|
||||||
|
None, # GND
|
||||||
|
None, # GND
|
||||||
|
board.SDA,
|
||||||
|
board.SCL,
|
||||||
|
board.GP04,
|
||||||
|
board.GP05,
|
||||||
|
board.GP06,
|
||||||
|
board.GP07,
|
||||||
|
board.GP08,
|
||||||
|
board.GP09,
|
||||||
|
board.GP21,
|
||||||
|
board.GP23,
|
||||||
|
board.GP20,
|
||||||
|
board.GP22,
|
||||||
|
board.GP26,
|
||||||
|
board.GP27,
|
||||||
|
board.GP28,
|
||||||
|
board.GP29,
|
||||||
|
None, # 3.3v
|
||||||
|
None, # RST
|
||||||
|
None, # GND
|
||||||
|
None, # RAW
|
||||||
|
]
|
28
kmk/quickpin/pro_micro/nice_nano.py
Normal file
28
kmk/quickpin/pro_micro/nice_nano.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
pinout = [
|
||||||
|
board.TX,
|
||||||
|
board.RX,
|
||||||
|
None, # GND
|
||||||
|
None, # GND
|
||||||
|
board.SDA,
|
||||||
|
board.SCL,
|
||||||
|
board.P0_22,
|
||||||
|
board.P0_24,
|
||||||
|
board.P1_00,
|
||||||
|
board.P0_11,
|
||||||
|
board.P1_04,
|
||||||
|
board.P1_06,
|
||||||
|
board.P0_09,
|
||||||
|
board.P0_10,
|
||||||
|
board.P1_11,
|
||||||
|
board.P1_13,
|
||||||
|
board.P1_15,
|
||||||
|
board.P0_02,
|
||||||
|
board.P0_29,
|
||||||
|
board.P0_31,
|
||||||
|
None, # 3.3v
|
||||||
|
None, # RST
|
||||||
|
None, # GND
|
||||||
|
None, # Battery+
|
||||||
|
]
|
28
kmk/quickpin/pro_micro/sparkfun_promicro_rp2040.py
Normal file
28
kmk/quickpin/pro_micro/sparkfun_promicro_rp2040.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import board
|
||||||
|
|
||||||
|
pinout = [
|
||||||
|
board.TX,
|
||||||
|
board.RX,
|
||||||
|
None, # GND
|
||||||
|
None, # GND
|
||||||
|
board.D2,
|
||||||
|
board.D3,
|
||||||
|
board.D4,
|
||||||
|
board.D5,
|
||||||
|
board.D6,
|
||||||
|
board.D7,
|
||||||
|
board.D8,
|
||||||
|
board.D9,
|
||||||
|
board.D21,
|
||||||
|
board.MOSI,
|
||||||
|
board.MISO,
|
||||||
|
board.SCK,
|
||||||
|
board.D26,
|
||||||
|
board.D27,
|
||||||
|
board.D28,
|
||||||
|
board.D29,
|
||||||
|
None, # 3.3v
|
||||||
|
None, # RST
|
||||||
|
None, # GND
|
||||||
|
None, # RAW
|
||||||
|
]
|
181
kmk/transports/ble.py
Normal file
181
kmk/transports/ble.py
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
from supervisor import ticks_ms
|
||||||
|
|
||||||
|
from kmk.hid import HIDModes
|
||||||
|
from kmk.kmktime import check_deadline
|
||||||
|
|
||||||
|
|
||||||
|
class BLE_UART:
|
||||||
|
def __init__(self, is_target=True, uart_interval=20):
|
||||||
|
try:
|
||||||
|
from adafruit_ble import BLERadio
|
||||||
|
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
|
||||||
|
from adafruit_ble.services.nordic import UARTService
|
||||||
|
|
||||||
|
self.BLERadio = BLERadio
|
||||||
|
self.ProvideServicesAdvertisement = ProvideServicesAdvertisement
|
||||||
|
self.UARTService = UARTService
|
||||||
|
except ImportError:
|
||||||
|
print('BLE Import error')
|
||||||
|
return # BLE isn't supported on this platform
|
||||||
|
|
||||||
|
self._debug_enabled = True
|
||||||
|
self._ble_last_scan = ticks_ms() - 5000
|
||||||
|
self._connection_count = 0
|
||||||
|
self._split_connected = False
|
||||||
|
self._uart_connection = None
|
||||||
|
self._advertisment = None # Seems to not be used anywhere
|
||||||
|
self._advertising = False
|
||||||
|
self._psave_enabled = False
|
||||||
|
self._uart = None
|
||||||
|
self._uart_interval = uart_interval
|
||||||
|
self._is_target = is_target
|
||||||
|
|
||||||
|
@property
|
||||||
|
def in_waiting(self):
|
||||||
|
return self._uart.in_waiting
|
||||||
|
|
||||||
|
def read(self, n):
|
||||||
|
return self._uart.read(n)
|
||||||
|
|
||||||
|
def readinto(self, buf):
|
||||||
|
return self._uart.readinto(buf)
|
||||||
|
|
||||||
|
def write(self, buffer):
|
||||||
|
try:
|
||||||
|
self._uart.write(buffer)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
self._uart.disconnect()
|
||||||
|
except: # noqa: E722
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('UART disconnect failed')
|
||||||
|
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Connection error')
|
||||||
|
self._uart_connection = None
|
||||||
|
self._uart = None
|
||||||
|
|
||||||
|
def check_connection(self, keyboard):
|
||||||
|
self._check_all_connections(keyboard)
|
||||||
|
|
||||||
|
def powersave(self, enable=True):
|
||||||
|
if enable:
|
||||||
|
if self._uart_connection and not self._psave_enable:
|
||||||
|
self._uart_connection.connection_interval = self._uart_interval
|
||||||
|
self._psave_enabled = True
|
||||||
|
else:
|
||||||
|
if self._uart_connection and self._psave_enable:
|
||||||
|
self._uart_connection.connection_interval = 11.25
|
||||||
|
self._psave_enable = False
|
||||||
|
|
||||||
|
def _check_all_connections(self, keyboard):
|
||||||
|
'''Validates the correct number of BLE connections'''
|
||||||
|
self._previous_connection_count = self._connection_count
|
||||||
|
self._connection_count = len(self._ble.connections)
|
||||||
|
if self._is_target:
|
||||||
|
if self._advertising or not self._check_if_split_connected():
|
||||||
|
self._target_advertise()
|
||||||
|
elif self._connection_count < 2 and keyboard.hid_type == HIDModes.BLE:
|
||||||
|
keyboard._hid_helper.start_advertising()
|
||||||
|
|
||||||
|
elif not self._is_target and self._connection_count < 1:
|
||||||
|
self._initiator_scan()
|
||||||
|
|
||||||
|
def _check_if_split_connected(self):
|
||||||
|
# I'm looking for a way how to recognize which connection is on and which one off
|
||||||
|
# For now, I found that service name relation to having other CP device
|
||||||
|
if self._connection_count == 0:
|
||||||
|
return False
|
||||||
|
if self._connection_count == 2:
|
||||||
|
self._split_connected = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Polling this takes some time so I check only if connection_count changed
|
||||||
|
if self._previous_connection_count == self._connection_count:
|
||||||
|
return self._split_connected
|
||||||
|
|
||||||
|
bleio_connection = self._ble.connections[0]._bleio_connection
|
||||||
|
connection_services = bleio_connection.discover_remote_services()
|
||||||
|
for service in connection_services:
|
||||||
|
if str(service.uuid).startswith("UUID('adaf0001"):
|
||||||
|
self._split_connected = True
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _initiator_scan(self):
|
||||||
|
'''Scans for target device'''
|
||||||
|
self._uart = None
|
||||||
|
self._uart_connection = None
|
||||||
|
# See if any existing connections are providing UARTService.
|
||||||
|
self._connection_count = len(self._ble.connections)
|
||||||
|
if self._connection_count > 0 and not self._uart:
|
||||||
|
for connection in self._ble.connections:
|
||||||
|
if self.UARTService in connection:
|
||||||
|
self._uart_connection = connection
|
||||||
|
self._uart_connection.connection_interval = 11.25
|
||||||
|
self._uart = self._uart_connection[self.UARTService]
|
||||||
|
break
|
||||||
|
|
||||||
|
if not self._uart:
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Scanning')
|
||||||
|
self._ble.stop_scan()
|
||||||
|
for adv in self._ble.start_scan(
|
||||||
|
self.ProvideServicesAdvertisement, timeout=20
|
||||||
|
):
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Scanning')
|
||||||
|
if self.UARTService in adv.services and adv.rssi > -70:
|
||||||
|
self._uart_connection = self._ble.connect(adv)
|
||||||
|
self._uart_connection.connection_interval = 11.25
|
||||||
|
self._uart = self._uart_connection[self.UARTService]
|
||||||
|
self._ble.stop_scan()
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Scan complete')
|
||||||
|
break
|
||||||
|
self._ble.stop_scan()
|
||||||
|
|
||||||
|
def _target_advertise(self):
|
||||||
|
'''Advertises the target for the initiator to find'''
|
||||||
|
# Give previous advertising some time to complete
|
||||||
|
if self._advertising:
|
||||||
|
if self._check_if_split_connected():
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Advertising complete')
|
||||||
|
self._ble.stop_advertising()
|
||||||
|
self._advertising = False
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.ble_rescan_timer():
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Advertising not answered')
|
||||||
|
|
||||||
|
self._ble.stop_advertising()
|
||||||
|
if self._debug_enabled:
|
||||||
|
print('Advertising')
|
||||||
|
# Uart must not change on this connection if reconnecting
|
||||||
|
if not self._uart:
|
||||||
|
self._uart = self.UARTService()
|
||||||
|
advertisement = self.ProvideServicesAdvertisement(self._uart)
|
||||||
|
|
||||||
|
self._ble.start_advertising(advertisement)
|
||||||
|
self._advertising = True
|
||||||
|
self.ble_time_reset()
|
||||||
|
|
||||||
|
def ble_rescan_timer(self):
|
||||||
|
'''If true, the rescan timer is up'''
|
||||||
|
return not bool(check_deadline(ticks_ms(), self._ble_last_scan, 5000))
|
||||||
|
|
||||||
|
def ble_time_reset(self):
|
||||||
|
'''Resets the rescan timer'''
|
||||||
|
self._ble_last_scan = ticks_ms()
|
||||||
|
|
||||||
|
def receive(self, keyboard):
|
||||||
|
if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer:
|
||||||
|
while self._uart.in_waiting >= 2:
|
||||||
|
update = self._deserialize_update(self._uart.read(2))
|
||||||
|
self._uart_buffer.append(update)
|
||||||
|
if self._uart_buffer:
|
||||||
|
keyboard.secondary_matrix_update = self._uart_buffer.pop(0)
|
51
kmk/transports/uart.py
Normal file
51
kmk/transports/uart.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
class UART:
|
||||||
|
def __init__(self, tx=None, rx=None, is_target=True, uart_interval=20):
|
||||||
|
self._debug_enabled = True
|
||||||
|
self._uart_connection = None
|
||||||
|
self._uart = None
|
||||||
|
self._uart_interval = uart_interval
|
||||||
|
self._is_target = is_target
|
||||||
|
|
||||||
|
@property
|
||||||
|
def in_waiting(self):
|
||||||
|
return self._uart.in_waiting
|
||||||
|
|
||||||
|
def read(self, n):
|
||||||
|
return self._uart.read(n)
|
||||||
|
|
||||||
|
def readinto(self, buf):
|
||||||
|
return self._uart.readinto(buf)
|
||||||
|
|
||||||
|
def powersave(enable: bool):
|
||||||
|
return
|
||||||
|
|
||||||
|
def write(self, buffer, update):
|
||||||
|
if self._uart is not None:
|
||||||
|
if not self._is_target or self.data_pin2:
|
||||||
|
update = self._serialize_update(update)
|
||||||
|
self._uart.write(self.uart_header)
|
||||||
|
self._uart.write(update)
|
||||||
|
self._uart.write(self._checksum(update))
|
||||||
|
|
||||||
|
def receive(self, keyboard):
|
||||||
|
if self._transport.in_waiting > 0 or self._uart_buffer:
|
||||||
|
if self._transport.in_waiting >= 60:
|
||||||
|
# This is a dirty hack to prevent crashes in unrealistic cases
|
||||||
|
# TODO See if this hack is needed with checksum, and if not, use
|
||||||
|
# that to fix it.
|
||||||
|
import microcontroller
|
||||||
|
|
||||||
|
microcontroller.reset()
|
||||||
|
|
||||||
|
while self._transport.in_waiting >= 4:
|
||||||
|
# Check the header
|
||||||
|
if self._transport.read(1) == self.uart_header:
|
||||||
|
update = self._transport.read(2)
|
||||||
|
|
||||||
|
# check the checksum
|
||||||
|
if self._checksum(update) == self._transport.read(1):
|
||||||
|
self._uart_buffer.append(self._deserialize_update(update))
|
||||||
|
if self._uart_buffer:
|
||||||
|
return self._uart_buffer.pop(0)
|
||||||
|
|
||||||
|
return None
|
10
kmk/utils.py
10
kmk/utils.py
@@ -1,7 +1,7 @@
|
|||||||
from supervisor import ticks_ms
|
from supervisor import ticks_ms
|
||||||
|
|
||||||
|
|
||||||
def clamp(x, bottom=0, top=100):
|
def clamp(x: int, bottom: int = 0, top: int = 100) -> int:
|
||||||
return min(max(bottom, x), top)
|
return min(max(bottom, x), top)
|
||||||
|
|
||||||
|
|
||||||
@@ -13,18 +13,18 @@ class Debug:
|
|||||||
debug = Debug(__name__)
|
debug = Debug(__name__)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, name=__name__):
|
def __init__(self, name: str = __name__):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def __call__(self, message):
|
def __call__(self, message: str) -> None:
|
||||||
print(f'{ticks_ms()} {self.name}: {message}')
|
print(f'{ticks_ms()} {self.name}: {message}')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self) -> bool:
|
||||||
global _debug_enabled
|
global _debug_enabled
|
||||||
return _debug_enabled
|
return _debug_enabled
|
||||||
|
|
||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled: bool):
|
||||||
global _debug_enabled
|
global _debug_enabled
|
||||||
_debug_enabled = enabled
|
_debug_enabled = enabled
|
||||||
|
@@ -74,6 +74,7 @@ class KeyboardTest:
|
|||||||
is_pressed = e[1]
|
is_pressed = e[1]
|
||||||
self.pins[key_pos].value = is_pressed
|
self.pins[key_pos].value = is_pressed
|
||||||
self.do_main_loop()
|
self.do_main_loop()
|
||||||
|
self.keyboard._main_loop()
|
||||||
|
|
||||||
matching = True
|
matching = True
|
||||||
for i in range(max(len(hid_reports), len(assert_reports))):
|
for i in range(max(len(hid_reports), len(assert_reports))):
|
||||||
|
@@ -192,7 +192,14 @@ class TestHoldTap(unittest.TestCase):
|
|||||||
keyboard.test(
|
keyboard.test(
|
||||||
'chained 4',
|
'chained 4',
|
||||||
[(1, True), (3, True), (0, True), (3, False), (1, False), (0, False)],
|
[(1, True), (3, True), (0, True), (3, False), (1, False), (0, False)],
|
||||||
[{KC.LCTL}, {KC.LCTL, KC.N3, KC.N0}, {KC.LCTL, KC.N0}, {KC.N0}, {}],
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.N3},
|
||||||
|
{KC.LCTL, KC.N0, KC.N3},
|
||||||
|
{KC.LCTL, KC.N0},
|
||||||
|
{KC.N0},
|
||||||
|
{},
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
@@ -293,25 +300,25 @@ class TestHoldTap(unittest.TestCase):
|
|||||||
keyboard.test(
|
keyboard.test(
|
||||||
'OS interrupt within tap time',
|
'OS interrupt within tap time',
|
||||||
[(4, True), (4, False), t_within, (3, True), (3, False)],
|
[(4, True), (4, False), t_within, (3, True), (3, False)],
|
||||||
[{KC.E}, {KC.D, KC.E}, {}],
|
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
'OS interrupt, multiple within tap time',
|
'OS interrupt, multiple within tap time',
|
||||||
[(4, True), (4, False), (3, True), (3, False), (2, True), (2, False)],
|
[(4, True), (4, False), (3, True), (3, False), (2, True), (2, False)],
|
||||||
[{KC.E}, {KC.D, KC.E}, {}, {KC.C}, {}],
|
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}, {KC.C}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
'OS interrupt, multiple interleaved',
|
'OS interrupt, multiple interleaved',
|
||||||
[(4, True), (4, False), (3, True), (2, True), (2, False), (3, False)],
|
[(4, True), (4, False), (3, True), (2, True), (2, False), (3, False)],
|
||||||
[{KC.E}, {KC.D, KC.E}, {KC.C, KC.D}, {KC.D}, {}],
|
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.D}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
'OS interrupt, multiple interleaved',
|
'OS interrupt, multiple interleaved',
|
||||||
[(4, True), (4, False), (3, True), (2, True), (3, False), (2, False)],
|
[(4, True), (4, False), (3, True), (2, True), (3, False), (2, False)],
|
||||||
[{KC.E}, {KC.D, KC.E}, {KC.C, KC.D}, {KC.C}, {}],
|
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.C}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
|
@@ -71,7 +71,7 @@ class TestTapDance(unittest.TestCase):
|
|||||||
keyboard.test(
|
keyboard.test(
|
||||||
'Tap x1 interrupted',
|
'Tap x1 interrupted',
|
||||||
[(0, True), (0, False), (4, True), (4, False)],
|
[(0, True), (0, False), (4, True), (4, False)],
|
||||||
[{KC.N0}, {KC.N4}, {}],
|
[{KC.N0}, {}, {KC.N4}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard.test(
|
keyboard.test(
|
||||||
|
Reference in New Issue
Block a user