1 Commits

Author SHA1 Message Date
Kyle Brown
9e8c9c3669 GC to reduce memory fragmentation 2022-09-15 19:21:39 -07:00
41 changed files with 695 additions and 2000 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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,
]

View File

@@ -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)

View File

@@ -1,15 +0,0 @@
# Zodiark
![Zodiark](https://camo.githubusercontent.com/b5283aea8fe39b0646a405fd358aa0c2c0f5896fc0fb80a92b5e761149759214/68747470733a2f2f692e696d6775722e636f6d2f34394f38616f776c2e6a7067)
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.

View File

@@ -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
]

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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())
```

View File

@@ -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) |

View File

@@ -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.

View File

@@ -1,47 +1,35 @@
# 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.
* Brightness Limit `brightness_limit`
@@ -49,7 +37,8 @@ The board's kb.py needs 3 fields:
* Number of LEDs `num_pixels`
* 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
@@ -135,12 +125,10 @@ Rgb_matrix:
* 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)

View File

@@ -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
![pro micro footprint pins](./img/pro_micro_pinout.png)
## 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
]
```

View File

@@ -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)
```

View File

@@ -81,18 +81,6 @@ COUNTDOWN_TO_PASTE = simple_key_sequence(
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`.

BIN
kmk/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -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):

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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
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)

View File

@@ -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,7 +44,6 @@ class HoldTap(Module):
def __init__(self):
self.key_buffer = []
self.key_states = {}
if not KC.get('HT'):
make_argumented_key(
validator=HoldTapKeyMeta,
names=('HT',),
@@ -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,16 +191,23 @@ 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')
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):
@@ -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)

View File

@@ -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

View File

@@ -29,12 +29,6 @@ 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)
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):

View File

@@ -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
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
self._get_side()
if not self._is_target:
keyboard._hid_send_enabled = False
if self.split_offset is None:
self.split_offset = keyboard.matrix[-1].coord_mapping[-1] + 1
self._init_transport(keyboard)
# Attempt to sanely guess a coord_mapping if one is not provided.
if not keyboard.coord_mapping:
self._guess_coord_mapping()
if self.split_side == SplitSide.RIGHT:
offset = self.split_offset
for matrix in keyboard.matrix:
matrix.offset = offset
offset += matrix.key_count
def before_matrix_scan(self, keyboard):
if self._can_receive(keyboard):
keyboard.secondary_matrix_update = self._transport.receive(keyboard)
def after_matrix_scan(self, keyboard):
if keyboard.matrix_update:
self._transport.write(keyboard, keyboard.matrix_update)
def before_hid_send(self, keyboard):
if not self._is_target:
keyboard.hid_pending = False
return
def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
self._transport.powersave(True)
def on_powersave_disable(self, keyboard):
self._transport.powersave(False)
def _serialize_update(self, update):
buffer = bytearray(2)
buffer[0] = update.key_number
buffer[1] = update.pressed
return buffer
def _deserialize_update(self, update):
kevent = KeyEvent(key_number=update[0], pressed=update[1])
return kevent
def _checksum(self, update):
checksum = bytes([sum(update) & 0xFF])
return checksum
def _get_side(self):
name = str(getmount('/').label)
# if split side was given, find target from split_side.
if self.split_side == SplitSide.LEFT:
self._is_target = bool(self.split_target_left)
@@ -133,44 +108,38 @@ 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.split_offset is None:
self.split_offset = keyboard.matrix[-1].coord_mapping[-1] + 1
if self.split_type == SplitType.UART and self.data_pin is not None:
if self._is_target or not self.uart_flip:
tx_pin = self.data_pin2
rx_pin = self.data_pin
if self._use_pio:
self._uart = self.PIO_UART(tx=self.data_pin2, rx=self.data_pin)
else:
tx_pin = self.data_pin
rx_pin = self.data_pin2
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
self._uart = busio.UART(
tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval
)
else:
raise NotImplementedError
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):
# 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)
@@ -189,13 +158,227 @@ class Split(Module):
keyboard.coord_mapping = tuple(cm)
def _can_receive(self, keyboard) -> bool:
if self.split_type == SplitType.BLE:
self._transport.check_connection(keyboard)
return True
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._check_all_connections(keyboard)
self._receive_ble(keyboard)
elif self.split_type == SplitType.UART:
if self._is_target or self.data_pin2:
self._receive_uart(keyboard)
elif self.split_type == SplitType.ONEWIRE:
pass # Protocol needs written
return
def after_matrix_scan(self, keyboard):
if keyboard.matrix_update:
if self.split_type == SplitType.UART:
if not self._is_target or self.data_pin2:
self._send_uart(keyboard.matrix_update)
else:
pass # explicit pass just for dev sanity...
elif self.split_type == SplitType.BLE:
self._send_ble(keyboard.matrix_update)
elif self.split_type == SplitType.ONEWIRE:
pass # Protocol needs written
else:
print('Unexpected case in after_matrix_scan')
return
def before_hid_send(self, keyboard):
if not self._is_target:
keyboard.hid_pending = False
return
def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
if self.split_type == SplitType.BLE:
if self._uart_connection and not self._psave_enable:
self._uart_connection.connection_interval = self._uart_interval
self._psave_enable = True
def on_powersave_disable(self, keyboard):
if self.split_type == SplitType.BLE:
if self._uart_connection and self._psave_enable:
self._uart_connection.connection_interval = 11.25
self._psave_enable = False
def _check_all_connections(self, keyboard):
'''Validates the correct number of BLE connections'''
self._previous_connection_count = self._connection_count
self._connection_count = len(self._ble.connections)
if self._is_target:
if self._advertising or not self._check_if_split_connected():
self._target_advertise()
elif self._connection_count < 2 and keyboard.hid_type == HIDModes.BLE:
keyboard._hid_helper.start_advertising()
elif not self._is_target and self._connection_count < 1:
self._initiator_scan()
def _check_if_split_connected(self):
# I'm looking for a way how to recognize which connection is on and which one off
# For now, I found that service name relation to having other CP device
if self._connection_count == 0:
return False
if self._connection_count == 2:
self._split_connected = True
return True
# 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)

View File

@@ -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)

View File

@@ -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,
}

View File

@@ -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
]

View File

@@ -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+
]

View File

@@ -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
]

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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))):

View File

@@ -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(

View File

@@ -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(