Compare commits
1 Commits
refactor-s
...
topic-memo
Author | SHA1 | Date | |
---|---|---|---|
|
9e8c9c3669 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -180,5 +180,3 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
|
||||
# Mac Finder
|
||||
.DS_Store
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||
from kmk.quickpin.pro_micro.boardsource_blok import pinout as pins
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
|
||||
class KMKKeyboard(_KMKKeyboard):
|
||||
row_pins = (pins[16], pins[17], pins[18])
|
||||
col_pins = (pins[12], pins[13], pins[14], pins[15])
|
||||
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)
|
||||
diode_orientation = DiodeOrientation.COLUMNS
|
||||
i2c = board.I2C
|
||||
powersave_pin = board.P0_13
|
||||
|
@@ -1,35 +1,30 @@
|
||||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||
from kmk.quickpin.pro_micro.boardsource_blok import pinout as pins
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
|
||||
class KMKKeyboard(_KMKKeyboard):
|
||||
col_pins = (
|
||||
pins[19],
|
||||
pins[18],
|
||||
pins[17],
|
||||
pins[16],
|
||||
pins[15],
|
||||
pins[14],
|
||||
)
|
||||
row_pins = (
|
||||
pins[6],
|
||||
pins[7],
|
||||
pins[8],
|
||||
pins[9],
|
||||
board.P0_31,
|
||||
board.P0_29,
|
||||
board.P0_02,
|
||||
board.P1_15,
|
||||
board.P1_13,
|
||||
board.P1_11,
|
||||
)
|
||||
row_pins = (board.P0_22, board.P0_24, board.P1_00, board.P0_11)
|
||||
diode_orientation = DiodeOrientation.COLUMNS
|
||||
data_pin = pins[1]
|
||||
rgb_pixel_pin = pins[0]
|
||||
data_pin = board.P0_08
|
||||
rgb_pixel_pin = board.P0_06
|
||||
i2c = board.I2C
|
||||
powersave_pin = board.P0_13
|
||||
|
||||
# flake8: noqa
|
||||
# fmt: off
|
||||
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,
|
||||
]
|
||||
|
||||
|
28
boards/crkbd/kb_rp2040.py
Normal file
28
boards/crkbd/kb_rp2040.py
Normal file
@@ -0,0 +1,28 @@
|
||||
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,9 +41,8 @@ from kmk.extensions.rgb import RGB, AnimationModes
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.keypad import KeysScanner
|
||||
|
||||
|
||||
# fmt: off
|
||||
|
||||
|
||||
def raspi_pins():
|
||||
return [
|
||||
board.D20, board.D16, board.D26,
|
||||
@@ -53,15 +52,6 @@ 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():
|
||||
return [
|
||||
board.D11, board.D12, board.D2,
|
||||
@@ -69,7 +59,6 @@ def itsybitsy_pins():
|
||||
board.A5, board.A4, board.A3,
|
||||
board.A2, board.A1, board.A0,
|
||||
]
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
@@ -77,17 +66,9 @@ def isPi():
|
||||
return sys.platform == 'BROADCOM'
|
||||
|
||||
|
||||
def isRP2040():
|
||||
return sys.platform == 'RP2040'
|
||||
|
||||
|
||||
if isPi():
|
||||
_KEY_CFG = raspi_pins()
|
||||
_LED_PINS = (board.SCK, board.MOSI)
|
||||
elif isRP2040():
|
||||
_KEY_CFG = rp2040_pins()
|
||||
_LED_PINS = (board.GP2, board.GP3)
|
||||
|
||||
else:
|
||||
_KEY_CFG = itsybitsy_pins()
|
||||
_LED_PINS = (board.SCK, board.MOSI)
|
||||
|
@@ -1,15 +0,0 @@
|
||||
# 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.
|
@@ -1,68 +0,0 @@
|
||||
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
|
||||
]
|
@@ -1,709 +0,0 @@
|
||||
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,21 +38,18 @@ import storage
|
||||
import usb_cdc
|
||||
import usb_hid
|
||||
|
||||
from kb import KMKKeyboard
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
# This is from the base kmk boot.py
|
||||
supervisor.set_next_stack_limit(4096 + 4096)
|
||||
|
||||
# If this key is held during boot, don't run the code which hides the storage and disables serial
|
||||
# This will use the first row/col pin. Feel free to change it if you want it to be another pin
|
||||
col = digitalio.DigitalInOut(KMKKeyboard.col_pins[0])
|
||||
row = digitalio.DigitalInOut(KMKKeyboard.row_pins[0])
|
||||
# To use another key just count its row and column and use those pins
|
||||
# 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(board.GP2)
|
||||
row = digitalio.DigitalInOut(board.GP13)
|
||||
|
||||
if KMKKeyboard.diode_orientation == DiodeOrientation.COLUMNS:
|
||||
col.switch_to_output(value=True)
|
||||
row.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
else:
|
||||
col.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
row.switch_to_output(value=True)
|
||||
# TODO: If your diode orientation is ROW2COL, then make row the output and col the input
|
||||
col.switch_to_output(value=True)
|
||||
row.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
|
||||
if not row.value:
|
||||
storage.disable_usb_drive()
|
||||
|
@@ -27,7 +27,6 @@ Optional arguments that customize individual combos:
|
||||
* `per_key_timeout`: If True, reset timeout on every key press (default for
|
||||
sequences).
|
||||
* `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
|
||||
```python
|
||||
@@ -44,8 +43,6 @@ make_key(
|
||||
combos.combos = [
|
||||
Chord((KC.A, KC.B), KC.LSFT),
|
||||
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.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
|
||||
]
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 400 KiB |
@@ -68,26 +68,3 @@ 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_VOL_UP` |`KC.VOLU` |Volume Up |
|
||||
|`KC.AUDIO_VOL_DOWN` |`KC.VOLD` |Volume Down |
|
||||
|`KC.BRIGHTNESS_UP` |`KC.BRIU` |Brightness Up |
|
||||
|`KC.BRIGHTESS_UP` |`KC.BRIU` |Brightness Up |
|
||||
|`KC.BRIGHTNESS_DOWN` |`KC.BRID` |Brightness Down |
|
||||
|`KC.MEDIA_NEXT_TRACK` |`KC.MNXT` |Next Track (Windows) |
|
||||
|`KC.MEDIA_PREV_TRACK` |`KC.MPRV` |Previous Track (Windows) |
|
||||
|
@@ -31,7 +31,7 @@ keyboard.modules.append(modtap)
|
||||
## Custom HoldTap Behavior
|
||||
The full ModTap signature is as follows:
|
||||
```python
|
||||
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=False)
|
||||
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None)
|
||||
```
|
||||
* `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
|
||||
@@ -40,11 +40,5 @@ KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, r
|
||||
key press/down, or after the first other key up/release. Set to `True` for
|
||||
interrupt on release.
|
||||
* `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.
|
||||
|
@@ -1,55 +1,44 @@
|
||||
# Peg RGB Matrix
|
||||
|
||||
## What you can and cannot do with this extension:
|
||||
|
||||
### Can Do
|
||||
### What you can and cannot do with this extension:
|
||||
|
||||
#### Can Do
|
||||
* 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 underglow LEDs to be a different color than per-key LEDs
|
||||
* Allows modifier keys to be set to a different color than alpha keys
|
||||
* 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.
|
||||
* Animations
|
||||
* Change LED color based on current layer
|
||||
|
||||
## Keycodes
|
||||
|
||||
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.
|
||||
### 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.
|
||||
|
||||
## Required Libraries
|
||||
|
||||
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)
|
||||
* [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.
|
||||
|
||||
### 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:
|
||||
|
||||
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.
|
||||
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
|
||||
* 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`
|
||||
* 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.
|
||||
This means we will have 10 underglow LEDs and 38 per-key LEDs.
|
||||
@@ -73,7 +62,8 @@ Underglow LEDs always appear at the end of the `led_key_pos` array, because the
|
||||
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.
|
||||
@@ -105,8 +95,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:
|
||||
|
||||
```python
|
||||
@@ -121,26 +111,24 @@ Rgb_matrix extension requires one argument (`Rgb_matrix_data`), although additio
|
||||
Rgb_matrix:
|
||||
|
||||
* LED Display `ledDisplay`
|
||||
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
|
||||
* Rgb_matrix_data only takes two fields:
|
||||
* 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
|
||||
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
|
||||
* Rgb_matrix_data only takes two fields:
|
||||
* 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
|
||||
* 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`
|
||||
* 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`
|
||||
* 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`
|
||||
* 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 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.
|
||||
|
||||
#### Passing RGB Codes
|
||||
|
||||
### Passing RGB Codes
|
||||
```python
|
||||
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""" ],
|
||||
@@ -148,8 +136,7 @@ Rgb_matrix_data(
|
||||
)
|
||||
```
|
||||
|
||||
#### Using `Color` Class
|
||||
|
||||
### Using `Color` Class
|
||||
```python
|
||||
Rgb_matrix_data(
|
||||
keys=[Color.RED, Color.GREEN, Color.BLUE, Color.WHITE, Color.YELLOW, Color.ORANGE,"""... rest of colors""" ],
|
||||
@@ -176,8 +163,7 @@ rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_data(
|
||||
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.
|
||||
|
||||
```python
|
||||
@@ -187,7 +173,6 @@ 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".
|
||||
|
||||
Generate LED Map Arguments:
|
||||
|
||||
* Number of Keys
|
||||
* Number of Underglow
|
||||
* Key Color
|
||||
@@ -199,5 +184,6 @@ 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]],
|
||||
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)
|
||||
|
||||
|
||||
|
103
docs/quickpin.md
103
docs/quickpin.md
@@ -1,103 +0,0 @@
|
||||
# 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
|
||||
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).
|
||||
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:
|
||||
|
||||
@@ -20,13 +20,13 @@ Changing the **Saturation** moves between the inner and outer sections of the wh
|
||||
Changing the **Value** sets the overall brightness.
|
||||
|
||||
## Enabling the extension
|
||||
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.
|
||||
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.
|
||||
```python
|
||||
import board
|
||||
from kmk.extensions.RGB import RGB
|
||||
from kb import rgb_pixel_pin # This can be imported or defined manually
|
||||
|
||||
rgb = RGB(pixel_pin=board.GP14, num_pixels=27)
|
||||
keyboard.extensions.append(rgb)
|
||||
rgb_ext = RGB(pixel_pin=rgb_pixel_pin, num_pixels=27)
|
||||
keyboard.extensions.append(rgb_ext)
|
||||
```
|
||||
|
||||
## [Keycodes]
|
||||
@@ -52,21 +52,22 @@ keyboard.extensions.append(rgb)
|
||||
## Configuration
|
||||
|Define |Default |Description |
|
||||
|-------------------------------------|-------------|-----------------------------------------------------------------------------|
|
||||
|`rgb.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) |
|
||||
|`rgb.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 |
|
||||
|`rgb.val_step` |`17` |The number of steps to change the brightness by |
|
||||
|`rgb.hue_default` |`0` |The default hue when the keyboard boots |
|
||||
|`rgb.sat_default` |`255` |The default saturation when the keyboard boots |
|
||||
|`rgb.val_default` |`255` |The default value (brightness) when the keyboard boots |
|
||||
|`rgb.val_limit` |`255` |The maximum brightness level |
|
||||
|`keyboard.pixel_pin` | |The pin connected to the data pin of the LEDs|
|
||||
|`keyboard.num_pixels`| |The number of LEDs connected |
|
||||
|`keyboard.rgb_config['rgb_order']` |`(1, 0, 2)` |The order of the pixels R G B, and optionally white. Example(1, 0, 2, 3) |
|
||||
|`keyboard.rgb_config['hue_step']` |`10` |The number of steps to cycle through the hue by |
|
||||
|`keyboard.rgb_config['sat_step']` |`17` |The number of steps to change the saturation by |
|
||||
|`keyboard.rgb_config['val_step']` |`17` |The number of steps to change the brightness by |
|
||||
|`keyboard.rgb_config['hue_default']` |`0` |The default hue when the keyboard boots |
|
||||
|`keyboard.rgb_config['sat_default']` |`100` |The default saturation when the keyboard boots |
|
||||
|`keyboard.rgb_config['val_default']` |`100` |The default value (brightness) when the keyboard boots |
|
||||
|`keyboard.rgb_config['val_limit']` |`255` |The maximum brightness level |
|
||||
|
||||
## Built-in Animation Configuration
|
||||
|Define |Default |Description |
|
||||
|----------------------------------------------|-------------|-------------------------------------------------------------------------------------|
|
||||
|`rgb.breathe_center` |`1.5` |Used to calculate the curve for the breathing animation. Anywhere from 1.0 - 2.7 is valid|
|
||||
|`rgb.knight_effect_length` |`4` |The number of LEDs to light up for the "Knight" animation |
|
||||
|`keyboard.rgb_config['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 |
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -74,36 +75,38 @@ If you want to create your own animations, or for example, change the lighting i
|
||||
|
||||
|Function |Description |
|
||||
|--------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|`rgb.set_hsv_fill(hue, sat, val)` |Fills all LED's with HSV values |
|
||||
|`rgb.set_hsv(hue, sat, val, index)` |Sets a single LED with HSV value |
|
||||
|`rgb.set_rgb_fill((r, g, b))` |Fills all LED's with RGB(W) values |
|
||||
|`rgb.set_rgb((r, g, b), index)` |Set's a single LED with RGB(W) values |
|
||||
|`rgb.disable_auto_write(bool)` |When True, disables showing changes. Good for setting multiple LED's before a visible update|
|
||||
|`rgb.increase_hue(step)` |Increases hue by a given step |
|
||||
|`rgb.decrease_hue(step)` |Decreases hue by a given step |
|
||||
|`rgb.increase_sat(step)` |Increases saturation by a given step |
|
||||
|`rgb.decrease_sat(step)` |Decreases saturation by a given step |
|
||||
|`rgb.increase_val(step)` |Increases value (brightness) by a given step |
|
||||
|`rgb.decrease_val(step)` |Decreases value (brightness) by a given step |
|
||||
|`rgb.increase_ani()` |Increases animation speed by 1. Maximum 10 |
|
||||
|`rgb.decrease_ani()` |Decreases animation speed by 1. Minimum 10 |
|
||||
|`rgb.off()` |Turns all LED's off |
|
||||
|`rgb.show()` |Displays all stored configuration for LED's. Useful with disable_auto_write explained above |
|
||||
|`keyboard.pixels.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 |
|
||||
|`keyboard.pixels.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 |
|
||||
|`keyboard.pixels.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 |
|
||||
|`keyboard.pixels.decrease_hue(step)` |Decreases hue by a given step |
|
||||
|`keyboard.pixels.increase_sat(step)` |Increases saturation by a given step |
|
||||
|`keyboard.pixels.decrease_sat(step)` |Decreases saturation by a given step |
|
||||
|`keyboard.pixels.increase_val(step)` |Increases value (brightness) by a given step |
|
||||
|`keyboard.pixels.decrease_val(step)` |Decreases value (brightness) by a given step |
|
||||
|`keyboard.pixels.increase_ani()` |Increases animation speed by 1. Maximum 10 |
|
||||
|`keyboard.pixels.decrease_ani()` |Decreases animation speed by 1. Minimum 10 |
|
||||
|`keyboard.pixels.off()` |Turns all LED's off |
|
||||
|`keyboard.pixels.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
|
||||
|Define |Default |Description |
|
||||
|-----------------------------------|-----------|-----------------------------------------------------------------------------------------------------------|
|
||||
|`rgb.hue` |`0` |Sets the hue from 0-255 |
|
||||
|`rgb.sat` |`255` |Sets the saturation from 0-255 |
|
||||
|`rgb.val` |`255` |Sets the brightness from 0-255 |
|
||||
|`rgb.reverse_animation`|`False` |If true, some animations will run in reverse. Can be safely used in user animations |
|
||||
|`rgb.animation_mode` |`static` |This can be changed to any modes included, or to something custom for user animations. Any string is valid |
|
||||
|`rgb.animation_speed` |`1` |Increases animation speed of most animations. Recommended 1-5, Maximum 10. |
|
||||
|`keyboard.pixels.hue` |`0` |Sets the hue from 0-360 |
|
||||
|`keyboard.pixels.sat` |`100` |Sets the saturation from 0-100 |
|
||||
|`keyboard.pixels.val` |`80` |Sets the brightness from 1-255 |
|
||||
|`keyboard.pixels.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 |
|
||||
|`keyboard.pixels.animation_speed` |`1` |Increases animation speed of most animations. Recommended 1-5, Maximum 10. |
|
||||
|
||||
```python
|
||||
from kmk.extensions.rgb import AnimationModes
|
||||
rgb = RGB(pixel_pin=rgb_pixel_pin,
|
||||
rgb_ext = RGB(pixel_pin=rgb_pixel_pin,
|
||||
num_pixels=27
|
||||
num_pixels=0,
|
||||
val_limit=100,
|
||||
hue_default=0,
|
||||
sat_default=100,
|
||||
@@ -166,8 +169,8 @@ from kb import rgb_pixel_pin # This can be imported or defined manually
|
||||
_LED_COUNT=12
|
||||
pixels = adafruit_dotstar.DotStar(board.SCK, board.MOSI, _LED_COUNT)
|
||||
|
||||
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb)
|
||||
rgb_ext = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb_ext)
|
||||
```
|
||||
|
||||
### Multiple PixelBuffer
|
||||
@@ -182,6 +185,6 @@ pixels = (
|
||||
CustomPixelBuf(...)
|
||||
)
|
||||
|
||||
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb)
|
||||
rgb_ext = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb_ext)
|
||||
```
|
||||
|
@@ -29,23 +29,23 @@ use it to add things like copying/pasting, tabbing between fields, etc.
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
PASTE_WITH_COMMENTARY = simple_key_sequence(
|
||||
(
|
||||
KC.L,
|
||||
KC.O,
|
||||
KC.O,
|
||||
KC.K,
|
||||
KC.SPC,
|
||||
KC.A,
|
||||
KC.T,
|
||||
KC.SPC,
|
||||
KC.T,
|
||||
KC.H,
|
||||
KC.I,
|
||||
KC.S,
|
||||
KC.COLN,
|
||||
KC.SPC,
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
(
|
||||
KC.L,
|
||||
KC.O,
|
||||
KC.O,
|
||||
KC.K,
|
||||
KC.SPC,
|
||||
KC.A,
|
||||
KC.T,
|
||||
KC.SPC,
|
||||
KC.T,
|
||||
KC.H,
|
||||
KC.I,
|
||||
KC.S,
|
||||
KC.COLN,
|
||||
KC.SPC,
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
)
|
||||
|
||||
keyboard.keymap = [<other keycodes>, PASTE_WITH_COMMENTARY, <other keycodes>]
|
||||
@@ -64,35 +64,23 @@ length of time, in milliseconds.
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
COUNTDOWN_TO_PASTE = simple_key_sequence(
|
||||
(
|
||||
KC.N3,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N2,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N1,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP(1000),
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
(
|
||||
KC.N3,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N2,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N1,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP(1000),
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
)
|
||||
|
||||
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:
|
||||
|
||||
3
|
||||
@@ -101,24 +89,6 @@ This example will type out the following, waiting one second (1000 ms) between n
|
||||
|
||||
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
|
||||
Before trying to send Unicode sequences, make sure you set your `UnicodeMode`.
|
||||
@@ -158,8 +128,8 @@ keys), that's supported too, through an obnoxiously long-winded method:
|
||||
from kmk.handlers.sequences import compile_unicode_string_sequences as cuss
|
||||
|
||||
emoticons = cuss({
|
||||
'BEER': r'🍺',
|
||||
'HAND_WAVE': r'👋',
|
||||
'BEER': r'🍺',
|
||||
'HAND_WAVE': r'👋',
|
||||
})
|
||||
|
||||
keymap = [<other keycodes>, emoticons.BEER, emoticons.HAND_WAVE, <other keycodes>]
|
||||
@@ -177,8 +147,8 @@ supported too, through `unicode_codepoint_sequence`.
|
||||
from kmk.handlers.sequences import unicode_codepoint_sequence
|
||||
|
||||
TABLE_FLIP = unicode_codepoint_sequence([
|
||||
"28", "30ce", "ca0", "75ca","ca0", "29",
|
||||
"30ce", "5f61", "253b", "2501", "253b",
|
||||
"28", "30ce", "ca0", "75ca","ca0", "29",
|
||||
"30ce", "5f61", "253b", "2501", "253b",
|
||||
])
|
||||
|
||||
keyboard.keymap = [<other keycodes>, TABLE_FLIP, <other keycodes>]
|
||||
|
BIN
kmk/.DS_Store
vendored
Normal file
BIN
kmk/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -61,6 +61,7 @@ class Oled(Extension):
|
||||
|
||||
def renderOledTextLayer(self, layer):
|
||||
splash = displayio.Group()
|
||||
self._display.show(splash)
|
||||
splash.append(
|
||||
label.Label(
|
||||
terminalio.FONT,
|
||||
@@ -97,17 +98,16 @@ class Oled(Extension):
|
||||
y=25,
|
||||
)
|
||||
)
|
||||
self._display.show(splash)
|
||||
gc.collect()
|
||||
|
||||
def renderOledImgLayer(self, layer):
|
||||
splash = displayio.Group()
|
||||
self._display.show(splash)
|
||||
odb = displayio.OnDiskBitmap(
|
||||
'/' + self.returnCurrectRenderText(layer, self._views[0])
|
||||
)
|
||||
image = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
|
||||
splash.append(image)
|
||||
self._display.show(splash)
|
||||
gc.collect()
|
||||
|
||||
def updateOLED(self, sandbox):
|
||||
|
@@ -9,15 +9,11 @@ from kmk.keys import make_key
|
||||
|
||||
class Color:
|
||||
OFF = [0, 0, 0]
|
||||
BLACK = OFF
|
||||
WHITE = [249, 249, 249]
|
||||
RED = [255, 0, 0]
|
||||
AZURE = [153, 245, 255]
|
||||
BLUE = [0, 0, 255]
|
||||
CYAN = [0, 255, 255]
|
||||
GREEN = [0, 255, 0]
|
||||
YELLOW = [255, 247, 0]
|
||||
MAGENTA = [255, 0, 255]
|
||||
ORANGE = [255, 77, 0]
|
||||
PURPLE = [255, 0, 242]
|
||||
TEAL = [0, 128, 128]
|
||||
@@ -57,9 +53,6 @@ class Rgb_matrix(Extension):
|
||||
self.disable_auto_write = disable_auto_write
|
||||
self.split = split
|
||||
self.rightSide = rightSide
|
||||
self.brightness_step = 0.1
|
||||
self.brightness = 0
|
||||
|
||||
if name.endswith('L'):
|
||||
self.rightSide = False
|
||||
elif name.endswith('R'):
|
||||
@@ -72,12 +65,6 @@ class Rgb_matrix(Extension):
|
||||
make_key(
|
||||
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):
|
||||
if self.enable:
|
||||
@@ -86,12 +73,6 @@ class Rgb_matrix(Extension):
|
||||
self.on()
|
||||
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):
|
||||
if self.neopixel:
|
||||
self.setBasedOffDisplay()
|
||||
@@ -107,34 +88,6 @@ class Rgb_matrix(Extension):
|
||||
if self.disable_auto_write:
|
||||
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):
|
||||
if self.split:
|
||||
for i, val in enumerate(self.ledDisplay):
|
||||
@@ -168,7 +121,6 @@ class Rgb_matrix(Extension):
|
||||
)
|
||||
self.num_pixels = board.num_pixels
|
||||
self.keyPos = board.led_key_pos
|
||||
self.brightness = board.brightness_limit
|
||||
self.on()
|
||||
return
|
||||
|
||||
@@ -185,17 +137,7 @@ class Rgb_matrix(Extension):
|
||||
return
|
||||
|
||||
def on_powersave_enable(self, sandbox):
|
||||
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()
|
||||
return
|
||||
|
||||
def on_powersave_disable(self, sandbox):
|
||||
if self.neopixel:
|
||||
self.neopixel.brightness = self.brightness
|
||||
if self.disable_auto_write:
|
||||
self.neopixel.show()
|
||||
return
|
||||
|
@@ -5,9 +5,7 @@ from kmk.extensions import Extension
|
||||
from kmk.handlers.stock import passthrough as handler_passthrough
|
||||
from kmk.keys import make_key
|
||||
from kmk.kmktime import PeriodicTimer
|
||||
from kmk.utils import Debug, clamp
|
||||
|
||||
debug = Debug(__name__)
|
||||
from kmk.utils import clamp
|
||||
|
||||
rgb_config = {}
|
||||
|
||||
@@ -129,10 +127,6 @@ class RGB(Extension):
|
||||
for pixels in self.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.hue_step = hue_step
|
||||
@@ -255,9 +249,6 @@ class RGB(Extension):
|
||||
:param val:
|
||||
:param index: Index of LED/Pixel
|
||||
'''
|
||||
|
||||
val = clamp(val, 0, self.val_limit)
|
||||
|
||||
if self.rgbw:
|
||||
self.set_rgb(hsv_to_rgbw(hue, sat, val), index)
|
||||
else:
|
||||
@@ -270,9 +261,6 @@ class RGB(Extension):
|
||||
:param sat:
|
||||
:param val:
|
||||
'''
|
||||
|
||||
val = clamp(val, 0, self.val_limit)
|
||||
|
||||
if self.rgbw:
|
||||
self.set_rgb_fill(hsv_to_rgbw(hue, sat, val))
|
||||
else:
|
||||
|
165
kmk/keys.py
165
kmk/keys.py
@@ -1,8 +1,4 @@
|
||||
try:
|
||||
from typing import Callable, Optional, Tuple
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from gc import collect
|
||||
from micropython import const
|
||||
|
||||
import kmk.handlers.stock as handlers
|
||||
@@ -11,20 +7,13 @@ from kmk.key_validators import key_seq_sleep_validator, unicode_mode_key_validat
|
||||
from kmk.types import UnicodeModeKeyMeta
|
||||
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)
|
||||
NEXT_AVAILABLE_KEY = 1000
|
||||
|
||||
KEY_SIMPLE = const(0)
|
||||
KEY_MODIFIER = const(1)
|
||||
KEY_CONSUMER = const(2)
|
||||
|
||||
ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
ALL_NUMBERS = '1234567890'
|
||||
# since KC.1 isn't valid Python, alias to KC.N1
|
||||
@@ -33,12 +22,7 @@ ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS)
|
||||
debug = Debug(__name__)
|
||||
|
||||
|
||||
def maybe_make_key(
|
||||
code: Optional[int],
|
||||
names: Tuple[str, ...],
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> Callable[[str], Key]:
|
||||
def maybe_make_key(code, names, *args, **kwargs):
|
||||
def closure(candidate):
|
||||
if candidate in names:
|
||||
return make_key(code=code, names=names, *args, **kwargs)
|
||||
@@ -48,10 +32,10 @@ def maybe_make_key(
|
||||
|
||||
def maybe_make_argumented_key(
|
||||
validator=lambda *validator_args, **validator_kwargs: object(),
|
||||
names: Tuple[str, ...] = tuple(), # NOQA
|
||||
names=tuple(), # NOQA
|
||||
*constructor_args,
|
||||
**constructor_kwargs,
|
||||
) -> Callable[[str], Key]:
|
||||
):
|
||||
def closure(candidate):
|
||||
if candidate in names:
|
||||
return make_argumented_key(
|
||||
@@ -61,7 +45,7 @@ def maybe_make_argumented_key(
|
||||
return closure
|
||||
|
||||
|
||||
def maybe_make_no_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_no_key(candidate):
|
||||
# NO and TRNS are functionally identical in how they (don't) mutate
|
||||
# the state, but are tracked semantically separately, so create
|
||||
# two keys with the exact same functionality
|
||||
@@ -79,7 +63,7 @@ def maybe_make_no_key(candidate: str) -> Optional[Key]:
|
||||
)
|
||||
|
||||
|
||||
def maybe_make_alpha_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_alpha_key(candidate):
|
||||
if len(candidate) != 1:
|
||||
return
|
||||
|
||||
@@ -91,7 +75,7 @@ def maybe_make_alpha_key(candidate: str) -> Optional[Key]:
|
||||
)
|
||||
|
||||
|
||||
def maybe_make_numeric_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_numeric_key(candidate):
|
||||
if candidate in ALL_NUMBERS or candidate in ALL_NUMBER_ALIASES:
|
||||
try:
|
||||
offset = ALL_NUMBERS.index(candidate)
|
||||
@@ -104,7 +88,7 @@ def maybe_make_numeric_key(candidate: str) -> Optional[Key]:
|
||||
)
|
||||
|
||||
|
||||
def maybe_make_mod_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_mod_key(candidate):
|
||||
# MEH = LCTL | LALT | LSFT
|
||||
# HYPR = LCTL | LALT | LSFT | LGUI
|
||||
mods = (
|
||||
@@ -122,10 +106,10 @@ def maybe_make_mod_key(candidate: str) -> Optional[Key]:
|
||||
|
||||
for code, names in mods:
|
||||
if candidate in names:
|
||||
return make_key(code=code, names=names, type=KeyType.MODIFIER)
|
||||
return make_key(code=code, names=names, type=KEY_MODIFIER)
|
||||
|
||||
|
||||
def maybe_make_more_ascii(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_more_ascii(candidate):
|
||||
codes = (
|
||||
(40, ('ENTER', 'ENT', '\n')),
|
||||
(41, ('ESCAPE', 'ESC')),
|
||||
@@ -150,7 +134,7 @@ def maybe_make_more_ascii(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names)
|
||||
|
||||
|
||||
def maybe_make_fn_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_fn_key(candidate):
|
||||
codes = (
|
||||
(58, ('F1',)),
|
||||
(59, ('F2',)),
|
||||
@@ -183,7 +167,7 @@ def maybe_make_fn_key(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names)
|
||||
|
||||
|
||||
def maybe_make_navlock_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_navlock_key(candidate):
|
||||
codes = (
|
||||
(57, ('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS')),
|
||||
# FIXME: Investigate whether this key actually works, and
|
||||
@@ -212,7 +196,7 @@ def maybe_make_navlock_key(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names)
|
||||
|
||||
|
||||
def maybe_make_numpad_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_numpad_key(candidate):
|
||||
codes = (
|
||||
(83, ('NUM_LOCK', 'NUMLOCK', 'NLCK')),
|
||||
(84, ('KP_SLASH', 'NUMPAD_SLASH', 'PSLS')),
|
||||
@@ -241,7 +225,7 @@ def maybe_make_numpad_key(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names)
|
||||
|
||||
|
||||
def maybe_make_shifted_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_shifted_key(candidate):
|
||||
codes = (
|
||||
(30, ('EXCLAIM', 'EXLM', '!')),
|
||||
(31, ('AT', '@')),
|
||||
@@ -271,7 +255,7 @@ def maybe_make_shifted_key(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names, has_modifiers={KC.LSFT.code})
|
||||
|
||||
|
||||
def maybe_make_international_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_international_key(candidate):
|
||||
codes = (
|
||||
(50, ('NONUS_HASH', 'NUHS')),
|
||||
(100, ('NONUS_BSLASH', 'NUBS')),
|
||||
@@ -301,7 +285,7 @@ def maybe_make_international_key(candidate: str) -> Optional[Key]:
|
||||
return make_key(code=code, names=names)
|
||||
|
||||
|
||||
def maybe_make_unicode_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_unicode_key(candidate):
|
||||
keys = (
|
||||
(
|
||||
('UC_MODE_NOOP', 'UC_DISABLE'),
|
||||
@@ -337,7 +321,7 @@ def maybe_make_unicode_key(candidate: str) -> Optional[Key]:
|
||||
)
|
||||
|
||||
|
||||
def maybe_make_firmware_key(candidate: str) -> Optional[Key]:
|
||||
def maybe_make_firmware_key(candidate):
|
||||
keys = (
|
||||
((('BLE_REFRESH',), handlers.ble_refresh)),
|
||||
((('BOOTLOADER',), handlers.bootloader)),
|
||||
@@ -405,13 +389,13 @@ class KeyAttrDict:
|
||||
def __iter__(self):
|
||||
return self.__cache.__iter__()
|
||||
|
||||
def __setitem__(self, key: str, value: Key):
|
||||
def __setitem__(self, key, value):
|
||||
self.__cache.__setitem__(key, value)
|
||||
|
||||
def __getattr__(self, key: Key):
|
||||
def __getattr__(self, key):
|
||||
return self.__getitem__(key)
|
||||
|
||||
def get(self, key: Key, default: Optional[Key] = None):
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return self.__getitem__(key)
|
||||
except Exception:
|
||||
@@ -420,7 +404,7 @@ class KeyAttrDict:
|
||||
def clear(self):
|
||||
self.__cache.clear()
|
||||
|
||||
def __getitem__(self, key: Key):
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return self.__cache[key]
|
||||
except KeyError:
|
||||
@@ -447,17 +431,13 @@ KC = KeyAttrDict()
|
||||
class Key:
|
||||
def __init__(
|
||||
self,
|
||||
code: int,
|
||||
has_modifiers: Optional[list[Key, ...]] = None,
|
||||
no_press: bool = False,
|
||||
no_release: bool = False,
|
||||
on_press: Callable[
|
||||
[object, Key, Keyboard, ...], None
|
||||
] = handlers.default_pressed,
|
||||
on_release: Callable[
|
||||
[object, Key, Keyboard, ...], None
|
||||
] = handlers.default_released,
|
||||
meta: object = object(),
|
||||
code,
|
||||
has_modifiers=None,
|
||||
no_press=False,
|
||||
no_release=False,
|
||||
on_press=handlers.default_pressed,
|
||||
on_release=handlers.default_released,
|
||||
meta=object(),
|
||||
):
|
||||
self.code = code
|
||||
self.has_modifiers = has_modifiers
|
||||
@@ -469,9 +449,7 @@ class Key:
|
||||
self._handle_release = on_release
|
||||
self.meta = meta
|
||||
|
||||
def __call__(
|
||||
self, no_press: Optional[bool] = None, no_release: Optional[bool] = None
|
||||
) -> Key:
|
||||
def __call__(self, no_press=None, no_release=None):
|
||||
if no_press is None and no_release is None:
|
||||
return self
|
||||
|
||||
@@ -488,31 +466,35 @@ class Key:
|
||||
def __repr__(self):
|
||||
return f'Key(code={self.code}, has_modifiers={self.has_modifiers})'
|
||||
|
||||
def on_press(self, keyboard: Keyboard, coord_int: Optional[int] = None) -> None:
|
||||
def on_press(self, state, coord_int=None):
|
||||
if hasattr(self, '_pre_press_handlers'):
|
||||
for fn in self._pre_press_handlers:
|
||||
if not fn(self, keyboard, KC, coord_int):
|
||||
return
|
||||
if not fn(self, state, KC, coord_int):
|
||||
return None
|
||||
|
||||
self._handle_press(self, keyboard, KC, coord_int)
|
||||
ret = self._handle_press(self, state, KC, coord_int)
|
||||
|
||||
if hasattr(self, '_post_press_handlers'):
|
||||
for fn in self._post_press_handlers:
|
||||
fn(self, keyboard, KC, coord_int)
|
||||
fn(self, state, KC, coord_int)
|
||||
|
||||
def on_release(self, keyboard: Keyboard, coord_int: Optional[int] = None) -> None:
|
||||
return ret
|
||||
|
||||
def on_release(self, state, coord_int=None):
|
||||
if hasattr(self, '_pre_release_handlers'):
|
||||
for fn in self._pre_release_handlers:
|
||||
if not fn(self, keyboard, KC, coord_int):
|
||||
return
|
||||
if not fn(self, state, KC, coord_int):
|
||||
return None
|
||||
|
||||
self._handle_release(self, keyboard, KC, coord_int)
|
||||
ret = self._handle_release(self, state, KC, coord_int)
|
||||
|
||||
if hasattr(self, '_post_release_handlers'):
|
||||
for fn in self._post_release_handlers:
|
||||
fn(self, keyboard, KC, coord_int)
|
||||
fn(self, state, KC, coord_int)
|
||||
|
||||
def clone(self) -> Key:
|
||||
return ret
|
||||
|
||||
def clone(self):
|
||||
'''
|
||||
Return a shallow clone of the current key without any pre/post press/release
|
||||
handlers attached. Almost exclusively useful for creating non-colliding keys
|
||||
@@ -529,7 +511,7 @@ class Key:
|
||||
meta=self.meta,
|
||||
)
|
||||
|
||||
def before_press_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||
def before_press_handler(self, fn):
|
||||
'''
|
||||
Attach a callback to be run prior to the on_press handler for this key.
|
||||
Receives the following:
|
||||
@@ -552,8 +534,9 @@ class Key:
|
||||
if not hasattr(self, '_pre_press_handlers'):
|
||||
self._pre_press_handlers = []
|
||||
self._pre_press_handlers.append(fn)
|
||||
return self
|
||||
|
||||
def after_press_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||
def after_press_handler(self, fn):
|
||||
'''
|
||||
Attach a callback to be run after the on_release handler for this key.
|
||||
Receives the following:
|
||||
@@ -575,8 +558,9 @@ class Key:
|
||||
if not hasattr(self, '_post_press_handlers'):
|
||||
self._post_press_handlers = []
|
||||
self._post_press_handlers.append(fn)
|
||||
return self
|
||||
|
||||
def before_release_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||
def before_release_handler(self, fn):
|
||||
'''
|
||||
Attach a callback to be run prior to the on_release handler for this
|
||||
key. Receives the following:
|
||||
@@ -599,8 +583,9 @@ class Key:
|
||||
if not hasattr(self, '_pre_release_handlers'):
|
||||
self._pre_release_handlers = []
|
||||
self._pre_release_handlers.append(fn)
|
||||
return self
|
||||
|
||||
def after_release_handler(self, fn: Callable[[Key, Keyboard, ...], bool]) -> None:
|
||||
def after_release_handler(self, fn):
|
||||
'''
|
||||
Attach a callback to be run after the on_release handler for this key.
|
||||
Receives the following:
|
||||
@@ -622,17 +607,13 @@ class Key:
|
||||
if not hasattr(self, '_post_release_handlers'):
|
||||
self._post_release_handlers = []
|
||||
self._post_release_handlers.append(fn)
|
||||
return self
|
||||
|
||||
|
||||
class ModifierKey(Key):
|
||||
FAKE_CODE = const(-1)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
modified_key: Optional[Key] = None,
|
||||
no_press: Optional[bool] = None,
|
||||
no_release: Optional[bool] = None,
|
||||
) -> Key:
|
||||
def __call__(self, modified_key=None, no_press=None, no_release=None):
|
||||
if modified_key is None:
|
||||
return super().__call__(no_press=no_press, no_release=no_release)
|
||||
|
||||
@@ -669,12 +650,7 @@ class ConsumerKey(Key):
|
||||
pass
|
||||
|
||||
|
||||
def make_key(
|
||||
code: Optional[int] = None,
|
||||
names: Tuple[str, ...] = tuple(), # NOQA
|
||||
type: KeyType = KeyType.SIMPLE,
|
||||
**kwargs,
|
||||
) -> Key:
|
||||
def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
|
||||
'''
|
||||
Create a new key, aliased by `names` in the KC lookup table.
|
||||
|
||||
@@ -691,13 +667,14 @@ def make_key(
|
||||
All **kwargs are passed to the Key constructor
|
||||
'''
|
||||
|
||||
collect()
|
||||
global NEXT_AVAILABLE_KEY
|
||||
|
||||
if type == KeyType.SIMPLE:
|
||||
if type == KEY_SIMPLE:
|
||||
constructor = Key
|
||||
elif type == KeyType.MODIFIER:
|
||||
elif type == KEY_MODIFIER:
|
||||
constructor = ModifierKey
|
||||
elif type == KeyType.CONSUMER:
|
||||
elif type == KEY_CONSUMER:
|
||||
constructor = ConsumerKey
|
||||
else:
|
||||
raise ValueError('Unrecognized key type')
|
||||
@@ -719,29 +696,29 @@ def make_key(
|
||||
return key
|
||||
|
||||
|
||||
def make_mod_key(code: int, names: Tuple[str, ...], *args, **kwargs) -> Key:
|
||||
return make_key(code, names, *args, **kwargs, type=KeyType.MODIFIER)
|
||||
def make_mod_key(code, names, *args, **kwargs):
|
||||
return make_key(code, names, *args, **kwargs, type=KEY_MODIFIER)
|
||||
|
||||
|
||||
def make_shifted_key(code: int, names: Tuple[str, ...]) -> Key:
|
||||
def make_shifted_key(code, names):
|
||||
return make_key(code, names, has_modifiers={KC.LSFT.code})
|
||||
|
||||
|
||||
def make_consumer_key(*args, **kwargs) -> Key:
|
||||
return make_key(*args, **kwargs, type=KeyType.CONSUMER)
|
||||
def make_consumer_key(*args, **kwargs):
|
||||
return make_key(*args, **kwargs, type=KEY_CONSUMER)
|
||||
|
||||
|
||||
# Argumented keys are implicitly internal, so auto-gen of code
|
||||
# is almost certainly the best plan here
|
||||
def make_argumented_key(
|
||||
validator: object = lambda *validator_args, **validator_kwargs: object(),
|
||||
names: Tuple[str, ...] = tuple(), # NOQA
|
||||
validator=lambda *validator_args, **validator_kwargs: object(),
|
||||
names=tuple(), # NOQA
|
||||
*constructor_args,
|
||||
**constructor_kwargs,
|
||||
) -> Key:
|
||||
):
|
||||
global NEXT_AVAILABLE_KEY
|
||||
|
||||
def _argumented_key(*user_args, **user_kwargs) -> Key:
|
||||
def _argumented_key(*user_args, **user_kwargs):
|
||||
global NEXT_AVAILABLE_KEY
|
||||
|
||||
meta = validator(*user_args, **user_kwargs)
|
||||
|
@@ -1,13 +1,11 @@
|
||||
try:
|
||||
from typing import Callable, Optional, Tuple
|
||||
from typing import Optional
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from gc import collect
|
||||
from supervisor import ticks_ms
|
||||
|
||||
from collections import namedtuple
|
||||
from keypad import Event as KeyEvent
|
||||
|
||||
from kmk.consts import UnicodeMode
|
||||
from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes
|
||||
from kmk.keys import KC, Key
|
||||
@@ -18,10 +16,6 @@ from kmk.utils import Debug
|
||||
|
||||
debug = Debug(__name__)
|
||||
|
||||
KeyBufferFrame = namedtuple(
|
||||
'KeyBufferFrame', ('key', 'is_pressed', 'int_coord', 'index')
|
||||
)
|
||||
|
||||
|
||||
class Sandbox:
|
||||
matrix_update = None
|
||||
@@ -64,8 +58,6 @@ class KMKKeyboard:
|
||||
i2c_deinit_count = 0
|
||||
_go_args = None
|
||||
_processing_timeouts = False
|
||||
_resume_buffer = []
|
||||
_resume_buffer_x = []
|
||||
|
||||
# this should almost always be PREpended to, replaces
|
||||
# former use of reversed_active_layers which had pointless
|
||||
@@ -78,7 +70,7 @@ class KMKKeyboard:
|
||||
# 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
|
||||
# tight on RAM so....
|
||||
def __repr__(self) -> str:
|
||||
def __repr__(self):
|
||||
return ''.join(
|
||||
[
|
||||
'KMKKeyboard(\n',
|
||||
@@ -96,12 +88,12 @@ class KMKKeyboard:
|
||||
]
|
||||
)
|
||||
|
||||
def _print_debug_cycle(self, init: bool = False) -> None:
|
||||
def _print_debug_cycle(self, init=False):
|
||||
if debug.enabled:
|
||||
debug(f'coordkeys_pressed={self._coordkeys_pressed}')
|
||||
debug(f'keys_pressed={self.keys_pressed}')
|
||||
|
||||
def _send_hid(self) -> None:
|
||||
def _send_hid(self):
|
||||
if self._hid_send_enabled:
|
||||
hid_report = self._hid_helper.create_report(self.keys_pressed)
|
||||
try:
|
||||
@@ -111,12 +103,12 @@ class KMKKeyboard:
|
||||
debug(f'HidNotFound(HIDReportType={e})')
|
||||
self.hid_pending = False
|
||||
|
||||
def _handle_matrix_report(self, kevent: KeyEvent) -> None:
|
||||
if kevent is not None:
|
||||
self._on_matrix_changed(kevent)
|
||||
def _handle_matrix_report(self, update=None):
|
||||
if update is not None:
|
||||
self._on_matrix_changed(update)
|
||||
self.state_changed = True
|
||||
|
||||
def _find_key_in_map(self, int_coord: int) -> Key:
|
||||
def _find_key_in_map(self, int_coord):
|
||||
try:
|
||||
idx = self.coord_mapping.index(int_coord)
|
||||
except ValueError:
|
||||
@@ -138,7 +130,7 @@ class KMKKeyboard:
|
||||
|
||||
return layer_key
|
||||
|
||||
def _on_matrix_changed(self, kevent: KeyEvent) -> None:
|
||||
def _on_matrix_changed(self, kevent):
|
||||
int_coord = kevent.key_number
|
||||
is_pressed = kevent.pressed
|
||||
if debug.enabled:
|
||||
@@ -165,62 +157,15 @@ class KMKKeyboard:
|
||||
|
||||
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
|
||||
def debug_enabled(self) -> bool:
|
||||
def debug_enabled(self):
|
||||
return debug.enabled
|
||||
|
||||
@debug_enabled.setter
|
||||
def debug_enabled(self, enabled: bool):
|
||||
def debug_enabled(self, enabled):
|
||||
debug.enabled = enabled
|
||||
|
||||
def pre_process_key(
|
||||
self,
|
||||
key: Key,
|
||||
is_pressed: bool,
|
||||
int_coord: Optional[int] = None,
|
||||
index: int = 0,
|
||||
) -> None:
|
||||
def pre_process_key(self, key, is_pressed, int_coord=None, index=0):
|
||||
for module in self.modules[index:]:
|
||||
try:
|
||||
key = module.process_key(self, key, is_pressed, int_coord)
|
||||
@@ -243,14 +188,16 @@ class KMKKeyboard:
|
||||
if key:
|
||||
self.process_key(key, is_pressed, int_coord)
|
||||
|
||||
def process_key(
|
||||
self, key: Key, is_pressed: bool, coord_int: Optional[int] = None
|
||||
) -> None:
|
||||
return self
|
||||
|
||||
def process_key(self, key, is_pressed, coord_int=None):
|
||||
if is_pressed:
|
||||
key.on_press(self, coord_int)
|
||||
else:
|
||||
key.on_release(self, coord_int)
|
||||
|
||||
return self
|
||||
|
||||
def resume_process_key(
|
||||
self,
|
||||
module: Module,
|
||||
@@ -259,27 +206,24 @@ class KMKKeyboard:
|
||||
int_coord: Optional[int] = None,
|
||||
) -> None:
|
||||
index = self.modules.index(module) + 1
|
||||
ksf = KeyBufferFrame(
|
||||
key=key, is_pressed=is_pressed, int_coord=int_coord, index=index
|
||||
)
|
||||
self._resume_buffer.append(ksf)
|
||||
self.pre_process_key(key, is_pressed, int_coord, index)
|
||||
|
||||
def remove_key(self, keycode: Key) -> None:
|
||||
def remove_key(self, keycode):
|
||||
self.keys_pressed.discard(keycode)
|
||||
self.process_key(keycode, False)
|
||||
return self.process_key(keycode, False)
|
||||
|
||||
def add_key(self, keycode: Key) -> None:
|
||||
def add_key(self, keycode):
|
||||
self.keys_pressed.add(keycode)
|
||||
self.process_key(keycode, True)
|
||||
return self.process_key(keycode, True)
|
||||
|
||||
def tap_key(self, keycode: Key) -> None:
|
||||
def tap_key(self, keycode):
|
||||
self.add_key(keycode)
|
||||
# On the next cycle, we'll remove the key.
|
||||
self.set_timeout(False, lambda: self.remove_key(keycode))
|
||||
|
||||
def set_timeout(
|
||||
self, after_ticks: int, callback: Callable[[None], None]
|
||||
) -> Tuple[int, int]:
|
||||
return self
|
||||
|
||||
def set_timeout(self, after_ticks, callback):
|
||||
# We allow passing False as an implicit "run this on the next process timeouts cycle"
|
||||
if after_ticks is False:
|
||||
after_ticks = 0
|
||||
@@ -297,16 +241,16 @@ class KMKKeyboard:
|
||||
|
||||
return (timeout_key, idx)
|
||||
|
||||
def cancel_timeout(self, timeout_key: int) -> None:
|
||||
def cancel_timeout(self, timeout_key):
|
||||
try:
|
||||
self._timeouts[timeout_key[0]][timeout_key[1]] = None
|
||||
except (KeyError, IndexError):
|
||||
if debug.enabled:
|
||||
debug(f'no such timeout: {timeout_key}')
|
||||
|
||||
def _process_timeouts(self) -> None:
|
||||
def _process_timeouts(self):
|
||||
if not self._timeouts:
|
||||
return
|
||||
return self
|
||||
|
||||
# Copy timeout keys to a temporary list to allow sorting.
|
||||
# Prevent net timeouts set during handling from running on the current
|
||||
@@ -330,7 +274,9 @@ class KMKKeyboard:
|
||||
|
||||
self._processing_timeouts = False
|
||||
|
||||
def _init_sanity_check(self) -> None:
|
||||
return self
|
||||
|
||||
def _init_sanity_check(self):
|
||||
'''
|
||||
Ensure the provided configuration is *probably* bootable
|
||||
'''
|
||||
@@ -345,7 +291,9 @@ class KMKKeyboard:
|
||||
self.diode_orientation is not None
|
||||
), 'diode orientation must be defined'
|
||||
|
||||
def _init_coord_mapping(self) -> None:
|
||||
return self
|
||||
|
||||
def _init_coord_mapping(self):
|
||||
'''
|
||||
Attempt to sanely guess a coord_mapping if one is not provided. No-op
|
||||
if `kmk.extensions.split.Split` is used, it provides equivalent
|
||||
@@ -363,7 +311,7 @@ class KMKKeyboard:
|
||||
cm.extend(m.coord_mapping)
|
||||
self.coord_mapping = tuple(cm)
|
||||
|
||||
def _init_hid(self) -> None:
|
||||
def _init_hid(self):
|
||||
if self.hid_type == HIDModes.NOOP:
|
||||
self._hid_helper = AbstractHID
|
||||
elif self.hid_type == HIDModes.USB:
|
||||
@@ -375,7 +323,7 @@ class KMKKeyboard:
|
||||
self._hid_helper = self._hid_helper(**self._go_args)
|
||||
self._hid_send_enabled = True
|
||||
|
||||
def _init_matrix(self) -> None:
|
||||
def _init_matrix(self):
|
||||
if self.matrix is None:
|
||||
if debug.enabled:
|
||||
debug('Initialising default matrix scanner.')
|
||||
@@ -394,7 +342,9 @@ class KMKKeyboard:
|
||||
except TypeError:
|
||||
self.matrix = (self.matrix,)
|
||||
|
||||
def before_matrix_scan(self) -> None:
|
||||
return self
|
||||
|
||||
def before_matrix_scan(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.before_matrix_scan(self)
|
||||
@@ -409,7 +359,7 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'Error in {ext}.before_matrix_scan: {err}')
|
||||
|
||||
def after_matrix_scan(self) -> None:
|
||||
def after_matrix_scan(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.after_matrix_scan(self)
|
||||
@@ -424,7 +374,7 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'Error in {ext}.after_matrix_scan: {err}')
|
||||
|
||||
def before_hid_send(self) -> None:
|
||||
def before_hid_send(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.before_hid_send(self)
|
||||
@@ -441,7 +391,7 @@ class KMKKeyboard:
|
||||
f'Error in {ext}.before_hid_send: {err}',
|
||||
)
|
||||
|
||||
def after_hid_send(self) -> None:
|
||||
def after_hid_send(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.after_hid_send(self)
|
||||
@@ -456,7 +406,7 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'Error in {ext}.after_hid_send: {err}')
|
||||
|
||||
def powersave_enable(self) -> None:
|
||||
def powersave_enable(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.on_powersave_enable(self)
|
||||
@@ -471,7 +421,7 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'Error in {ext}.powersave_enable: {err}')
|
||||
|
||||
def powersave_disable(self) -> None:
|
||||
def powersave_disable(self):
|
||||
for module in self.modules:
|
||||
try:
|
||||
module.on_powersave_disable(self)
|
||||
@@ -485,33 +435,35 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'Error in {ext}.powersave_disable: {err}')
|
||||
|
||||
def go(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs) -> None:
|
||||
def go(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs):
|
||||
self._init(hid_type=hid_type, secondary_hid_type=secondary_hid_type, **kwargs)
|
||||
while True:
|
||||
self._main_loop()
|
||||
|
||||
def _init(
|
||||
self,
|
||||
hid_type: HIDModes = HIDModes.USB,
|
||||
secondary_hid_type: Optional[HIDModes] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
def _init(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs):
|
||||
self._go_args = kwargs
|
||||
self.hid_type = hid_type
|
||||
self.secondary_hid_type = secondary_hid_type
|
||||
|
||||
# Collect is run to keep memory fragmentation down.
|
||||
collect()
|
||||
self._init_sanity_check()
|
||||
collect()
|
||||
self._init_hid()
|
||||
collect()
|
||||
self._init_matrix()
|
||||
collect()
|
||||
self._init_coord_mapping()
|
||||
|
||||
for module in self.modules:
|
||||
collect()
|
||||
try:
|
||||
module.during_bootup(self)
|
||||
except Exception as err:
|
||||
if debug.enabled:
|
||||
debug(f'Failed to load module {module}: {err}')
|
||||
for ext in self.extensions:
|
||||
collect()
|
||||
try:
|
||||
ext.during_bootup(self)
|
||||
except Exception as err:
|
||||
@@ -521,31 +473,28 @@ class KMKKeyboard:
|
||||
if debug.enabled:
|
||||
debug(f'init: {self}')
|
||||
|
||||
def _main_loop(self) -> None:
|
||||
def _main_loop(self):
|
||||
self.state_changed = False
|
||||
self.sandbox.active_layers = self.active_layers.copy()
|
||||
|
||||
self.before_matrix_scan()
|
||||
|
||||
self._process_resume_buffer()
|
||||
|
||||
for matrix in self.matrix:
|
||||
update = matrix.scan_for_changes()
|
||||
if update:
|
||||
self.matrix_update = update
|
||||
break
|
||||
self.sandbox.matrix_update = self.matrix_update
|
||||
self.sandbox.secondary_matrix_update = self.secondary_matrix_update
|
||||
|
||||
self.after_matrix_scan()
|
||||
|
||||
if self.secondary_matrix_update:
|
||||
self.matrix_update_queue.append(self.secondary_matrix_update)
|
||||
self.secondary_matrix_update = None
|
||||
del self.secondary_matrix_update
|
||||
|
||||
if self.matrix_update:
|
||||
self.matrix_update_queue.append(self.matrix_update)
|
||||
self.matrix_update = None
|
||||
del self.matrix_update
|
||||
|
||||
# only handle one key per cycle.
|
||||
if self.matrix_update_queue:
|
||||
@@ -572,3 +521,5 @@ class KMKKeyboard:
|
||||
|
||||
if self.state_changed:
|
||||
self._print_debug_cycle()
|
||||
|
||||
collect()
|
||||
|
@@ -6,26 +6,26 @@ _TICKS_MAX = const(_TICKS_PERIOD - 1)
|
||||
_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2)
|
||||
|
||||
|
||||
def ticks_diff(new: int, start: int) -> int:
|
||||
def ticks_diff(new, start):
|
||||
diff = (new - start) & _TICKS_MAX
|
||||
diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
|
||||
return diff
|
||||
|
||||
|
||||
def ticks_add(ticks: int, delta: int) -> int:
|
||||
def ticks_add(ticks, delta):
|
||||
return (ticks + delta) % _TICKS_PERIOD
|
||||
|
||||
|
||||
def check_deadline(new: int, start: int, ms: int) -> int:
|
||||
def check_deadline(new, start, ms):
|
||||
return ticks_diff(new, start) < ms
|
||||
|
||||
|
||||
class PeriodicTimer:
|
||||
def __init__(self, period: int):
|
||||
def __init__(self, period):
|
||||
self.period = period
|
||||
self.last_tick = ticks_ms()
|
||||
|
||||
def tick(self) -> bool:
|
||||
def tick(self):
|
||||
now = ticks_ms()
|
||||
if ticks_diff(now, self.last_tick) >= self.period:
|
||||
self.last_tick = now
|
||||
|
@@ -1,5 +1,5 @@
|
||||
try:
|
||||
from typing import Optional, Tuple, Union
|
||||
from typing import Optional, Tuple
|
||||
except ImportError:
|
||||
pass
|
||||
from micropython import const
|
||||
@@ -24,16 +24,14 @@ class Combo:
|
||||
_remaining = []
|
||||
_timeout = None
|
||||
_state = _ComboState.IDLE
|
||||
_match_coord = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
match: Tuple[Union[Key, int], ...],
|
||||
match: Tuple[Key, ...],
|
||||
result: Key,
|
||||
fast_reset=None,
|
||||
per_key_timeout=None,
|
||||
timeout=None,
|
||||
match_coord=None,
|
||||
):
|
||||
'''
|
||||
match: tuple of keys (KC.A, KC.B)
|
||||
@@ -47,36 +45,22 @@ class Combo:
|
||||
self.per_key_timeout = per_key_timeout
|
||||
if timeout is not None:
|
||||
self.timeout = timeout
|
||||
if match_coord is not None:
|
||||
self._match_coord = match_coord
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.__class__.__name__}({[k.code for k in self.match]})'
|
||||
|
||||
def matches(self, key: Key, int_coord: int):
|
||||
def matches(self, key):
|
||||
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):
|
||||
self._remaining = list(self.match)
|
||||
|
||||
|
||||
class Chord(Combo):
|
||||
def matches(self, key: Key, int_coord: int):
|
||||
if not self._match_coord and key in self._remaining:
|
||||
def matches(self, key):
|
||||
if key in self._remaining:
|
||||
self._remaining.remove(key)
|
||||
return True
|
||||
elif self._match_coord and int_coord in self._remaining:
|
||||
self._remaining.remove(int_coord)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -86,12 +70,8 @@ class Sequence(Combo):
|
||||
per_key_timeout = True
|
||||
timeout = 1000
|
||||
|
||||
def matches(self, key: Key, int_coord: int):
|
||||
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
|
||||
):
|
||||
def matches(self, key):
|
||||
if self._remaining and self._remaining[0] == key:
|
||||
self._remaining.pop(0)
|
||||
return True
|
||||
else:
|
||||
@@ -147,7 +127,7 @@ class Combos(Module):
|
||||
for combo in self.combos:
|
||||
if combo._state != _ComboState.MATCHING:
|
||||
continue
|
||||
if combo.matches(key, int_coord):
|
||||
if combo.matches(key):
|
||||
continue
|
||||
combo._state = _ComboState.IDLE
|
||||
if combo._timeout:
|
||||
@@ -191,11 +171,10 @@ class Combos(Module):
|
||||
)
|
||||
else:
|
||||
# There's no matching combo: send and reset key buffer
|
||||
if self._key_buffer:
|
||||
self._key_buffer.append((int_coord, key, True))
|
||||
self.send_key_buffer(keyboard)
|
||||
self._key_buffer = []
|
||||
key = None
|
||||
self.send_key_buffer(keyboard)
|
||||
self._key_buffer = []
|
||||
if int_coord is not None:
|
||||
key = keyboard._find_key_in_map(int_coord)
|
||||
|
||||
return key
|
||||
|
||||
@@ -203,7 +182,7 @@ class Combos(Module):
|
||||
for combo in self.combos:
|
||||
if combo._state != _ComboState.ACTIVE:
|
||||
continue
|
||||
if combo.has_match(key, int_coord):
|
||||
if key in combo.match:
|
||||
# Deactivate combo if it matches current key.
|
||||
self.deactivate(keyboard, combo)
|
||||
|
||||
@@ -211,7 +190,7 @@ class Combos(Module):
|
||||
self.reset_combo(keyboard, combo)
|
||||
self._key_buffer = []
|
||||
else:
|
||||
combo.insert(key, int_coord)
|
||||
combo._remaining.insert(0, key)
|
||||
combo._state = _ComboState.MATCHING
|
||||
|
||||
key = combo.result
|
||||
@@ -224,7 +203,7 @@ class Combos(Module):
|
||||
for combo in self.combos:
|
||||
if combo._state != _ComboState.MATCHING:
|
||||
continue
|
||||
if not combo.has_match(key, int_coord):
|
||||
if key not in combo.match:
|
||||
continue
|
||||
|
||||
# Combo matches, but first key released before timeout.
|
||||
@@ -237,7 +216,7 @@ class Combos(Module):
|
||||
if combo.fast_reset:
|
||||
self.reset_combo(keyboard, combo)
|
||||
else:
|
||||
combo.insert(key, int_coord)
|
||||
combo._remaining.insert(0, key)
|
||||
combo._state = _ComboState.MATCHING
|
||||
self.reset(keyboard)
|
||||
|
||||
@@ -252,14 +231,12 @@ class Combos(Module):
|
||||
elif len(combo._remaining) == len(combo.match) - 1:
|
||||
self.reset_combo(keyboard, combo)
|
||||
if not self.count_matching():
|
||||
self._key_buffer.append((int_coord, key, False))
|
||||
self.send_key_buffer(keyboard)
|
||||
self._key_buffer = []
|
||||
key = None
|
||||
|
||||
# Anything between first and last key released.
|
||||
else:
|
||||
combo.insert(key, int_coord)
|
||||
combo._remaining.insert(0, key)
|
||||
|
||||
# Don't propagate key-release events for keys that have been
|
||||
# buffered. Append release events only if corresponding press is in
|
||||
@@ -298,7 +275,17 @@ class Combos(Module):
|
||||
|
||||
def send_key_buffer(self, keyboard):
|
||||
for (int_coord, key, is_pressed) in self._key_buffer:
|
||||
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||
new_key = None
|
||||
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):
|
||||
combo.result.on_press(keyboard)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
from micropython import const
|
||||
|
||||
from kmk.keys import KC, make_argumented_key
|
||||
from kmk.keys import make_argumented_key
|
||||
from kmk.modules import Module
|
||||
from kmk.utils import Debug
|
||||
|
||||
@@ -12,7 +12,6 @@ class ActivationType:
|
||||
RELEASED = const(1)
|
||||
HOLD_TIMEOUT = const(2)
|
||||
INTERRUPTED = const(3)
|
||||
REPEAT = const(4)
|
||||
|
||||
|
||||
class HoldTapKeyState:
|
||||
@@ -31,14 +30,12 @@ class HoldTapKeyMeta:
|
||||
prefer_hold=True,
|
||||
tap_interrupted=False,
|
||||
tap_time=None,
|
||||
repeat=False,
|
||||
):
|
||||
self.tap = tap
|
||||
self.hold = hold
|
||||
self.prefer_hold = prefer_hold
|
||||
self.tap_interrupted = tap_interrupted
|
||||
self.tap_time = tap_time
|
||||
self.repeat = repeat
|
||||
|
||||
|
||||
class HoldTap(Module):
|
||||
@@ -47,13 +44,12 @@ class HoldTap(Module):
|
||||
def __init__(self):
|
||||
self.key_buffer = []
|
||||
self.key_states = {}
|
||||
if not KC.get('HT'):
|
||||
make_argumented_key(
|
||||
validator=HoldTapKeyMeta,
|
||||
names=('HT',),
|
||||
on_press=self.ht_pressed,
|
||||
on_release=self.ht_released,
|
||||
)
|
||||
make_argumented_key(
|
||||
validator=HoldTapKeyMeta,
|
||||
names=('HT',),
|
||||
on_press=self.ht_pressed,
|
||||
on_release=self.ht_released,
|
||||
)
|
||||
|
||||
def during_bootup(self, keyboard):
|
||||
return
|
||||
@@ -86,8 +82,12 @@ class HoldTap(Module):
|
||||
self.ht_activate_on_interrupt(
|
||||
key, keyboard, *state.args, **state.kwargs
|
||||
)
|
||||
keyboard._send_hid()
|
||||
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
|
||||
# is released.
|
||||
if (
|
||||
@@ -100,13 +100,10 @@ class HoldTap(Module):
|
||||
# apply changes with 'side-effects' on key_states or the loop behaviour
|
||||
# outside the loop.
|
||||
if append_buffer:
|
||||
self.key_buffer.append((int_coord, current_key, is_pressed))
|
||||
self.key_buffer.append((int_coord, current_key))
|
||||
current_key = None
|
||||
|
||||
elif send_buffer:
|
||||
self.send_key_buffer(keyboard)
|
||||
keyboard.resume_process_key(self, current_key, is_pressed, int_coord)
|
||||
current_key = None
|
||||
|
||||
return current_key
|
||||
|
||||
@@ -123,20 +120,7 @@ class HoldTap(Module):
|
||||
return
|
||||
|
||||
def ht_pressed(self, key, keyboard, *args, **kwargs):
|
||||
'''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
|
||||
|
||||
'''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.'''
|
||||
if key.meta.tap_time is None:
|
||||
tap_time = self.tap_time
|
||||
else:
|
||||
@@ -165,24 +149,10 @@ class HoldTap(Module):
|
||||
elif state.activated == ActivationType.PRESSED:
|
||||
# press and release tap because key released within tap time
|
||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||
self.send_key_buffer(keyboard)
|
||||
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
||||
state.activated = ActivationType.RELEASED
|
||||
self.send_key_buffer(keyboard)
|
||||
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)
|
||||
)
|
||||
del self.key_states[key]
|
||||
|
||||
return keyboard
|
||||
|
||||
@@ -206,12 +176,11 @@ class HoldTap(Module):
|
||||
del self.key_states[key]
|
||||
|
||||
def send_key_buffer(self, keyboard):
|
||||
if not self.key_buffer:
|
||||
return
|
||||
|
||||
for (int_coord, key, is_pressed) in self.key_buffer:
|
||||
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||
for (int_coord, key) in self.key_buffer:
|
||||
new_key = keyboard._find_key_in_map(int_coord)
|
||||
keyboard.resume_process_key(self, new_key, True, int_coord)
|
||||
|
||||
keyboard._send_hid()
|
||||
self.key_buffer.clear()
|
||||
|
||||
def ht_activate_hold(self, key, keyboard, *args, **kwargs):
|
||||
@@ -222,17 +191,24 @@ class HoldTap(Module):
|
||||
def ht_deactivate_hold(self, key, keyboard, *args, **kwargs):
|
||||
if debug.enabled:
|
||||
debug('ht_deactivate_hold')
|
||||
keyboard.resume_process_key(self, key.meta.hold, False)
|
||||
keyboard.set_timeout(
|
||||
False, lambda: keyboard.resume_process_key(self, key.meta.hold, False)
|
||||
)
|
||||
|
||||
def ht_activate_tap(self, key, keyboard, *args, **kwargs):
|
||||
if debug.enabled:
|
||||
debug('ht_activate_tap')
|
||||
keyboard.resume_process_key(self, key.meta.tap, True)
|
||||
|
||||
def ht_deactivate_tap(self, key, keyboard, *args, **kwargs):
|
||||
def ht_deactivate_tap(self, key, keyboard, *args, delayed=True, **kwargs):
|
||||
if debug.enabled:
|
||||
debug('ht_deactivate_tap')
|
||||
keyboard.resume_process_key(self, key.meta.tap, False)
|
||||
if delayed:
|
||||
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):
|
||||
if debug.enabled:
|
||||
@@ -248,4 +224,4 @@ class HoldTap(Module):
|
||||
if key.meta.prefer_hold:
|
||||
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
|
||||
else:
|
||||
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
|
||||
self.ht_deactivate_tap(key, keyboard, *args, delayed=False, **kwargs)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
'''One layer isn't enough. Adds keys to get to more of them'''
|
||||
from kmk.keys import KC, make_argumented_key
|
||||
from kmk.modules.holdtap import HoldTap, HoldTapKeyMeta
|
||||
from kmk.modules.holdtap import ActivationType, HoldTap, HoldTapKeyMeta
|
||||
from kmk.utils import Debug
|
||||
|
||||
debug = Debug(__name__)
|
||||
@@ -73,6 +73,20 @@ class Layers(HoldTap):
|
||||
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):
|
||||
'''
|
||||
Switches the default layer
|
||||
|
@@ -29,13 +29,7 @@ class OneShot(HoldTap):
|
||||
elif state.activated == ActivationType.RELEASED and is_pressed:
|
||||
state.activated = ActivationType.INTERRUPTED
|
||||
elif state.activated == ActivationType.INTERRUPTED:
|
||||
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)
|
||||
self.ht_released(key, keyboard)
|
||||
|
||||
return current_key
|
||||
|
||||
@@ -43,7 +37,6 @@ class OneShot(HoldTap):
|
||||
'''Register HoldTap mechanism and activate os key.'''
|
||||
self.ht_pressed(key, keyboard, *args, **kwargs)
|
||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||
self.send_key_buffer(keyboard)
|
||||
return keyboard
|
||||
|
||||
def osk_released(self, key, keyboard, *args, **kwargs):
|
||||
|
@@ -1,10 +1,13 @@
|
||||
'''Enables splitting keyboards wirelessly or wired'''
|
||||
import busio
|
||||
from micropython import const
|
||||
from supervisor import runtime
|
||||
from supervisor import runtime, ticks_ms
|
||||
|
||||
from keypad import Event as KeyEvent
|
||||
from storage import getmount
|
||||
|
||||
from kmk.hid import HIDModes
|
||||
from kmk.kmktime import check_deadline
|
||||
from kmk.modules import Module
|
||||
|
||||
|
||||
@@ -18,7 +21,6 @@ class SplitType:
|
||||
I2C = const(2) # unused
|
||||
ONEWIRE = const(3) # unused
|
||||
BLE = const(4)
|
||||
PIO_UART = const(5)
|
||||
|
||||
|
||||
class Split(Module):
|
||||
@@ -48,79 +50,52 @@ class Split(Module):
|
||||
self.data_pin2 = data_pin2
|
||||
self.uart_flip = uart_flip
|
||||
self._use_pio = use_pio
|
||||
self._transport = None
|
||||
self._uart = None
|
||||
self._uart_interval = uart_interval
|
||||
self._debug_enabled = debug_enabled
|
||||
self.uart_header = bytearray([0xB2]) # Any non-zero byte should work
|
||||
|
||||
if split_type == SplitType.UART and use_pio:
|
||||
split_type = SplitType.PIO_UART
|
||||
if self.split_type == SplitType.BLE:
|
||||
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._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):
|
||||
# Set up name for target side detection and BLE advertisment
|
||||
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 self.split_type == SplitType.BLE:
|
||||
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
|
||||
|
||||
# if split side was given, find target from split_side.
|
||||
if self.split_side == SplitSide.LEFT:
|
||||
self._is_target = bool(self.split_target_left)
|
||||
@@ -133,69 +108,277 @@ class Split(Module):
|
||||
or self.split_type == SplitType.ONEWIRE
|
||||
):
|
||||
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'):
|
||||
self.split_side = SplitSide.LEFT
|
||||
elif name.endswith('R'):
|
||||
self.split_side = SplitSide.RIGHT
|
||||
|
||||
def _init_transport(self, keyboard):
|
||||
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 not self._is_target:
|
||||
keyboard._hid_send_enabled = False
|
||||
|
||||
if self._is_target or not self.uart_flip:
|
||||
tx_pin = self.data_pin2
|
||||
rx_pin = self.data_pin
|
||||
else:
|
||||
tx_pin = self.data_pin
|
||||
rx_pin = self.data_pin2
|
||||
if self.split_offset is None:
|
||||
self.split_offset = keyboard.matrix[-1].coord_mapping[-1] + 1
|
||||
|
||||
if self.split_type == SplitType.UART:
|
||||
self._transport = UART(
|
||||
tx=tx_pin,
|
||||
rx=rx_pin,
|
||||
target=self.is_target,
|
||||
uart_interval=self._uart_interval,
|
||||
)
|
||||
elif self.split_type == SplitType.PIO_UART:
|
||||
self._transport = PIO_UART(tx=tx_pin, rx=rx_pin)
|
||||
elif self.split_type == SplitType.BLE:
|
||||
self._transport = BLE_UART(
|
||||
target=self._is_target, uart_interval=self.uart_interval
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
if self.split_type == SplitType.UART and self.data_pin is not None:
|
||||
if self._is_target or not self.uart_flip:
|
||||
if self._use_pio:
|
||||
self._uart = self.PIO_UART(tx=self.data_pin2, rx=self.data_pin)
|
||||
else:
|
||||
self._uart = busio.UART(
|
||||
tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval
|
||||
)
|
||||
else:
|
||||
if self._use_pio:
|
||||
self._uart = self.PIO_UART(tx=self.data_pin, rx=self.data_pin2)
|
||||
else:
|
||||
self._uart = busio.UART(
|
||||
tx=self.data_pin, rx=self.data_pin2, timeout=self._uart_interval
|
||||
)
|
||||
|
||||
def _guess_coord_mapping(self, keyboard):
|
||||
cm = []
|
||||
# Attempt to sanely guess a coord_mapping if one is not provided.
|
||||
if not keyboard.coord_mapping:
|
||||
cm = []
|
||||
|
||||
rows_to_calc = len(keyboard.row_pins)
|
||||
cols_to_calc = len(keyboard.col_pins)
|
||||
rows_to_calc = len(keyboard.row_pins)
|
||||
cols_to_calc = len(keyboard.col_pins)
|
||||
|
||||
# Flips the col order if PCB is the same but flipped on right
|
||||
cols_rhs = list(range(cols_to_calc))
|
||||
if self.split_flip:
|
||||
cols_rhs = list(reversed(cols_rhs))
|
||||
# Flips the col order if PCB is the same but flipped on right
|
||||
cols_rhs = list(range(cols_to_calc))
|
||||
if self.split_flip:
|
||||
cols_rhs = list(reversed(cols_rhs))
|
||||
|
||||
for ridx in range(rows_to_calc):
|
||||
for cidx in range(cols_to_calc):
|
||||
cm.append(cols_to_calc * ridx + cidx)
|
||||
for cidx in cols_rhs:
|
||||
cm.append(cols_to_calc * (rows_to_calc + ridx) + cidx)
|
||||
for ridx in range(rows_to_calc):
|
||||
for cidx in range(cols_to_calc):
|
||||
cm.append(cols_to_calc * ridx + cidx)
|
||||
for cidx in cols_rhs:
|
||||
cm.append(cols_to_calc * (rows_to_calc + ridx) + cidx)
|
||||
|
||||
keyboard.coord_mapping = tuple(cm)
|
||||
keyboard.coord_mapping = tuple(cm)
|
||||
|
||||
def _can_receive(self, keyboard) -> bool:
|
||||
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.split_type == SplitType.BLE:
|
||||
self._transport.check_connection(keyboard)
|
||||
return True
|
||||
|
||||
self._check_all_connections(keyboard)
|
||||
self._receive_ble(keyboard)
|
||||
elif self.split_type == SplitType.UART:
|
||||
if self._is_target or self.data_pin2:
|
||||
return True
|
||||
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
|
||||
|
||||
# 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 _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,11 +48,8 @@ class TapDance(HoldTap):
|
||||
if state.activated == ActivationType.RELEASED:
|
||||
keyboard.cancel_timeout(state.timeout_key)
|
||||
self.ht_activate_tap(_key, keyboard)
|
||||
self.send_key_buffer(keyboard)
|
||||
self.ht_deactivate_tap(_key, keyboard)
|
||||
keyboard.resume_process_key(self, key, is_pressed, int_coord)
|
||||
key = None
|
||||
|
||||
keyboard._send_hid()
|
||||
self.ht_deactivate_tap(_key, keyboard, delayed=False)
|
||||
del self.key_states[_key]
|
||||
del self.td_counts[state.tap_dance]
|
||||
|
||||
@@ -117,6 +114,6 @@ class TapDance(HoldTap):
|
||||
state = self.key_states[key]
|
||||
if state.activated == ActivationType.RELEASED:
|
||||
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||
self.send_key_buffer(keyboard)
|
||||
keyboard._send_hid()
|
||||
del self.td_counts[state.tap_dance]
|
||||
super().on_tap_time_expired(key, keyboard, *args, **kwargs)
|
||||
|
@@ -1,20 +0,0 @@
|
||||
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,
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
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
|
||||
]
|
@@ -1,28 +0,0 @@
|
||||
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+
|
||||
]
|
@@ -1,28 +0,0 @@
|
||||
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
|
||||
]
|
@@ -1,181 +0,0 @@
|
||||
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)
|
@@ -1,51 +0,0 @@
|
||||
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
|
||||
|
||||
|
||||
def clamp(x: int, bottom: int = 0, top: int = 100) -> int:
|
||||
def clamp(x, bottom=0, top=100):
|
||||
return min(max(bottom, x), top)
|
||||
|
||||
|
||||
@@ -13,18 +13,18 @@ class Debug:
|
||||
debug = Debug(__name__)
|
||||
'''
|
||||
|
||||
def __init__(self, name: str = __name__):
|
||||
def __init__(self, name=__name__):
|
||||
self.name = name
|
||||
|
||||
def __call__(self, message: str) -> None:
|
||||
def __call__(self, message):
|
||||
print(f'{ticks_ms()} {self.name}: {message}')
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
def enabled(self):
|
||||
global _debug_enabled
|
||||
return _debug_enabled
|
||||
|
||||
@enabled.setter
|
||||
def enabled(self, enabled: bool):
|
||||
def enabled(self, enabled):
|
||||
global _debug_enabled
|
||||
_debug_enabled = enabled
|
||||
|
@@ -74,7 +74,6 @@ class KeyboardTest:
|
||||
is_pressed = e[1]
|
||||
self.pins[key_pos].value = is_pressed
|
||||
self.do_main_loop()
|
||||
self.keyboard._main_loop()
|
||||
|
||||
matching = True
|
||||
for i in range(max(len(hid_reports), len(assert_reports))):
|
||||
|
@@ -192,14 +192,7 @@ class TestHoldTap(unittest.TestCase):
|
||||
keyboard.test(
|
||||
'chained 4',
|
||||
[(1, True), (3, True), (0, True), (3, False), (1, False), (0, False)],
|
||||
[
|
||||
{KC.LCTL},
|
||||
{KC.LCTL, KC.N3},
|
||||
{KC.LCTL, KC.N0, KC.N3},
|
||||
{KC.LCTL, KC.N0},
|
||||
{KC.N0},
|
||||
{},
|
||||
],
|
||||
[{KC.LCTL}, {KC.LCTL, KC.N3, KC.N0}, {KC.LCTL, KC.N0}, {KC.N0}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
@@ -300,25 +293,25 @@ class TestHoldTap(unittest.TestCase):
|
||||
keyboard.test(
|
||||
'OS interrupt within tap time',
|
||||
[(4, True), (4, False), t_within, (3, True), (3, False)],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}],
|
||||
[{KC.E}, {KC.D, KC.E}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
'OS interrupt, multiple within tap time',
|
||||
[(4, True), (4, False), (3, True), (3, False), (2, True), (2, False)],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}, {KC.C}, {}],
|
||||
[{KC.E}, {KC.D, KC.E}, {}, {KC.C}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
'OS interrupt, multiple interleaved',
|
||||
[(4, True), (4, False), (3, True), (2, True), (2, False), (3, False)],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.D}, {}],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.C, KC.D}, {KC.D}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
'OS interrupt, multiple interleaved',
|
||||
[(4, True), (4, False), (3, True), (2, True), (3, False), (2, False)],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.C}, {}],
|
||||
[{KC.E}, {KC.D, KC.E}, {KC.C, KC.D}, {KC.C}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
|
@@ -71,7 +71,7 @@ class TestTapDance(unittest.TestCase):
|
||||
keyboard.test(
|
||||
'Tap x1 interrupted',
|
||||
[(0, True), (0, False), (4, True), (4, False)],
|
||||
[{KC.N0}, {}, {KC.N4}, {}],
|
||||
[{KC.N0}, {KC.N4}, {}],
|
||||
)
|
||||
|
||||
keyboard.test(
|
||||
|
Reference in New Issue
Block a user