Merge pull request #21 from KMKfw/topic-internal-keycodes
Internal Keycodes and partial layer support (MO, DF, TO, TG)
This commit is contained in:
		@@ -41,25 +41,6 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - run: make BOARD=boards/noop.py build-pyboard
 | 
			
		||||
 | 
			
		||||
  build_feather_nrf52832:
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: 'python:3.7'
 | 
			
		||||
 | 
			
		||||
    environment:
 | 
			
		||||
        KMK_TEST: 1
 | 
			
		||||
        PIPENV_VENV_IN_PROJECT: 1
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - checkout
 | 
			
		||||
      - restore_cache:
 | 
			
		||||
          keys:
 | 
			
		||||
            - v1-kmk-venv-{{ checksum "Pipfile.lock" }}
 | 
			
		||||
 | 
			
		||||
      - run: pip install pipenv==2018.7.1
 | 
			
		||||
      - run: apt-get update && apt-get install -y gcc-arm-none-eabi gettext wget unzip
 | 
			
		||||
 | 
			
		||||
      - run: make BOARD=boards/noop.py build-feather-nrf52832
 | 
			
		||||
 | 
			
		||||
  build_teensy_31:
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: 'python:3.7'
 | 
			
		||||
@@ -97,14 +78,6 @@ workflows:
 | 
			
		||||
              only: /.*/
 | 
			
		||||
          requires:
 | 
			
		||||
            - lint
 | 
			
		||||
      - build_feather_nrf52832:
 | 
			
		||||
          filters:
 | 
			
		||||
            branches:
 | 
			
		||||
              only: /.*/
 | 
			
		||||
            tags:
 | 
			
		||||
              only: /.*/
 | 
			
		||||
          requires:
 | 
			
		||||
            - lint
 | 
			
		||||
      - build_teensy_31:
 | 
			
		||||
          filters:
 | 
			
		||||
            branches:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -106,6 +106,7 @@ venv.bak/
 | 
			
		||||
.ampy
 | 
			
		||||
.submodules
 | 
			
		||||
.circuitpy-deps
 | 
			
		||||
 | 
			
		||||
.idea/
 | 
			
		||||
.micropython-deps
 | 
			
		||||
 | 
			
		||||
# Pycharms cruft
 | 
			
		||||
.idea
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							@@ -17,6 +17,9 @@ devdeps: Pipfile.lock
 | 
			
		||||
lint: devdeps
 | 
			
		||||
	@pipenv run flake8
 | 
			
		||||
 | 
			
		||||
fix-isort: devdeps
 | 
			
		||||
	@find kmk/ boards/ entrypoints/ -name "*.py" | xargs pipenv run isort
 | 
			
		||||
 | 
			
		||||
.submodules: .gitmodules
 | 
			
		||||
	@echo "===> Pulling dependencies, this may take several minutes"
 | 
			
		||||
	@git submodule update --init --recursive
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							@@ -16,6 +16,7 @@ ipdb = "*"
 | 
			
		||||
isort = "*"
 | 
			
		||||
"flake8-isort" = "*"
 | 
			
		||||
neovim = "*"
 | 
			
		||||
"flake8-per-file-ignores" = "*"
 | 
			
		||||
 | 
			
		||||
[requires]
 | 
			
		||||
python_version = "3.7"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										70
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "_meta": {
 | 
			
		||||
        "hash": {
 | 
			
		||||
            "sha256": "44e8c37b94a71b7f47fc43f2c98bf17d546f4a5ef7ad1cad5076d4a47fc4515a"
 | 
			
		||||
            "sha256": "96625b372d35c7f5ed0fd3289ba61afd0bcc034ddad31feb958a107e2751fb0a"
 | 
			
		||||
        },
 | 
			
		||||
        "pipfile-spec": 6,
 | 
			
		||||
        "requires": {
 | 
			
		||||
@@ -86,29 +86,37 @@
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==2.5"
 | 
			
		||||
        },
 | 
			
		||||
        "flake8-per-file-ignores": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:3c4b1d770fa509aaad997ca147bd3533b730c3f6c48290b69a4265072c465522",
 | 
			
		||||
                "sha256:4ee4f24cbea5e18e1fefdfccb043e819caf483d16d08e39cb6df5d18b0407275"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.6"
 | 
			
		||||
        },
 | 
			
		||||
        "greenlet": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0411b5bf0de5ec11060925fd811ad49073fa19f995bcf408839eb619b59bb9f7",
 | 
			
		||||
                "sha256:131f4ed14f0fd28d2a9fa50f79a57d5ed1c8f742d3ccac3d773fee09ef6fe217",
 | 
			
		||||
                "sha256:13510d32f8db72a0b3e1720dbf8cba5c4eecdf07abc4cb631982f51256c453d1",
 | 
			
		||||
                "sha256:31dc4d77ef04ab0460d024786f51466dbbc274fda7c8aad0885a6df5ff8d642e",
 | 
			
		||||
                "sha256:35021d9fecea53b21e4defec0ff3ad69a8e2b75aca1ceddd444a5ba71216547e",
 | 
			
		||||
                "sha256:426a8ef9e3b97c27e841648241c2862442c13c91ec4a48c4a72b262ccf30add9",
 | 
			
		||||
                "sha256:58217698193fb94f3e6ff57eed0ae20381a8d06c2bc10151f76c06bb449a3a19",
 | 
			
		||||
                "sha256:5f45adbbb69281845981bb4e0a4efb8a405f10f3cd6c349cb4a5db3357c6bf93",
 | 
			
		||||
                "sha256:5fdb524767288f7ad161d2182f7ed6cafc0a283363728dcd04b9485f6411547c",
 | 
			
		||||
                "sha256:71fbee1f7ef3fb42efa3761a8faefc796e7e425f528de536cfb4c9de03bde885",
 | 
			
		||||
                "sha256:80bd314157851d06f7db7ca527082dbb0ee97afefb529cdcd59f7a5950927ba0",
 | 
			
		||||
                "sha256:b843c9ef6aed54a2649887f55959da0031595ccfaf7e7a0ba7aa681ffeaa0aa1",
 | 
			
		||||
                "sha256:c6a05ef8125503d2d282ccf1448e3599b8a6bd805c3cdee79760fa3da0ea090e",
 | 
			
		||||
                "sha256:deeda2769a52db840efe5bf7bdf7cefa0ae17b43a844a3259d39fb9465c8b008",
 | 
			
		||||
                "sha256:e66f8b09eec1afdcab947d3a1d65b87b25fde39e9172ae1bec562488335633b4",
 | 
			
		||||
                "sha256:e8db93045414980dbada8908d49dbbc0aa134277da3ff613b3e548cb275bdd37",
 | 
			
		||||
                "sha256:f1cc268a15ade58d9a0c04569fe6613e19b8b0345b64453064e2c3c6d79051af",
 | 
			
		||||
                "sha256:fe3001b6a4f3f3582a865b9e5081cc548b973ec20320f297f5e2d46860e9c703",
 | 
			
		||||
                "sha256:fe85bf7adb26eb47ad53a1bae5d35a28df16b2b93b89042a3a28746617a4738d"
 | 
			
		||||
                "sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0",
 | 
			
		||||
                "sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28",
 | 
			
		||||
                "sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8",
 | 
			
		||||
                "sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304",
 | 
			
		||||
                "sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0",
 | 
			
		||||
                "sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214",
 | 
			
		||||
                "sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043",
 | 
			
		||||
                "sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6",
 | 
			
		||||
                "sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625",
 | 
			
		||||
                "sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc",
 | 
			
		||||
                "sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638",
 | 
			
		||||
                "sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163",
 | 
			
		||||
                "sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4",
 | 
			
		||||
                "sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490",
 | 
			
		||||
                "sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248",
 | 
			
		||||
                "sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939",
 | 
			
		||||
                "sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87",
 | 
			
		||||
                "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720",
 | 
			
		||||
                "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.4.14"
 | 
			
		||||
            "version": "==0.4.15"
 | 
			
		||||
        },
 | 
			
		||||
        "ipdb": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
@@ -189,6 +197,12 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.3.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pathmatch": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:b35db907d0532c66132e5bc8aaa20dbfae916441987c8f0abd53ac538376d9a7"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.2.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pexpect": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
 | 
			
		||||
@@ -269,10 +283,10 @@
 | 
			
		||||
        },
 | 
			
		||||
        "testfixtures": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:7e4df89a8bf8b8905464160f08aff131a36f0b33654fe4f9e4387afe546eae25",
 | 
			
		||||
                "sha256:bcadbad77526cc5fc38bfb2ab80da810d7bde56ffe4c7fdb8e2bba122ded9620"
 | 
			
		||||
                "sha256:334497d26344e8c0c5d01b4d785a1c83464573151e6a5f7ab250eb7981d452ec",
 | 
			
		||||
                "sha256:53c06c1feb0bf378d63c54d1d96858978422d5a34793b39f0dcb0e44f8ec26f4"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==6.2.0"
 | 
			
		||||
            "version": "==6.3.0"
 | 
			
		||||
        },
 | 
			
		||||
        "traitlets": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
@@ -281,6 +295,14 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==4.3.2"
 | 
			
		||||
        },
 | 
			
		||||
        "typing": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d",
 | 
			
		||||
                "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4",
 | 
			
		||||
                "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==3.6.6"
 | 
			
		||||
        },
 | 
			
		||||
        "wcwidth": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
# flake8: noqa
 | 
			
		||||
from logging import DEBUG
 | 
			
		||||
 | 
			
		||||
import machine
 | 
			
		||||
@@ -17,12 +18,18 @@ def main():
 | 
			
		||||
    diode_orientation = DiodeOrientation.COLUMNS
 | 
			
		||||
 | 
			
		||||
    keymap = [
 | 
			
		||||
        [KC.ESC, KC.QUOTE, KC.COMMA, KC.PERIOD, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L,
 | 
			
		||||
            KC.BACKSPACE],
 | 
			
		||||
        [KC.TAB, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENTER],
 | 
			
		||||
        [KC.SHIFT, KC.SEMICOLON, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.SLASH],
 | 
			
		||||
        [KC.CTRL, KC.GUI, KC.ALT, KC.A, KC.A, KC.SPACE, KC.SPACE, KC.A, KC.LEFT, KC.DOWN,
 | 
			
		||||
            KC.UP, KC.RIGHT],
 | 
			
		||||
        [
 | 
			
		||||
            [KC.ESC, KC.QUOTE, KC.COMMA, KC.DOT, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L, KC.BKSP],
 | 
			
		||||
            [KC.TAB, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENT],
 | 
			
		||||
            [KC.LSFT, KC.SCLN, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.SLSH],
 | 
			
		||||
            [KC.CTRL, KC.GUI, KC.ALT, KC.RESET, KC.MO(1), KC.SPC, KC.SPC, KC.A, KC.LEFT, KC.DOWN, KC.UP, KC.RIGHT],
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            [KC.A, KC.QUOTE, KC.COMMA, KC.DOT, KC.P, KC.Y, KC.F, KC.G, KC.C, KC.R, KC.L, KC.BACKSPACE],
 | 
			
		||||
            [KC.TAB, KC.A, KC.O, KC.E, KC.U, KC.I, KC.D, KC.H, KC.T, KC.N, KC.S, KC.ENT],
 | 
			
		||||
            [KC.LSFT, KC.SCOLON, KC.Q, KC.J, KC.K, KC.X, KC.B, KC.M, KC.W, KC.V, KC.Z, KC.SLSH],
 | 
			
		||||
            [KC.CTRL, KC.GUI, KC.ALT, KC.RESET, KC.MO(1), KC.SPC, KC.SPC, KC.A, KC.LEFT, KC.DOWN, KC.UP, KC.RIGHT],
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    firmware = Firmware(
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,21 @@ def main():
 | 
			
		||||
    diode_orientation = DiodeOrientation.COLUMNS
 | 
			
		||||
 | 
			
		||||
    keymap = [
 | 
			
		||||
        [KC.ESC, KC.H, KC.BACKSPACE],
 | 
			
		||||
        [KC.TAB, KC.I, KC.ENTER],
 | 
			
		||||
        [KC.CTRL, KC.SPACE, KC.SHIFT],
 | 
			
		||||
        [
 | 
			
		||||
            [KC.MO(1), KC.H, KC.RESET],
 | 
			
		||||
            [KC.MO(2), KC.I, KC.ENTER],
 | 
			
		||||
            [KC.LCTRL, KC.SPACE, KC.LSHIFT],
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            [KC.TRNS, KC.B, KC.C],
 | 
			
		||||
            [KC.NO, KC.D, KC.E],
 | 
			
		||||
            [KC.F, KC.G, KC.H],
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            [KC.X, KC.Y, KC.Z],
 | 
			
		||||
            [KC.TRNS, KC.N, KC.O],
 | 
			
		||||
            [KC.R, KC.P, KC.Q],
 | 
			
		||||
        ],
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    firmware = Firmware(
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ from kmk.common.consts import DiodeOrientation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractMatrixScanner():
 | 
			
		||||
    def __init__(self, cols, rows, diode_orientation=DiodeOrientation.COLUMNS):
 | 
			
		||||
    def __init__(self, cols, rows, active_layers, diode_orientation=DiodeOrientation.COLUMNS):
 | 
			
		||||
        raise NotImplementedError('Abstract implementation')
 | 
			
		||||
 | 
			
		||||
    def _normalize_matrix(self, matrix):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,16 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from micropython import const
 | 
			
		||||
 | 
			
		||||
from kmk.common.keycodes import Keycodes
 | 
			
		||||
 | 
			
		||||
KEY_UP_EVENT = const(1)
 | 
			
		||||
KEY_DOWN_EVENT = const(2)
 | 
			
		||||
INIT_FIRMWARE_EVENT = const(3)
 | 
			
		||||
NEW_MATRIX_EVENT = const(4)
 | 
			
		||||
HID_REPORT_EVENT = const(5)
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init_firmware(keymap, row_pins, col_pins, diode_orientation):
 | 
			
		||||
@@ -15,19 +23,75 @@ def init_firmware(keymap, row_pins, col_pins, diode_orientation):
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def key_up_event(keycode, row, col):
 | 
			
		||||
def key_up_event(row, col):
 | 
			
		||||
    return {
 | 
			
		||||
        'type': KEY_UP_EVENT,
 | 
			
		||||
        'keycode': keycode,
 | 
			
		||||
        'row': row,
 | 
			
		||||
        'col': col,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def key_down_event(keycode, row, col):
 | 
			
		||||
def key_down_event(row, col):
 | 
			
		||||
    return {
 | 
			
		||||
        'type': KEY_DOWN_EVENT,
 | 
			
		||||
        'keycode': keycode,
 | 
			
		||||
        'row': row,
 | 
			
		||||
        'col': col,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def new_matrix_event(matrix):
 | 
			
		||||
    return {
 | 
			
		||||
        'type': NEW_MATRIX_EVENT,
 | 
			
		||||
        'matrix': matrix,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def hid_report_event():
 | 
			
		||||
    return {
 | 
			
		||||
        'type': HID_REPORT_EVENT,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def matrix_changed(new_matrix):
 | 
			
		||||
    def _key_pressed(dispatch, get_state):
 | 
			
		||||
        state = get_state()
 | 
			
		||||
        # Temporarily preserve a reference to the old event
 | 
			
		||||
        # We do fake Redux around here because microcontrollers
 | 
			
		||||
        # aren't exactly RAM or CPU powerhouses - the state does
 | 
			
		||||
        # mutate in place. Unfortunately this makes reasoning
 | 
			
		||||
        # about code a bit messier and really hurts one of the
 | 
			
		||||
        # selling points of Redux. Former development versions
 | 
			
		||||
        # of KMK created new InternalState copies every single
 | 
			
		||||
        # time the state changed, but it was sometimes slow.
 | 
			
		||||
        old_matrix = state.matrix
 | 
			
		||||
        old_keys_pressed = state.keys_pressed
 | 
			
		||||
 | 
			
		||||
        dispatch(new_matrix_event(new_matrix))
 | 
			
		||||
 | 
			
		||||
        with get_state() as new_state:
 | 
			
		||||
            for ridx, row in enumerate(new_state.matrix):
 | 
			
		||||
                for cidx, col in enumerate(row):
 | 
			
		||||
                    if col != old_matrix[ridx][cidx]:
 | 
			
		||||
                        if col:
 | 
			
		||||
                            dispatch(key_down_event(
 | 
			
		||||
                                row=ridx,
 | 
			
		||||
                                col=cidx,
 | 
			
		||||
                            ))
 | 
			
		||||
                        else:
 | 
			
		||||
                            dispatch(key_up_event(
 | 
			
		||||
                                row=ridx,
 | 
			
		||||
                                col=cidx,
 | 
			
		||||
                            ))
 | 
			
		||||
 | 
			
		||||
        with get_state() as new_state:
 | 
			
		||||
            if old_keys_pressed != new_state.keys_pressed:
 | 
			
		||||
                dispatch(hid_report_event())
 | 
			
		||||
 | 
			
		||||
            if Keycodes.KMK.KC_RESET in new_state.keys_pressed:
 | 
			
		||||
                try:
 | 
			
		||||
                    import machine
 | 
			
		||||
                    machine.bootloader()
 | 
			
		||||
                except ImportError:
 | 
			
		||||
                    logger.warning('Tried to reset to bootloader, but not supported on this chip?')
 | 
			
		||||
 | 
			
		||||
    return _key_pressed
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								kmk/common/internal_keycodes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								kmk/common/internal_keycodes.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from kmk.common.event_defs import KEY_DOWN_EVENT, KEY_UP_EVENT
 | 
			
		||||
from kmk.common.keycodes import Keycodes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def process_internal_key_event(state, action, changed_key, logger=None):
 | 
			
		||||
    if logger is None:
 | 
			
		||||
        logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
    if changed_key.code == Keycodes.Layers._KC_DF:
 | 
			
		||||
        return df(state, action, changed_key, logger=logger)
 | 
			
		||||
    elif changed_key.code == Keycodes.Layers._KC_MO:
 | 
			
		||||
        return mo(state, action, changed_key, logger=logger)
 | 
			
		||||
    elif changed_key.code == Keycodes.Layers._KC_TG:
 | 
			
		||||
        return tg(state, action, changed_key, logger=logger)
 | 
			
		||||
    elif changed_key.code == Keycodes.Layers._KC_TO:
 | 
			
		||||
        return to(state, action, changed_key, logger=logger)
 | 
			
		||||
    else:
 | 
			
		||||
        return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tilde(state, action, changed_key, logger):
 | 
			
		||||
    # TODO Actually process keycodes
 | 
			
		||||
    return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def df(state, action, changed_key, logger):
 | 
			
		||||
    """Switches the default layer"""
 | 
			
		||||
    if action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
        state.active_layers[0] = changed_key.layer
 | 
			
		||||
 | 
			
		||||
    return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mo(state, action, changed_key, logger):
 | 
			
		||||
    """Momentarily activates layer, switches off when you let go"""
 | 
			
		||||
    if action['type'] == KEY_UP_EVENT:
 | 
			
		||||
        state.active_layers = [
 | 
			
		||||
            layer for layer in state.active_layers
 | 
			
		||||
            if layer != changed_key.layer
 | 
			
		||||
        ]
 | 
			
		||||
    elif action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
        state.active_layers.append(changed_key.layer)
 | 
			
		||||
 | 
			
		||||
    return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lm(layer, mod):
 | 
			
		||||
    """As MO(layer) but with mod active"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lt(layer, kc):
 | 
			
		||||
    """Momentarily activates layer if held, sends kc if tapped"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tg(state, action, changed_key, logger):
 | 
			
		||||
    """Toggles the layer (enables it if not active, and vise versa)"""
 | 
			
		||||
    if action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
        if changed_key.layer in state.active_layers:
 | 
			
		||||
            state.active_layers = [
 | 
			
		||||
                layer for layer in state.active_layers
 | 
			
		||||
                if layer != changed_key.layer
 | 
			
		||||
            ]
 | 
			
		||||
        else:
 | 
			
		||||
            state.active_layers.append(changed_key.layer)
 | 
			
		||||
 | 
			
		||||
    return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to(state, action, changed_key, logger):
 | 
			
		||||
    """Activates layer and deactivates all other layers"""
 | 
			
		||||
    if action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
        state.active_layers = [changed_key.layer]
 | 
			
		||||
 | 
			
		||||
    return state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def tt(layer):
 | 
			
		||||
    """Momentarily activates layer if held, toggles it if tapped repeatedly"""
 | 
			
		||||
@@ -2,8 +2,11 @@ import logging
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from kmk.common.consts import DiodeOrientation
 | 
			
		||||
from kmk.common.event_defs import (INIT_FIRMWARE_EVENT, KEY_DOWN_EVENT,
 | 
			
		||||
                                   KEY_UP_EVENT)
 | 
			
		||||
from kmk.common.event_defs import (HID_REPORT_EVENT, INIT_FIRMWARE_EVENT,
 | 
			
		||||
                                   KEY_DOWN_EVENT, KEY_UP_EVENT,
 | 
			
		||||
                                   NEW_MATRIX_EVENT)
 | 
			
		||||
from kmk.common.internal_keycodes import process_internal_key_event
 | 
			
		||||
from kmk.common.keycodes import FIRST_KMK_INTERNAL_KEYCODE, Keycodes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReduxStore:
 | 
			
		||||
@@ -15,9 +18,15 @@ class ReduxStore:
 | 
			
		||||
        self.callbacks = []
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, action):
 | 
			
		||||
        self.logger.debug('Dispatching action: {}'.format(action))
 | 
			
		||||
        self.state = self.reducer(self.state, action)
 | 
			
		||||
        self.logger.debug('Dispatching complete: {}'.format(action))
 | 
			
		||||
        if callable(action):
 | 
			
		||||
            self.logger.debug('Received thunk')
 | 
			
		||||
            action(self.dispatch, self.get_state)
 | 
			
		||||
            self.logger.debug('Finished thunk')
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        self.logger.debug('Dispatching action: Type {} >> {}'.format(action['type'], action))
 | 
			
		||||
        self.state = self.reducer(self.state, action, logger=self.logger)
 | 
			
		||||
        self.logger.debug('Dispatching complete: Type {}'.format(action['type']))
 | 
			
		||||
 | 
			
		||||
        self.logger.debug('New state: {}'.format(self.state))
 | 
			
		||||
 | 
			
		||||
@@ -48,33 +57,62 @@ class InternalState:
 | 
			
		||||
    col_pins = []
 | 
			
		||||
    matrix = []
 | 
			
		||||
    diode_orientation = DiodeOrientation.COLUMNS
 | 
			
		||||
    active_layers = [0]
 | 
			
		||||
    _oldstates = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def __dict__(self):
 | 
			
		||||
        return {
 | 
			
		||||
    def __init__(self, preserve_intermediate_states=False):
 | 
			
		||||
        self.preserve_intermediate_states = preserve_intermediate_states
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, type, value, traceback):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def to_dict(self, verbose=False):
 | 
			
		||||
        ret = {
 | 
			
		||||
            'keys_pressed': self.keys_pressed,
 | 
			
		||||
            'modifiers_pressed': self.modifiers_pressed,
 | 
			
		||||
            'keymap': self.keymap,
 | 
			
		||||
            'col_pins': self.col_pins,
 | 
			
		||||
            'row_pins': self.row_pins,
 | 
			
		||||
            'diode_orientation': self.diode_orientation,
 | 
			
		||||
            'active_layers': self.active_layers,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if verbose:
 | 
			
		||||
            ret.update({
 | 
			
		||||
                'keymap': self.keymap,
 | 
			
		||||
                'matrix': self.matrix,
 | 
			
		||||
                'col_pins': self.col_pins,
 | 
			
		||||
                'row_pins': self.row_pins,
 | 
			
		||||
                'diode_orientation': self.diode_orientation,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return 'InternalState({})'.format(self.__dict__)
 | 
			
		||||
        return 'InternalState({})'.format(self.to_dict())
 | 
			
		||||
 | 
			
		||||
    def copy(self, **kwargs):
 | 
			
		||||
        new_state = InternalState()
 | 
			
		||||
 | 
			
		||||
        for k, v in self.__dict__.items():
 | 
			
		||||
            if hasattr(new_state, k):
 | 
			
		||||
                setattr(new_state, k, v)
 | 
			
		||||
    def update(self, **kwargs):
 | 
			
		||||
        if self.preserve_intermediate_states:
 | 
			
		||||
            self._oldstates.append(repr(self.to_dict(verbose=True)))
 | 
			
		||||
 | 
			
		||||
        for k, v in kwargs.items():
 | 
			
		||||
            if hasattr(new_state, k):
 | 
			
		||||
                setattr(new_state, k, v)
 | 
			
		||||
            setattr(self, k, v)
 | 
			
		||||
 | 
			
		||||
        return new_state
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_key_in_map(state, row, col):
 | 
			
		||||
    # Later-added layers have priority. Sift through the layers
 | 
			
		||||
    # in reverse order until we find a valid keycode object
 | 
			
		||||
    for layer in reversed(state.active_layers):
 | 
			
		||||
        layer_key = state.keymap[layer][row][col]
 | 
			
		||||
 | 
			
		||||
        if not layer_key or layer_key == Keycodes.KMK.KC_TRNS:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        if layer_key == Keycodes.KMK.KC_NO:
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
        return layer_key
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def kmk_reducer(state=None, action=None, logger=None):
 | 
			
		||||
@@ -90,36 +128,57 @@ def kmk_reducer(state=None, action=None, logger=None):
 | 
			
		||||
 | 
			
		||||
        return state
 | 
			
		||||
 | 
			
		||||
    if action['type'] == KEY_UP_EVENT:
 | 
			
		||||
        return state.copy(
 | 
			
		||||
            keys_pressed=frozenset(
 | 
			
		||||
                key for key in state.keys_pressed if key != action['keycode']
 | 
			
		||||
            ),
 | 
			
		||||
            matrix=[
 | 
			
		||||
                r if ridx != action['row'] else [
 | 
			
		||||
                    c if cidx != action['col'] else False
 | 
			
		||||
                    for cidx, c in enumerate(r)
 | 
			
		||||
                ]
 | 
			
		||||
                for ridx, r in enumerate(state.matrix)
 | 
			
		||||
            ],
 | 
			
		||||
    if action['type'] == NEW_MATRIX_EVENT:
 | 
			
		||||
        return state.update(
 | 
			
		||||
            matrix=action['matrix'],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if action['type'] == KEY_UP_EVENT:
 | 
			
		||||
        row = action['row']
 | 
			
		||||
        col = action['col']
 | 
			
		||||
 | 
			
		||||
        changed_key = find_key_in_map(state, row, col)
 | 
			
		||||
 | 
			
		||||
        logger.debug('Detected change to key: {}'.format(changed_key))
 | 
			
		||||
 | 
			
		||||
        if not changed_key:
 | 
			
		||||
            return state
 | 
			
		||||
 | 
			
		||||
        newstate = state.update(
 | 
			
		||||
            keys_pressed=frozenset(
 | 
			
		||||
                key for key in state.keys_pressed if key != changed_key
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE:
 | 
			
		||||
            return process_internal_key_event(newstate, action, changed_key, logger=logger)
 | 
			
		||||
 | 
			
		||||
        return newstate
 | 
			
		||||
 | 
			
		||||
    if action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
        return state.copy(
 | 
			
		||||
        row = action['row']
 | 
			
		||||
        col = action['col']
 | 
			
		||||
 | 
			
		||||
        changed_key = find_key_in_map(state, row, col)
 | 
			
		||||
 | 
			
		||||
        logger.debug('Detected change to key: {}'.format(changed_key))
 | 
			
		||||
 | 
			
		||||
        if not changed_key:
 | 
			
		||||
            return state
 | 
			
		||||
 | 
			
		||||
        newstate = state.update(
 | 
			
		||||
            keys_pressed=(
 | 
			
		||||
                state.keys_pressed | {action['keycode']}
 | 
			
		||||
                state.keys_pressed | {changed_key}
 | 
			
		||||
            ),
 | 
			
		||||
            matrix=[
 | 
			
		||||
                r if ridx != action['row'] else [
 | 
			
		||||
                    c if cidx != action['col'] else True
 | 
			
		||||
                    for cidx, c in enumerate(r)
 | 
			
		||||
                ]
 | 
			
		||||
                for ridx, r in enumerate(state.matrix)
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if changed_key.code >= FIRST_KMK_INTERNAL_KEYCODE:
 | 
			
		||||
            return process_internal_key_event(newstate, action, changed_key, logger=logger)
 | 
			
		||||
 | 
			
		||||
        return newstate
 | 
			
		||||
 | 
			
		||||
    if action['type'] == INIT_FIRMWARE_EVENT:
 | 
			
		||||
        return state.copy(
 | 
			
		||||
        return state.update(
 | 
			
		||||
            keymap=action['keymap'],
 | 
			
		||||
            row_pins=action['row_pins'],
 | 
			
		||||
            col_pins=action['col_pins'],
 | 
			
		||||
@@ -129,3 +188,14 @@ def kmk_reducer(state=None, action=None, logger=None):
 | 
			
		||||
                for r in action['row_pins']
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # HID events are non-mutating, used exclusively for listeners to know
 | 
			
		||||
    # they should be doing things. This could/should arguably be folded back
 | 
			
		||||
    # into KEY_UP_EVENT and KEY_DOWN_EVENT, but for now it's nice to separate
 | 
			
		||||
    # this out for debugging's sake.
 | 
			
		||||
    if action['type'] == HID_REPORT_EVENT:
 | 
			
		||||
        return state
 | 
			
		||||
 | 
			
		||||
    # On unhandled events, log and do not mutate state
 | 
			
		||||
    logger.warning('Unhandled event! Returning state unmodified.')
 | 
			
		||||
    return state
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,10 @@ except ImportError:
 | 
			
		||||
from kmk.common.types import AttrDict
 | 
			
		||||
from kmk.common.util import flatten_dict
 | 
			
		||||
 | 
			
		||||
FIRST_KMK_INTERNAL_KEYCODE = 1000
 | 
			
		||||
 | 
			
		||||
Keycode = namedtuple('Keycode', ('code', 'is_modifier'))
 | 
			
		||||
LayerKeycode = namedtuple('LayerKeycode', ('code', 'layer'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class KeycodeCategory(type):
 | 
			
		||||
@@ -312,6 +315,77 @@ class Keycodes(KeycodeCategory):
 | 
			
		||||
        KC_MEDIA_FAST_FORWARD = KC_MFFD = Keycode(187, False)
 | 
			
		||||
        KC_MEDIA_REWIND = KC_MRWD = Keycode(189, False)
 | 
			
		||||
 | 
			
		||||
    class KMK(KeycodeCategory):
 | 
			
		||||
        KC_RESET = Keycode(1000, False)
 | 
			
		||||
        KC_DEBUG = Keycode(1001, False)
 | 
			
		||||
        KC_GESC = Keycode(1002, False)
 | 
			
		||||
        KC_LSPO = Keycode(1003, False)
 | 
			
		||||
        KC_RSPC = Keycode(1004, False)
 | 
			
		||||
        KC_LEAD = Keycode(1005, False)
 | 
			
		||||
        KC_LOCK = Keycode(1006, False)
 | 
			
		||||
        KC_NO = Keycode(1100, False)
 | 
			
		||||
        KC_TRNS = Keycode(1101, False)
 | 
			
		||||
 | 
			
		||||
    class Layers(KeycodeCategory):
 | 
			
		||||
        _KC_DF = 1050
 | 
			
		||||
        _KC_MO = 1051
 | 
			
		||||
        _KC_LM = 1052
 | 
			
		||||
        _KC_LT = 1053
 | 
			
		||||
        _KC_TG = 1054
 | 
			
		||||
        _KC_TO = 1055
 | 
			
		||||
        _KC_TT = 1056
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_DF(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_DF, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_MO(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_MO, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_LM(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_LM, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_LT(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_LT, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_TG(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_TG, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_TO(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_TO, layer)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def KC_TT(layer):
 | 
			
		||||
            return LayerKeycode(Keycodes.Layers._KC_TT, layer)
 | 
			
		||||
 | 
			
		||||
    class ShiftedKeycodes(KeycodeCategory):
 | 
			
		||||
        KC_TILDE = KC_TILD = Keycode(1100, False)
 | 
			
		||||
        KC_EXCLAIM = KC_EXLM = Keycode(1101, False)
 | 
			
		||||
        KC_AT = Keycode(1102, False)
 | 
			
		||||
        KC_HASH = Keycode(1103, False)
 | 
			
		||||
        KC_DOLLAR = KC_DLR = Keycode(1104, False)
 | 
			
		||||
        KC_PERCENT = KC_PERC = Keycode(1105, False)
 | 
			
		||||
        KC_CIRCUMFLEX = KC_CIRC = Keycode(1106, False)  # The ^ Symbol
 | 
			
		||||
        KC_AMPERSAND = KC_AMPR = Keycode(1107, False)
 | 
			
		||||
        KC_ASTERISK = KC_ASTR = Keycode(1108, False)
 | 
			
		||||
        KC_LEFT_PAREN = KC_LPRN = Keycode(1109, False)
 | 
			
		||||
        KC_RIGHT_PAREN = KC_RPRN = Keycode(1110, False)
 | 
			
		||||
        KC_UNDERSCORE = KC_UNDS = Keycode(1111, False)
 | 
			
		||||
        KC_PLUS = Keycode(1112, False)
 | 
			
		||||
        KC_LEFT_CURLY_BRACE = KC_LCBR = Keycode(1113, False)
 | 
			
		||||
        KC_RIGHT_CURLY_BRACE = KC_RCBR = Keycode(1114, False)
 | 
			
		||||
        KC_PIPE = Keycode(1115, False)
 | 
			
		||||
        KC_COLON = KC_COLN = Keycode(1116, False)
 | 
			
		||||
        KC_DOUBLE_QUOTE = KC_DQUO = KC_DQT = Keycode(1117, False)
 | 
			
		||||
        KC_LEFT_ANGLE_BRACKET = KC_LABK = KC_LT = Keycode(1118, False)
 | 
			
		||||
        KC_RIGHT_ANGLE_BRACKET = KC_RABK = KC_GT = Keycode(1119, False)
 | 
			
		||||
        KC_QUESTION = KC_QUES = Keycode(1120, False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ALL_KEYS = KC = AttrDict({
 | 
			
		||||
    k.replace('KC_', ''): v
 | 
			
		||||
@@ -324,6 +398,6 @@ char_lookup = {
 | 
			
		||||
    ' ': (Keycodes.Common.KC_SPACE,),
 | 
			
		||||
    '-': (Keycodes.Common.KC_MINUS,),
 | 
			
		||||
    '=': (Keycodes.Common.KC_EQUAL,),
 | 
			
		||||
    '+': (Keycodes.Common.KC_EQUAL, Keycodes.Modifiers.KC_SHIFT),
 | 
			
		||||
    '~': (Keycodes.Common.KC_TILDE,),
 | 
			
		||||
    '+': (Keycodes.Common.KC_EQUAL, Keycodes.Modifiers.KC_LSHIFT),
 | 
			
		||||
    '~': (Keycodes.Common.KC_GRAVE,),
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
from kmk.common.event_defs import key_down_event, key_up_event
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Keymap:
 | 
			
		||||
    def __init__(self, map):
 | 
			
		||||
        self.map = map
 | 
			
		||||
 | 
			
		||||
    def parse(self, matrix, store):
 | 
			
		||||
        state = store.get_state()
 | 
			
		||||
 | 
			
		||||
        for ridx, row in enumerate(matrix):
 | 
			
		||||
            for cidx, col in enumerate(row):
 | 
			
		||||
                if col != state.matrix[ridx][cidx]:
 | 
			
		||||
                    if col:
 | 
			
		||||
                        store.dispatch(key_down_event(
 | 
			
		||||
                            row=ridx,
 | 
			
		||||
                            col=cidx,
 | 
			
		||||
                            keycode=self.map[ridx][cidx],
 | 
			
		||||
                        ))
 | 
			
		||||
                    else:
 | 
			
		||||
                        store.dispatch(key_up_event(
 | 
			
		||||
                            row=ridx,
 | 
			
		||||
                            col=cidx,
 | 
			
		||||
                            keycode=self.map[ridx][cidx],
 | 
			
		||||
                        ))
 | 
			
		||||
@@ -2,7 +2,6 @@ import logging
 | 
			
		||||
 | 
			
		||||
from kmk.common.event_defs import init_firmware
 | 
			
		||||
from kmk.common.internal_state import ReduxStore, kmk_reducer
 | 
			
		||||
from kmk.common.keymap import Keymap
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from kmk.circuitpython.matrix import MatrixScanner
 | 
			
		||||
@@ -12,8 +11,8 @@ except ImportError:
 | 
			
		||||
 | 
			
		||||
class Firmware:
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, keymap, row_pins, col_pins, diode_orientation,
 | 
			
		||||
        hid=None, log_level=logging.NOTSET,
 | 
			
		||||
        self, keymap, row_pins, col_pins,
 | 
			
		||||
        diode_orientation, hid=None, log_level=logging.NOTSET,
 | 
			
		||||
    ):
 | 
			
		||||
        logger = logging.getLogger(__name__)
 | 
			
		||||
        logger.setLevel(log_level)
 | 
			
		||||
@@ -40,9 +39,6 @@ class Firmware:
 | 
			
		||||
        ))
 | 
			
		||||
 | 
			
		||||
    def _subscription(self, state, action):
 | 
			
		||||
        if self.cached_state is None or self.cached_state.keymap != state.keymap:
 | 
			
		||||
            self.keymap = Keymap(state.keymap)
 | 
			
		||||
 | 
			
		||||
        if self.cached_state is None or any(
 | 
			
		||||
            getattr(self.cached_state, k) != getattr(state, k)
 | 
			
		||||
            for k in state.__dict__.keys()
 | 
			
		||||
@@ -55,4 +51,8 @@ class Firmware:
 | 
			
		||||
 | 
			
		||||
    def go(self):
 | 
			
		||||
        while True:
 | 
			
		||||
            self.keymap.parse(self.matrix.raw_scan(), store=self.store)
 | 
			
		||||
            state = self.store.get_state()
 | 
			
		||||
            update = self.matrix.scan_for_changes(state.matrix)
 | 
			
		||||
 | 
			
		||||
            if update:
 | 
			
		||||
                self.store.dispatch(update)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,11 @@ import machine
 | 
			
		||||
 | 
			
		||||
from kmk.common.abstract.matrix_scanner import AbstractMatrixScanner
 | 
			
		||||
from kmk.common.consts import DiodeOrientation
 | 
			
		||||
from kmk.common.event_defs import matrix_changed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MatrixScanner(AbstractMatrixScanner):
 | 
			
		||||
    def __init__(self, cols, rows, diode_orientation=DiodeOrientation.COLUMNS):
 | 
			
		||||
    def __init__(self, cols, rows, active_layers, diode_orientation=DiodeOrientation.COLUMNS):
 | 
			
		||||
        # A pin cannot be both a row and column, detect this by combining the
 | 
			
		||||
        # two tuples into a set and validating that the length did not drop
 | 
			
		||||
        #
 | 
			
		||||
@@ -19,6 +20,7 @@ class MatrixScanner(AbstractMatrixScanner):
 | 
			
		||||
        self.cols = [machine.Pin(pin) for pin in cols]
 | 
			
		||||
        self.rows = [machine.Pin(pin) for pin in rows]
 | 
			
		||||
        self.diode_orientation = diode_orientation
 | 
			
		||||
        self.active_layers = active_layers
 | 
			
		||||
 | 
			
		||||
        if self.diode_orientation == DiodeOrientation.COLUMNS:
 | 
			
		||||
            self.outputs = self.cols
 | 
			
		||||
@@ -51,3 +53,17 @@ class MatrixScanner(AbstractMatrixScanner):
 | 
			
		||||
            opin.value(0)
 | 
			
		||||
 | 
			
		||||
        return self._normalize_matrix(matrix)
 | 
			
		||||
 | 
			
		||||
    def scan_for_changes(self, old_matrix):
 | 
			
		||||
        matrix = self.raw_scan()
 | 
			
		||||
 | 
			
		||||
        if any(
 | 
			
		||||
            any(
 | 
			
		||||
                col != old_matrix[ridx][cidx]
 | 
			
		||||
                for cidx, col in enumerate(row)
 | 
			
		||||
            )
 | 
			
		||||
            for ridx, row in enumerate(matrix)
 | 
			
		||||
        ):
 | 
			
		||||
            return matrix_changed(matrix)
 | 
			
		||||
 | 
			
		||||
        return None  # The default, but for explicitness
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@ import string
 | 
			
		||||
 | 
			
		||||
from pyb import USB_HID, delay
 | 
			
		||||
 | 
			
		||||
from kmk.common.event_defs import KEY_DOWN_EVENT, KEY_UP_EVENT
 | 
			
		||||
from kmk.common.keycodes import Keycodes, char_lookup
 | 
			
		||||
from kmk.common.event_defs import HID_REPORT_EVENT
 | 
			
		||||
from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes,
 | 
			
		||||
                                 char_lookup)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HIDHelper:
 | 
			
		||||
@@ -48,20 +49,19 @@ class HIDHelper:
 | 
			
		||||
        self.clear_all()
 | 
			
		||||
 | 
			
		||||
    def _subscription(self, state, action):
 | 
			
		||||
        if action['type'] == KEY_DOWN_EVENT:
 | 
			
		||||
            if action['keycode'].is_modifier:
 | 
			
		||||
                self.add_modifier(action['keycode'])
 | 
			
		||||
                self.send()
 | 
			
		||||
            else:
 | 
			
		||||
                self.add_key(action['keycode'])
 | 
			
		||||
                self.send()
 | 
			
		||||
        elif action['type'] == KEY_UP_EVENT:
 | 
			
		||||
            if action['keycode'].is_modifier:
 | 
			
		||||
                self.remove_modifier(action['keycode'])
 | 
			
		||||
                self.send()
 | 
			
		||||
            else:
 | 
			
		||||
                self.remove_key(action['keycode'])
 | 
			
		||||
                self.send()
 | 
			
		||||
        if action['type'] == HID_REPORT_EVENT:
 | 
			
		||||
            self.clear_all()
 | 
			
		||||
 | 
			
		||||
            for key in state.keys_pressed:
 | 
			
		||||
                if key.code >= FIRST_KMK_INTERNAL_KEYCODE:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if key.is_modifier:
 | 
			
		||||
                    self.add_modifier(key)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.add_key(key)
 | 
			
		||||
 | 
			
		||||
            self.send()
 | 
			
		||||
 | 
			
		||||
    def send(self):
 | 
			
		||||
        self.logger.debug('Sending HID report: {}'.format(self._evt))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
[flake8]
 | 
			
		||||
exclude = .git,__pycache__,vendor,.venv
 | 
			
		||||
max_line_length = 99
 | 
			
		||||
ignore = X100
 | 
			
		||||
per-file-ignores =
 | 
			
		||||
	boards/**/*.py: E501
 | 
			
		||||
 | 
			
		||||
[isort]
 | 
			
		||||
known_third_party = analogio,bitbangio,bleio,board,busio,digitalio,framebuf,gamepad,gc,microcontroller,micropython,pulseio,pyb,pydux,uio,ubluepy,machine,pyb
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user